Ingress 存取控制

此任務向您展示如何使用授權原則在 Istio ingress 閘道上強制執行基於 IP 的存取控制。

開始之前

在開始此任務之前,請執行以下操作

  • 閱讀 Istio 授權概念

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

  • 在命名空間 foo 中部署工作負載 httpbin,並啟用 Sidecar 注入

    Zip
    $ kubectl create ns foo
    $ kubectl label namespace foo istio-injection=enabled
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n foo
    
  • 透過 Ingress 閘道公開 httpbin

設定閘道

Zip
$ kubectl apply -f @samples/httpbin/httpbin-gateway.yaml@ -n foo

開啟 Ingress 閘道的 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

依照 判斷 Ingress 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。

  • LoadBalancerNodePort 非常相似,但它還會建立一個特定於環境的外部負載平衡器來處理將流量分配到工作節點。例如,在 AWS EKS 中,LoadBalancer 服務會建立一個經典 ELB,並以您的工作節點作為目標。如果您的 Kubernetes 環境沒有 LoadBalancer 實現,那麼它將只會像 NodePort 一樣運作。Istio 入口閘道會建立一個 LoadBalancer 服務。

如果處理來自 NodePortLoadBalancer 的流量的 Pod 沒有在接收流量的工作節點上執行怎麼辦?Kubernetes 有自己的內部代理,稱為 kube-proxy,它接收封包並將它們轉發到正確的節點。

原始用戶端的來源 IP 位址

如果封包通過外部代理負載平衡器和/或 kube-proxy,則客戶端的原始來源 IP 位址會遺失。以下小節將描述一些策略,用於為不同類型的負載平衡器保留原始客戶端 IP 以進行記錄或安全目的

  1. TCP/UDP Proxy 負載平衡器
  2. 網路負載平衡器
  3. HTTP/HTTPS 負載平衡器

為了方便參考,以下是在常見的受管理 Kubernetes 環境中使用 LoadBalancer 服務由 Istio 建立的負載平衡器類型

雲端供應商負載平衡器名稱負載平衡器類型
AWS EKS經典彈性負載平衡器TCP 代理
GCP GKETCP/UDP 網路負載平衡器網路
Azure AKSAzure 負載平衡器網路
IBM IKS/ROKS網路負載平衡器網路
DO DOKS負載平衡器網路

TCP/UDP Proxy 負載平衡器

如果您使用的是 TCP/UDP 代理外部負載平衡器 (AWS 經典 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
    
此資訊是否有用?
您是否有任何改進建議?

感謝您的回饋!