入口网关

此任务向您展示如何使用授权策略在 Istio 入口网关上实施基于 IP 的访问控制。

开始之前

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

  • 阅读 Istio 授权概念

  • 使用 Istio 安装指南安装 Istio。

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

    ZipZip
    $ kubectl create ns foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin-gateway.yaml@) -n foo
    
  • 在 Envoy 中为入口网关打开 RBAC 调试:

    $ 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
    
  • 遵从确定入口 IP 和端口中的指示说明来定义 INGRESS_HOSTINGRESS_PORT 环境变量。

  • 使用以下命令验证 httpbin 工作负载和入口网关正在按预期工作:

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

将流量引入 Kubernetes 和 Istio

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

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

  • LoadBalancer 就像 NodePort 一样,除了它还创建一个特定于环境的外部负载均衡器来处理将流量分配到工作节点。例如,在 AWS EKS 中,LoadBalancer 服务将创建一个以您的工作程序节点为目标的经典 ELB。如果您的 Kubernetes 环境没有 LoadBalancer 实现,那么它的行为就像 NodePort。Istio 入口网关创建一个 LoadBalancer服务。

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

原始客户端的源 IP 地址

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

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

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: proxy-protocol
  namespace: istio-system
spec:
  configPatches:
  - applyTo: LISTENER
    patch:
      operation: MERGE
      value:
        listener_filters:
        - name: envoy.listener.proxy_protocol
        - name: envoy.listener.tls_inspector
  workloadSelector:
    labels:
      istio: ingressgateway

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

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    accessLogEncoding: JSON
    accessLogFile: /dev/stdout
  components:
    ingressGateways:
    - enabled: true
      k8s:
        hpaSpec:
          maxReplicas: 10
          minReplicas: 5
        serviceAnnotations:
          service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5"
          service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
          service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: elb-logs
          service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: k8sELBIngressGW
          service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
        affinity:
          podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
            - podAffinityTerm:
                labelSelector:
                  matchLabels:
                    istio: ingressgateway
                topologyKey: failure-domain.beta.kubernetes.io/zone
              weight: 1
      name: istio-ingressgateway

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

云提供商负载均衡器名称负载均衡器类型
AWS EKSClassic Elastic Load BalancerTCP Proxy
GCP GKETCP/UDP Network Load BalancerNetwork
Azure AKSAzure Load BalancerNetwork
DO DOKSLoad BalancerNetwork

基于 IP 的允许列表和拒绝列表

何时使用 ipBlocksremoteIpBlocks 如果您使用 X-Forwarded-For HTTP 标头或代理协议来确定原始客户端 IP 地址,那么您应该在您的 AuthorizationPolicy 中使用 remoteIpBlocks。 如果您使用的是 externalTrafficPolicy: Local,那么您应该在 AuthorizationPolicy 中使用 ipBlocks

负载均衡器类型客户端源 IPipBlocksremoteIpBlocks
TCP ProxyProxy ProtocolremoteIpBlocks
Networkpacket source addressipBlocks
HTTP/HTTPSX-Forwarded-ForremoteIpBlocks
  • 以下命令为 Istio 入口网关创建授权策略 ingress-policy。以下策略将 action 字段设置为 ALLOW,以允 ipBlocks 中指定的 IP 地址访问入口网关。不在列表中的 IP 地址将被拒绝。ipBlocks 支持单个 IP 地址和 CIDR 表示法。

创建授权策略:

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
  - from:
    - source:
        ipBlocks: ["1.2.3.4", "5.6.7.0/24"]
EOF
  • 验证对入口网关的请求是否被拒绝:

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

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

$ 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"
192.168.10.15
$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
  - from:
    - source:
        ipBlocks: ["1.2.3.4", "5.6.7.0/24", "$CLIENT_IP"]
EOF
  • 验证是否允许对入口网关的请求:

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

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: DENY
  rules:
  - from:
    - source:
        ipBlocks: ["$CLIENT_IP"]
EOF
  • 验证对入口网关的请求是否被拒绝:

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

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

    $ 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
    

清理

  • 移除命名空间 foo

    $ kubectl delete namespace foo
    
  • 移除授权策略:

    $ kubectl delete authorizationpolicy ingress-policy -n istio-system
    
这些信息有用吗?
Do you have any suggestions for improvement?

Thanks for your feedback!