在HTTP请求头中获取客户端真实IP

为了对ASM的入口网关进行访问保护,需要获取客户端真实IP,以便使用授权策略设置访问入口网关的IP黑名单和白名单。本文介绍如何在HTTP请求头中获取客户端真实IP。

前提条件

背景信息

通常情况下,应用程序依靠反向代理来转发请求中的客户端属性,例如X-Forwarded-For标头。但是由于Istio可以部署多种网络拓扑,除了直接使用负载均衡器CLB的公网地址访问之外,也可能会在Web应用程序防火墙(WAF)接入入口网关地址,或者使用其他的未指定的部署拓扑来访问入口网关地址。在支持各种部署架构的情况下,服务网格ASM无法提供一个固定的默认值,将客户端属性正确转发到目标工作负载,也就无法直接通过X-Forwarded-For获取客户端真实IP。

为了解决上述问题,您需要在ASM网关中将numTrustedProxies参数的值,配置为部署在网关代理前面的可受信的代理数量,以便正确获取客户端地址。控制入口网关在X-Envoy-External-Address标头中填充的值,以便上游服务使用该值来访问客户端的原始IP地址。

操作步骤

  1. 部署示例应用。

    1. 通过kubectl管理集群和应用。具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群

    2. 部署httpbin应用。

      1. 使用以下内容创建httpbin.yaml文件。

        展开查看httpbin.yaml

        # httpbin service
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: httpbin
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: httpbin
          labels:
            app: httpbin
            service: httpbin
        spec:
          ports:
          - name: http
            port: 8000
            targetPort: 80
          selector:
            app: httpbin
        ---
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: httpbin
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: httpbin
              version: v1
          template:
            metadata:
              labels:
                app: httpbin
                version: v1
            spec:
              serviceAccountName: httpbin
              containers:
              - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/httpbin:0.1.0
                imagePullPolicy: IfNotPresent
                name: httpbin
                ports:
                - containerPort: 80
                                                
      2. 执行以下命令,部署httpbin应用。

        kubectl apply -f httpbin.yaml
  2. 创建ASM网关。

    1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

    2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 入口网关

    3. 入口网关页面,单击创建,配置网关的基本信息。

      负载均衡CLB类型选择公网访问,其他按需配置即可。关于配置项的更多信息,请参见创建入口网关服务

    4. 单击高级选项,设置外部流量策略Local,然后单击创建

  3. 创建网关规则和虚拟服务。

    1. 通过API方式管理ASM实例。具体操作,请参见通过控制面kubectl访问Istio资源

    2. 创建网关规则。

      1. 使用以下内容,创建httpbin-gateway.yaml文件。

        apiVersion: networking.istio.io/v1alpha3
        kind: Gateway
        metadata:
          name: httpbin-gateway
        spec:
          selector:
            istio: ingressgateway
          servers:
          - port:
              number: 80
              name: http
              protocol: HTTP
            hosts:
            - "*"
      2. 执行以下命令,创建网关规则。

        kubectl apply -f httpbin-gateway.yaml
    3. 创建虚拟服务。

      1. 使用以下内容,创建httpbin-virtualservice.yaml文件。

        apiVersion: networking.istio.io/v1alpha3
        kind: VirtualService
        metadata:
          name: httpbin
        spec:
          hosts:
          - "*"
          gateways:
          - httpbin-gateway
          http:
          - route:
            - destination:
                host: httpbin
                port:
                  number: 8000
      2. 执行以下命令,创建虚拟服务。

        kubectl apply -f httpbin-virtualservice.yaml
  4. 获取httpbin应用的80端口的入口网关地址。具体操作,请参见创建入口网关服务

  5. Web应用防火墙,接入步骤4获取的入口网关的地址。具体操作,请参见使用教程

  6. 为ASM网关添加numTrustedProxies参数。

    1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

    2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 入口网关

    3. 入口网关页面的目标网关右侧,单击查看YAML

    4. 编辑对话框的spec参数下,添加以下内容,然后单击确定

      podAnnotations:
          proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 2 } }'

      根据实际的拓扑设置numTrustedProxies为部署在网关代理前面的可受信的代理数量。如果设置numTrustedProxies为一个大于零的值N,则可信客户端地址为X-Forwarded-For右侧起的第N+1个地址。

  7. 执行以下命令,访问httpbin应用,获取客户端真实IP。

    curl http://{入口网关地址}/get?show_env=true

    预期输出:

    {
      "args": {
        "show_env": "true"
      },
      "headers": {
        "Accept": "*/*",
        ....
        "X-Envoy-Attempt-Count": "1",
        "X-Envoy-External-Address": "106.11.**.**",
        ....
      },
      ....
    }

    X-Envoy-External-Address的值即为客户端真实IP。