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

前提条件

背景信息

通常情况下,应用程序依靠反向代理来转发请求中的客户端属性,例如X-Forward-For标头。但是由于Istio可以部署多种网络拓扑,除了直接使用负载均衡器SLB的公网地址访问之外,也可能会在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 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: docker.io/kennethreitz/httpbin
                imagePullPolicy: IfNotPresent
                name: httpbin
                ports:
                - containerPort: 80
                                                
      2. 执行以下命令,部署httpbin应用。
        kubectl apply -f httpbin.yaml
  2. 创建ASM网关。
    1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理
    2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 入口网关
    3. 入口网关页面,单击创建,配置网关的基本信息。
      重要配置项说明如下,其他配置项保持默认即可。关于配置项的更多信息,请参见创建入口网关服务
      配置项说明
      名称自定义网关的名称。
      部署集群选择网关部署的集群。
      负载均衡类型选择公网访问
      新建负载均衡选择负载均衡,可选:
      • 使用已有负载均衡:从已有负载均衡列表中选择。
      • 新建负载均衡:单击新建负载均衡,从下拉列表中选择所需的负载均衡规格。
      端口映射单击添加端口,在新增端口行中,选择协议,输入服务端口
    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-Forward-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。