入口访问控制

本任务演示如何使用授权策略在 Istio 入站网关上实施基于 IP 的访问控制。

开始之前

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

  • 阅读 Istio 授权概念

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

  • 在命名空间foo中部署工作负载httpbin,并启用 sidecar 注射。

    压缩
    $ kubectl create ns foo
    $ kubectl label namespace foo istio-injection=enabled
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n foo
    
  • 通过入站网关公开httpbin

配置网关

压缩
$ kubectl apply -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_PORTINGRESS_HOST环境变量。

  • 使用以下命令验证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服务将创建一个 Classic ELB,并将您的工作节点作为目标。如果您的 Kubernetes 环境没有LoadBalancer实现,那么它将表现得像NodePort一样。Istio 入站网关创建LoadBalancer服务。

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

原始客户端的源 IP 地址

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

  1. TCP/UDP 代理负载均衡器
  2. 网络负载均衡器
  3. HTTP/HTTPS 负载均衡器

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

云提供商负载均衡器名称负载均衡器类型
AWS EKS经典弹性负载均衡器TCP 代理
GCP GKETCP/UDP 网络负载均衡器网络
Azure AKSAzure 负载均衡器网络
IBM IKS/ROKS网络负载均衡器网络
DO DOKS负载均衡器网络

TCP/UDP 代理负载均衡器

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

这是一个示例配置,它演示了如何在 AWS EKS 上使入站网关支持 PROXY 协议。

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    accessLogEncoding: JSON
    accessLogFile: /dev/stdout
    defaultConfig:
      gatewayTopology:
        proxyProtocol: {}
  components:
    ingressGateways:
    - enabled: true
      name: istio-ingressgateway
      k8s:
        hpaSpec:
          maxReplicas: 10
          minReplicas: 5
        serviceAnnotations:
          service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
        ...

网络负载均衡器

如果您使用的是保留客户端 IP 地址的 TCP/UDP 网络负载均衡器(AWS 网络负载均衡器、GCP 外部网络负载均衡器、Azure 负载均衡器)或使用轮循 DNS,则可以使用externalTrafficPolicy: Local设置在 Kubernetes 内部保留客户端 IP,方法是绕过 kube-proxy 并阻止它将流量发送到其他节点。

更新入站网关以设置externalTrafficPolicy: Local,以便在入站网关上保留原始客户端源 IP,请使用以下命令

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

HTTP/HTTPS 负载均衡器

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

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    accessLogEncoding: JSON
    accessLogFile: /dev/stdout
    defaultConfig:
      gatewayTopology:
        numTrustedProxies: 1

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

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

负载均衡器类型客户端 IP 的来源ipBlocksremoteIpBlocks
TCP 代理PROXY 协议remoteIpBlocks
网络数据包源地址ipBlocks
HTTP/HTTPSX-Forwarded-ForremoteIpBlocks
  • 以下命令创建 Istio 入站网关的授权策略ingress-policy。以下策略将action字段设置为ALLOW,以允许ipBlocks中指定的 IP 地址访问入站网关。列表中不存在的 IP 地址将被拒绝。ipBlocks支持单个 IP 地址和 CIDR 表示法。

ipBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
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

remoteIpBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
  - from:
    - source:
        remoteIpBlocks: ["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
    
  • 将您的原始客户端 IP 地址分配给一个环境变量。如果您不知道它,您可以使用以下命令在 Envoy 日志中找到它

ipBlocks

$ 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

remoteIpBlocks

$ 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"
192.168.10.15
  • 更新ingress-policy以包含您的客户端 IP 地址

ipBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
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

remoteIpBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
  - from:
    - source:
        remoteIpBlocks: ["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 地址不允许访问入站网关

ipBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: DENY
  rules:
  - from:
    - source:
        ipBlocks: ["$CLIENT_IP"]
EOF

remoteIpBlocks

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: ingress-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: DENY
  rules:
  - from:
    - source:
        remoteIpBlocks: ["$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

清理

  • 删除授权策略
$ kubectl delete authorizationpolicy ingress-policy -n istio-system
  • 删除命名空间foo

    $ kubectl delete namespace foo
    
这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!