イングレスアクセス制御

このタスクでは、承認ポリシーを使用してIstioイングレスゲートウェイでIPベースのアクセス制御を強制する方法を示します。

始める前に

このタスクを開始する前に、次の手順を実行してください。

  • Istioの承認に関する概念をお読みください。

  • Istioインストールガイドを使用してIstioをインストールします。

  • サイドカーインジェクションが有効になっている名前空間fooにワークロードhttpbinをデプロイします。

    Zip
    $ kubectl create ns foo
    $ kubectl label namespace foo istio-injection=enabled
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n foo
    
  • イングレスゲートウェイを介してhttpbinを公開します。

ゲートウェイの設定

Zip
$ 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リソースでさえ、NodePortサービスまたはLoadBalancerサービスを作成するイングレスコントローラーによってバックアップされる必要があります。

  • NodePortは、各ワーカーノードの30000〜32767の範囲でポートを開き、ラベルセレクターを使用してトラフィックを送信するPodを識別します。ワーカーノードの前に何らかのロードバランサーを手動で作成するか、ラウンドロビンDNSを使用する必要があります。

  • LoadBalancerNodePortと似ていますが、ワーカーノードへのトラフィックの分散を処理するための環境固有の外部ロードバランサーも作成します。たとえば、AWS EKSでは、LoadBalancerサービスはワーカーノードをターゲットとして持つClassic ELBを作成します。Kubernetes環境にLoadBalancerの実装がない場合は、NodePortのように動作します。IstioイングレスゲートウェイはLoadBalancerサービスを作成します。

NodePortまたはLoadBalancerからのトラフィックを処理しているPodが、トラフィックを受信したワーカーノードで実行されていない場合はどうなりますか?Kubernetesには、パケットを受信して正しいノードに転送するkube-proxyと呼ばれる独自の内部プロキシがあります。

元のクライアントの送信元IPアドレス

パケットが外部プロキシロードバランサーとkube-proxyを通過する場合、クライアントの元の送信元IPアドレスは失われます。次のセクションでは、さまざまなロードバランサーの種類について、ログ記録またはセキュリティの目的で元のクライアントIPを保持するためのいくつかの戦略について説明します。

  1. TCP/UDPプロキシロードバランサー
  2. ネットワークロードバランサー
  3. HTTP/HTTPSロードバランサー

参考までに、一般的なマネージドKubernetes環境でLoadBalancerサービスを使用してIstioによって作成されるロードバランサーの種類を以下に示します。

クラウドプロバイダーロードバランサー名ロードバランサーの種類
AWS EKSClassic Elastic Load BalancerTCPプロキシ
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設定を使用して、kube-proxyをバイパスし、他のノードへのトラフィックの送信を防ぐことで、Kubernetes内部でもクライアントIPを保持できます。

次のコマンドを使用して、イングレスゲートウェイを更新し、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アドレスを決定する場合は、AuthorizationPolicyremoteIpBlocksを使用する必要があります。externalTrafficPolicy: Localを使用する場合は、AuthorizationPolicyipBlocksを使用する必要があります。

ロードバランサーの種類クライアント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アドレスをenv変数に割り当てます。わからない場合は、次のコマンドを使用して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
  • クライアントIPアドレスを含むようにingress-policyを更新します。

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
    
この情報は役に立ちましたか?
改善のための提案はありますか?

ご意見ありがとうございます!