入口网关

    在开始此任务之前,请执行以下操作:

    • 阅读 Istio 授权概念

    • 使用 安装 Istio。

    • 在命名空间中部署工作负载,例如foo,并使用以下命令通过 Istio 入口网关公开它:

      Zip

    • 在 Envoy 中为入口网关打开 RBAC 调试:

      1. $ kubectl get pods -n istio-system -o name -l istio=ingressgateway | sed 's|pod/||' | while read -r pod; do istioctl proxy-config log "$pod" -n istio-system --level rbac:debug; done
    • Follow the instructions in 确定入口 IP 和端口 to define the INGRESS_HOST and INGRESS_PORT environment variables.

    • Verify that the httpbin workload and ingress gateway are working as expected using this command:

      1. $ curl "$INGRESS_HOST:$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
      2. 200

    如果您没有看到预期的输出,请在几秒钟后重试。 缓存和传播开销可能会导致延迟。

    所有将流量引入 Kubernetes 的方法都涉及在所有工作节点上打开一个端口,实现这一点的主要功能是NodePort服务和LoadBalancer服务,甚至 Kubernetes 的Ingress资源也必须由 Ingress 控制器支持,该控制器将创建NodePortLoadBalancer服务。

    • NodePort 只是在每个工作节点上打开一个 30000-32767 范围内的端口,并使用标签选择器来识别将流量发送到哪些 Pod。 您必须在工作节点前面手动创建某种负载均衡器或使用轮询模式的 DNS。

    如果处理来自 NodePortLoadBalancer 的流量的 Pod 没有在接收流量的工作节点上运行怎么办? Kubernetes 有自己的内部代理,称为 kube-proxy,它接收数据包并将它们转发到正确的节点。

    如果数据包通过外部代理负载均衡器和/或 kube-proxy,则客户端的原始源 IP 地址会丢失。 以下是一些保留原始客户端 IP 以用于日志记录或安全目的的策略。

    Envoy 中发现了一个严重的 ,即针对非 HTTP 连接错误地恢复了代理协议下游地址。

    请不要在非 HTTP 连接上使用带有代理协议的 remoteIpBlocks 字段和 remote_ip 属性,直到发布具有适当修复的更新版本的 Istio。

    请注意,Istio 不支持代理协议,它只能通过 EnvoyFilter API 启用,使用风险自负。

    如果您使用的是 TCP/UDP 代理外部负载均衡器(AWS Classic ELB),它可以使用 代理协议 嵌入原始 数据包数据中的客户端 IP 地址。 外部负载均衡器和 Istio 入口网关都必须支持代理协议才能工作。 在 Istio 中,您可以使用 EnvoyFilter 启用它,如下所示:

    1. apiVersion: networking.istio.io/v1alpha3
    2. kind: EnvoyFilter
    3. metadata:
    4. name: proxy-protocol
    5. namespace: istio-system
    6. spec:
    7. configPatches:
    8. - applyTo: LISTENER
    9. patch:
    10. operation: MERGE
    11. value:
    12. listener_filters:
    13. - name: envoy.listener.proxy_protocol
    14. - name: envoy.listener.tls_inspector
    15. workloadSelector:
    16. labels:
    17. istio: ingressgateway

    以下是IstioOperator示例,展示了如何在 AWS EKS 上配置 Istio 入口网关以支持代理协议:

    1. apiVersion: install.istio.io/v1alpha1
    2. kind: IstioOperator
    3. spec:
    4. meshConfig:
    5. accessLogEncoding: JSON
    6. accessLogFile: /dev/stdout
    7. components:
    8. ingressGateways:
    9. - enabled: true
    10. k8s:
    11. hpaSpec:
    12. maxReplicas: 10
    13. minReplicas: 5
    14. serviceAnnotations:
    15. service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5"
    16. service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
    17. service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: elb-logs
    18. service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: k8sELBIngressGW
    19. service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
    20. affinity:
    21. podAntiAffinity:
    22. preferredDuringSchedulingIgnoredDuringExecution:
    23. - podAffinityTerm:
    24. istio: ingressgateway
    25. topologyKey: failure-domain.beta.kubernetes.io/zone
    26. weight: 1
    27. name: istio-ingressgateway

    如果您使用的是保留客户端 IP 地址的 TCP/UDP 网络负载均衡器(AWS 网络负载均衡器、GCP 外部网络负载均衡器、Azure 负载均衡器),或者您使用的是轮询模式的 DNS,那么您还可以保留客户端 IP 在 Kubernetes 内部绕过 kube-proxy 并阻止它向其他节点发送流量。 但是,您必须在每个节点上运行一个入口网关 pod。 如果不这样做,那么任何接收流量但没有入口网关的节点都会丢弃流量。 有关更多信息,请参阅 。 使用以下命令更新入口网关以设置 externalTrafficPolicy: Local 以保留入口网关上的原始客户端源 IP:

    1. $ kubectl patch svc istio-ingressgateway -n istio-system -p '{"spec":{"externalTrafficPolicy":"Local"}}'

    如果您使用的是 HTTP/HTTPS 外部负载均衡器(AWS ALB、GCP),它可以将原始客户端 IP 地址放在 X-Forwarded-For 标头中。 Istio 可以通过一些配置从此标头中提取客户端 IP 地址。 请参阅配置网关网络拓扑。 如果在 Kubernetes 前面使用单个负载均衡器,则快速示例:

    1. apiVersion: install.istio.io/v1alpha1
    2. kind: IstioOperator
    3. spec:
    4. meshConfig:
    5. accessLogEncoding: JSON
    6. accessLogFile: /dev/stdout
    7. defaultConfig:
    8. gatewayTopology:
    9. numTrustedProxies: 1

    作为参考,以下是 Istio 在流行的托管 Kubernetes 环境中使用LoadBalancer服务创建的负载均衡器类型:

    您可以在安装 Istio 时使用如下所示的 serviceAnnotation 指示 AWS EKS 创建网络负载均衡器:

    Load Balancer TypeSource of Client IPipBlocks vs. remoteIpBlocks
    TCP ProxyProxy ProtocolremoteIpBlocks
    Networkpacket source addressipBlocks
    HTTP/HTTPSX-Forwarded-ForremoteIpBlocks
    • 以下命令为 Istio 入口网关创建授权策略ingress-policy。 以下策略将action字段设置为ALLOW,以允ipBlocks中指定的 IP 地址访问入口网关。 不在列表中的 IP 地址将被拒绝。 ipBlocks 支持单个 IP 地址和 CIDR 表示法。

    创建授权策略:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. name: ingress-policy
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. app: istio-ingressgateway
    11. action: ALLOW
    12. rules:
    13. - from:
    14. - source:
    15. ipBlocks: ["1.2.3.4", "5.6.7.0/24"]
    16. EOF
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. name: ingress-policy
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. app: istio-ingressgateway
    11. action: ALLOW
    12. rules:
    13. - from:
    14. - source:
    15. remoteIpBlocks: ["1.2.3.4", "5.6.7.0/24"]
    • 验证对入口网关的请求是否被拒绝:

      1. $ curl "$INGRESS_HOST:$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
      2. 403
    • 更新 ingress-policy 以包含您的客户端 IP 地址:

    如果您不知道原始客户端 IP 地址并将其分配给变量,请查找它:

    1. $ CLIENT_IP=$(kubectl get pods -n istio-system -o name -l istio=ingressgateway | sed 's|pod/||' | while read -r pod; do kubectl logs "$pod" -n istio-system | grep remoteIP; done | tail -1 | awk -F, '{print $3}' | awk -F: '{print $2}' | sed 's/ //') && echo "$CLIENT_IP"
    2. 192.168.10.15
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. namespace: istio-system
    6. spec:
    7. selector:
    8. matchLabels:
    9. app: istio-ingressgateway
    10. action: ALLOW
    11. rules:
    12. - from:
    13. - source:
    14. ipBlocks: ["1.2.3.4", "5.6.7.0/24", "$CLIENT_IP"]
    15. EOF

    如果您不知道原始客户端 IP 地址并将其分配给变量,请查找它:

    1. $ CLIENT_IP=$(kubectl get pods -n istio-system -o name -l istio=ingressgateway | sed 's|pod/||' | while read -r pod; do kubectl logs "$pod" -n istio-system | grep remoteIP; done | tail -1 | awk -F, '{print $4}' | awk -F: '{print $2}' | sed 's/ //') && echo "$CLIENT_IP"
    2. 192.168.10.15

    创建授权策略:

    • 验证是否允许对入口网关的请求:

      1. $ curl "$INGRESS_HOST:$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
      2. 200
    • 更新 ingress-policy 授权策略,将 action 键设置为 DENY,从而不允许 ipBlocks 中指定的 IP 地址访问入口网关:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. name: ingress-policy
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. app: istio-ingressgateway
    11. action: DENY
    12. rules:
    13. - from:
    14. - source:
    15. ipBlocks: ["$CLIENT_IP"]
    16. EOF
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. name: ingress-policy
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. app: istio-ingressgateway
    11. action: DENY
    12. rules:
    13. - from:
    14. - source:
    15. remoteIpBlocks: ["$CLIENT_IP"]
    16. EOF
    • 验证对入口网关的请求是否被拒绝:

      1. $ curl "$INGRESS_HOST:$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
      2. 403
    • 您可以使用在线代理服务使用不同的客户端 IP 访问入口网关,以验证请求是否被允许。

    • 如果您没有得到预期的响应,请查看应显示 RBAC 调试信息的入口网关日志:

      1. $ kubectl get pods -n istio-system -o name -l istio=ingressgateway | sed 's|pod/||' | while read -r pod; do kubectl logs "$pod" -n istio-system; done
      1. $ kubectl delete namespace foo