前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >k8s生产实践之获取客户端真实IP

k8s生产实践之获取客户端真实IP

作者头像
仙人技术
发布2021-08-31 18:04:45
3.9K0
发布2021-08-31 18:04:45
举报
文章被收录于专栏:山山仙人的专栏

1、概述

通常web应用获取用户客户端的真实ip一个很常见的需求,例如将用户真实ip取到之后对用户做白名单访问限制、将用户ip记录到数据库日志中对用户的操作做审计等等

vm时代是一个比较容易解决的问题,但当一切云原生化(容器化)之后变得稍微复杂了些

k8s中运行的应用通过Service抽象来互相查找、通信和与外部世界沟通,在k8s中是kube-proxy组件实现了Service的通信与负载均衡,流量在传递的过程中经过了源地址转换SNAT,因此在默认的情况下,常常是拿不到用户真实的ip

这个问题在k8s官方文档(https://kubernetes.io/zh/docs/tutorials/services/source-ip/)中基于Cluster IPNodePortLoadBalancer三种不同的Service类型进行了一定的说明,这里不再剖析

2、环境介绍

本篇仅介绍私有云+外部硬件负载+k8s集群的真实场景下如何进行配置

相关环境及设备说明如下

组件名

型号或版本

硬件负载设备

SANGFOR(深信服) AD 6.5R1

k8s Ingress 控制器

NGINX Ingress Controller 0.25.0

k8s 集群

Kubernetes 1.17.0

3、相关说明

真实生产场景下,一般提供给用户的都是七层https服务

首先域名解析在外部负载设备绑定的公网ip上,负载周边可能还会有一些安全设备例如WAF等,这里不多介绍

流量经过负载后进入到k8s集群中,其中Ingress ControllerDaemonSet方式部署并使用hostNetwork模式接收并处理到达宿主机的80443端口流量

关于https证书的配置,一般有以下两种可选方式:

  • 配置在负载设备(负载类型如果只考虑七层负载),由负载负责将数据包封包解包,并转发到后端,如果用户通过https形式访问,流量经过的流程是:用户端——>负载80端口——>负载443端口——>服务端(k8s node)的80端口
  • 配置在后端,例如Ingress资源上,如果用户通过https形式访问,流量经过的流程是:用户端——>负载80端口——>服务端(k8s node)的80端口——>服务端(k8s node)的443端口

但是为了获取用户的真实ip,只能选择方式一,因为如果证书配置在后端服务,流量经过负载时是加密的,负载一般在没有证书的情况下,是无法对数据包进行解包操作透传用户ip

以上在公有云环境下,例如腾讯云CLB、阿里云新的应用型负载ALB或传统型负载CLB均有涉及,可能不尽详细

4、环境准备

首先需要准备一个后端获取用户请求,显示打印或输出的应用,可以自己手撸一个简单应用,当然为了操作简单也可以选择nginx容器在应用日志中查看,更好的方式是选择whoamiechoserver这类镜像

其中whoami可以在控制台访问服务时打印用户请求等相关信息,echoserver可以在浏览器呈现用户请求等相关信息

这里为了模拟和真实应用一样的场景,选择更为直观的echoserver,其源镜像地址为gcr.io/google-containers/echoserver

如果网络不佳,可以从我的地址获取ssgeek/echoserver

首先基于k8s部署该应用,创建deploysvcing,定义如下

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echoserver
  labels:
    app: echoserver
spec:
  selector:
    matchLabels:
      app: echoserver
  template:
    metadata:
      labels:
        app: echoserver
    spec:
      containers:
      - name: echoserver
        image: ssgeek/echoserver:latest
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP

---
apiVersion: v1
kind: Service
metadata:
  name: echoserver
  labels:
    app: echoserver
spec:
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: echoserver

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echoserver
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: echo.ssgeek.com
    http:
      paths:
      - backend:
          serviceName: echoserver
          servicePort: 80
        path: /

5、负载配置

这里简单分析及列出关键配置

  • 插入请求头以透传ip

部署好后端服务后,开始配置外部(深信服)负载,除了导入https证书外,还需要在转发的请求头中插入X-Forwarded-For头部,确保用户ip在经过负载时作为请求头的一部分传递到后端服务器

  • 负载设备到后端请求头部改写

由于负载设备到后端的80端口,因此后端只接收http请求,也就是请求经过负载处理https及证书相关动作

未添加请求头部改写时,对请求抓包的现象对比如下(分别为无https配置时和有https配置但未改写请求头部时)

6、Ingress Controller 配置

修改Nginx Ingress Controller配置,添加如下内容

参考:https://kubernetes.github.io/ingress-nginx/user-guide/

代码语言:javascript
复制
data:
  use-forwarded-headers: "true"
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  • use-forwarded-headers 如果为true,会将传入的X-Forwarded-*头传递给upstreams 如果为false,会忽略传入的X-Forwarded-*头,用看到的请求信息填充它们。如果直接暴露在互联网上,或者它在基于L3/packet-based load balancer后面,并且不改变数据包中的源IP时使用此选项
  • forwarded-for-header 设置标头字段以标识客户端的原始IP地址。 默认: X-Forwarded-For
  • compute-full-forwarded-for 将远程地址附加到 X-Forwarded-For标头,而不是替换它。 启用此选项后,upstreams应用程序将根据其自己的受信任代理列表提取客户端IP

7、服务端验证

服务端请求暴露及应用获取ip效果如下

正常情况可拿到以下几类ip

  • pod ip

k8s pod自身的ip

  • node ip

k8s pod所在nodeip

  • 负载 ip

位于请求头X-Forwarded-For字段中

  • 用户真实 ip

位于请求头X-Forwarded-For字段、x-original-forwarded-for字段、x-real-ip字段中

关于x-forwarded-forx-original-forwarded-forx-real-ip的说明:

X-Forwarded-For用于记录从客户端地址到最后一个代理服务器的所有地址

X-Real-IP用于记录请求的客户端地址

X-Original-Forwarded-For字面意思是原始转发 IP,这是Ingress的功能,Ingress将用户的真实IP记录到了这个字段

对应用来说,以java应用为例,获取用户ip的代码如下

代码语言:javascript
复制
/**
 * 获取操作用户ip
 * @return
 */
private String getIp() {

    HttpServletRequest request;
    try {
        request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    } catch (NullPointerException e) {
        return "127.0.0.1";
    }

    //取客户端ip
    String ipAddress = request.getHeader("x-forwarded-for");
    if (ipAddress == null || ipAddress.length() == 0
            || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.length() == 0
            || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.length() == 0
            || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getRemoteAddr();
        if (ipAddress.equals("127.0.0.1")) {
            // 根据网卡取本机配置的IP
            InetAddress inet = null;
            try {
                inet = InetAddress.getLocalHost();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            ipAddress = inet.getHostAddress();
        }
    }
    // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
    if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
        // = 15
        if (ipAddress.indexOf(",") > 0) {
            ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
        }
    }
    return ipAddress;
}

8、小结

本文记录了私有云和有外部负载的真实场景下,k8s集群中的应用获取用户ip的相关实现逻辑及关键处理,希望能帮助到大家

See you ~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、概述
  • 2、环境介绍
  • 3、相关说明
  • 4、环境准备
  • 5、负载配置
  • 6、Ingress Controller 配置
  • 7、服务端验证
  • 8、小结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档