IBM Cloud Kubernetes Service Ingress から Istio Ingress Gateway への暗号化されたトラフィックの直接送信

相互 TLS を使用して、IBM Cloud Kubernetes Service Application Load Balancer が Istio Ingress ゲートウェイにトラフィックを送信するように構成します。

2020年5月15日 | Vadim Eisenberg - IBM

このブログ記事では、Ingress Application Load Balancer(ALB)IBM Cloud Kubernetes Service(IKS)上で構成し、相互 TLS 認証を使用して、それらの間のトラフィックを保護しながら、Istio Ingress ゲートウェイにトラフィックを送信する方法を示します。

Istio なしで IKS を使用する場合、提供された ALB を使用してイングレス トラフィックを制御できます。このイングレス トラフィックのルーティングは、Kubernetes のIngressリソースとALB 固有のアノテーションを使用して構成されます。IKS は、DNS ドメイン名、そのドメインに一致する TLS 証明書、および証明書の秘密鍵を提供します。IKS は、証明書と秘密鍵をKubernetes シークレットに保存します。

IKS クラスターで Istio の使用を開始する場合、Istio 対応のワークロードにトラフィックを送信するための推奨される方法は、Kubernetes Ingressを使用する代わりに、Istio Ingress Gatewayを使用することです。Istio Ingress ゲートウェイを使用する主な理由の 1 つは、STRICT 相互 TLS を有効にすると、IKS によって提供される ALB がメッシュ内のサービスと直接通信できなくなるという事実です。Istio Ingress ゲートウェイのみをメインのエントリポイントとする移行中に、非 Istio サービスには従来の Ingress を引き続き使用し、メッシュの一部であるサービスには Istio Ingress ゲートウェイを使用できます。

IKS は、Istio ゲートウェイの IP 用に新しい DNS サブドメインを登録できるようにすることで、クライアントが Istio Ingress ゲートウェイにアクセスするための便利な方法を提供します。ドメインは、次の形式です: <cluster_name>-<globally_unique_account_HASH>-0001.<region>.containers.appdomain.cloud。たとえば、mycluster-a1b2cdef345678g9hi012j3kl4567890-0001.us-south.containers.appdomain.cloud などです。ALB ドメインの場合と同じように、IKS は証明書と秘密鍵を提供し、別の Kubernetes シークレットに保存します。

このブログでは、IKS Ingress ALB と Istio Ingress ゲートウェイをチェーン接続して、ALB 固有の機能と ALB サブドメイン名を引き続き使用できるようにしながら、Istio 対応のワークロードにトラフィックを送信する方法について説明します。ALB とゲートウェイ間の相互 TLS 認証を使用しながら、Istio Ingress ゲートウェイを介して Istio サービス メッシュ内のサービスにトラフィックを送信するように、IKS Ingress ALB を構成します。相互 TLS 認証では、ALB と Istio Ingress ゲートウェイが、ALB および NLB サブドメイン用に IKS によって提供される証明書と鍵を使用するように構成します。IKS によって提供される証明書を使用すると、ALB と Istio Ingress ゲートウェイ間の接続用に独自の証明書を管理するオーバーヘッドを節約できます。

NLB サブドメイン証明書を、意図したとおり Istio Ingress ゲートウェイのサーバー証明書として使用します。NLB サブドメイン証明書は、特定の NLB サブドメイン、この場合は Ingress ゲートウェイを提供するサーバーの ID を表します。

ALB サブドメイン証明書を、ALB と Istio Ingress 間の相互 TLS 認証のクライアント証明書として使用します。ALB がサーバーとして動作する場合、クライアントが ALB を認証できるように、ALB 証明書をクライアントに提示します。ALB が Istio Ingress ゲートウェイのクライアントとして動作する場合、Istio Ingress ゲートウェイが ALB を認証できるように、同じ証明書を Istio Ingress ゲートウェイに提示します。

Istio サイドカーのないサービスへのトラフィックは、以前と同様に ALB から直接流れ続けることができます。

以下の図は、説明した設定を示しています。クラスター内の 2 つのサービス、サービス Aサービス B を示しています。サービス A には Istio サイドカーが挿入されており、相互 TLS が必要です。サービス B には Istio サイドカーがありません。サービス B には、サービス B と直接通信する ALB を介してクライアントがアクセスできます。サービス A にも、ALB を介してクライアントがアクセスできますが、この場合は、トラフィックは Istio Ingress ゲートウェイを通過する必要があります。ALB とゲートウェイ間の相互 TLS 認証は、IKS によって提供される証明書に基づいています。クライアントは Istio Ingress ゲートウェイに直接アクセスすることもできます。IKS は、ALB と Ingress ゲートウェイに異なる DNS ドメインを登録します。

A cluster with the ALB and the Istio ingress gateway
ALB と Istio Ingress ゲートウェイを備えたクラスター

初期設定

  1. httptools 名前空間を作成し、Istio サイドカーの挿入を有効にします。

    $ kubectl create namespace httptools
    $ kubectl label namespace httptools istio-injection=enabled
    namespace/httptools created
    namespace/httptools labeled
    
  2. httpbin サンプルを httptools にデプロイします。

    Zip
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n httptools
    service/httpbin created
    deployment.apps/httpbin created
    

ALB および Istio Ingress ゲートウェイ用のシークレットを作成します。

IKS は、ibmcloud ks nlb-dns-create コマンドを使用して外部 IP の DNS ドメインを登録すると、TLS 証明書と秘密鍵を生成し、それらをデフォルト名前空間にシークレットとして保存します。IKS は、ALB の証明書と秘密鍵も、デフォルト名前空間にシークレットとして保存します。これらの認証情報は、ALB と Istio Ingress ゲートウェイが相互 TLS 認証中に提示する ID を確立するために必要です。ALB と Istio Ingress ゲートウェイがこれらの証明書を交換し、互いの証明書を信頼し、秘密鍵を使用してトラフィックを暗号化および署名するように構成します。

  1. クラスターの名前を CLUSTER_NAME 環境変数に保存します。

    $ export CLUSTER_NAME=<your cluster name>
    
  2. ALB のドメイン名を ALB_INGRESS_DOMAIN 環境変数に保存します。

    $ ibmcloud ks cluster get --cluster $CLUSTER_NAME | grep Ingress
    Ingress Subdomain:              <your ALB ingress domain>
    Ingress Secret:                 <your ALB secret>
    
    $ export ALB_INGRESS_DOMAIN=<your ALB ingress domain>
    $ export ALB_SECRET=<your ALB secret>
    
  3. istio-ingressgateway サービスの外部 IP を環境変数に保存します。

    $ export INGRESS_GATEWAY_IP=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    $ echo INGRESS_GATEWAY_IP = $INGRESS_GATEWAY_IP
    
  4. Istio Ingress ゲートウェイサービスの IP の DNS ドメインと証明書を作成します。

    $ ibmcloud ks nlb-dns create classic --cluster $CLUSTER_NAME --ip $INGRESS_GATEWAY_IP --secret-namespace istio-system
    Host name subdomain is created as <some domain>
    
  5. 前のコマンドのドメイン名を環境変数に保存します。

    $ export INGRESS_GATEWAY_DOMAIN=<the domain from the previous command>
    
  6. 登録済みのドメイン名を一覧表示します。

    $ ibmcloud ks nlb-dnss --cluster $CLUSTER_NAME
    Retrieving host names, certificates, IPs, and health check monitors for network load balancer (NLB) pods in cluster <your cluster>...
    OK
    Hostname                          IP(s)                       Health Monitor   SSL Cert Status   SSL Cert Secret Name                          Secret Namespace
    <your ingress gateway hostname>   <your ingress gateway IP>   None             created           <the matching secret name>           istio-system
    ...
    

    新しいドメイン名の証明書(4 番目のフィールド)のステータスが enabled になるまで待ちます(最初は pending です)。

  7. 新しいドメイン名のシークレットの名前を保存します。

    $ export INGRESS_GATEWAY_SECRET=<the secret's name as shown in the SSL Cert Secret Name column>
    
  8. ALB に提供されたシークレットから証明書と鍵を抽出します。

    $ mkdir alb_certs
    $ kubectl get secret $ALB_SECRET --namespace=default -o yaml | grep 'tls.key:' | cut -f2 -d: | base64 --decode > alb_certs/client.key
    $ kubectl get secret $ALB_SECRET --namespace=default -o yaml | grep 'tls.crt:' | cut -f2 -d: | base64 --decode > alb_certs/client.crt
    $ ls -al alb_certs
    -rw-r--r--   1 user  staff  3738 Sep 11 07:57 client.crt
    -rw-r--r--   1 user  staff  1675 Sep 11 07:57 client.key
    
  9. Let’s Encrypt証明書の発行者である、IKS によって提供される証明書の発行者証明書をダウンロードします。この証明書を、ALB と Istio Ingress ゲートウェイの両方で信頼する認証局の証明書として指定します。

    $ curl https://letsencrypt.dokyumento.jp/certs/trustid-x3-root.pem --output trusted.crt
    
  10. 相互 TLS 接続を確立するために ALB によって使用される Kubernetes シークレットを作成します。

    $ kubectl create secret generic alb-certs -n istio-system --from-file=trusted.crt --from-file=alb_certs/client.crt --from-file=alb_certs/client.key
    secret "alb-certs" created
    
  11. 相互 TLS の場合、cacert キーを持つ <tls-cert-secret>-cacert という名前の別のシークレットがイングレス ゲートウェイに必要です。

    $ kubectl create -n istio-system secret generic $INGRESS_GATEWAY_SECRET-cacert --from-file=ca.crt=trusted.crt
    secret/cluster_name-hash-XXXX-cacert created
    

相互 TLS イングレス ゲートウェイを構成します。

このセクションでは、外部クライアントとゲートウェイの間で相互 TLS を実行するように Istio イングレス ゲートウェイを構成します。イングレス ゲートウェイと ALB に提供された証明書と鍵を使用します。

  1. ポート 443 でのみ相互 TLS を使用したアクセスを許可する Gateway を定義します。

    $ kubectl apply -n httptools -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: default-ingress-gateway
    spec:
      selector:
        istio: ingressgateway # use istio default ingress gateway
      servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        tls:
          mode: MUTUAL
          credentialName: $INGRESS_GATEWAY_SECRET
        hosts:
        - "$INGRESS_GATEWAY_DOMAIN"
        - "httpbin.$ALB_INGRESS_DOMAIN"
    EOF
    
  2. Gateway を介して入るトラフィックのルートを構成します。

    $ kubectl apply -n httptools -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: default-ingress
    spec:
      hosts:
      - "$INGRESS_GATEWAY_DOMAIN"
      - "httpbin.$ALB_INGRESS_DOMAIN"
      gateways:
      - default-ingress-gateway
      http:
      - match:
        - uri:
            prefix: /status
        route:
        - destination:
            port:
              number: 8000
            host: httpbin.httptools.svc.cluster.local
    EOF
    
  3. クライアント証明書(--cert オプション)と秘密鍵(--key オプション)をパラメータとして渡し、curl を使用して httpbin にリクエストを送信します。

    $ curl https://$INGRESS_GATEWAY_DOMAIN/status/418 --cert alb_certs/client.crt  --key alb_certs/client.key
    
    -=[ teapot ]=-
    
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
    
  4. ALB およびイングレス ゲートウェイの証明書と鍵を含むディレクトリを削除します。

    $ rm -r alb_certs trusted.crt
    

ALB を構成します。

alb-certs シークレットに保存されている証明書を使用しながら、Istio イングレス ゲートウェイにトラフィックを送信するように、Ingress リソースを構成する必要があります。通常、ALB はトラフィックをアプリに転送する前に HTTPS リクエストを復号化します。Ingress リソースの ssl-services アノテーションを使用すると、Istio イングレス ゲートウェイに転送する前にトラフィックを再暗号化するように ALB を構成できます。このアノテーションを使用すると、相互 TLS に必要な alb-certs シークレットに保存されている証明書を指定することもできます。

  1. ALB の Ingress リソースを構成します。トラフィックを Istio イングレス ゲートウェイに転送するには、istio-system 名前空間に Ingress リソースを作成する必要があります。

    $ kubectl apply -f - <<EOF
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: alb-ingress
      namespace: istio-system
      annotations:
        ingress.bluemix.net/ssl-services: "ssl-service=istio-ingressgateway ssl-secret=alb-certs proxy-ssl-name=$INGRESS_GATEWAY_DOMAIN"
    spec:
      tls:
      - hosts:
        - httpbin.$ALB_INGRESS_DOMAIN
        secretName: $ALB_SECRET
      rules:
      - host: httpbin.$ALB_INGRESS_DOMAIN
        http:
          paths:
          - path: /status
            backend:
              serviceName: istio-ingressgateway
              servicePort: 443
    EOF
    
  2. ALB イングレスをテストします。

    $ curl https://httpbin.$ALB_INGRESS_DOMAIN/status/418
    
    -=[ teapot ]=-
    
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
    

おめでとうございます!IKS Ingress ALB が Istio イングレスゲートウェイに暗号化されたトラフィックを送信するように設定されました。Istio イングレスゲートウェイのホスト名と証明書を割り当て、その証明書を Istio イングレスゲートウェイのサーバー証明書として使用しました。ALB のクライアント証明書として、IKS が ALB に提供した証明書を使用しました。証明書を Kubernetes シークレットとしてデプロイしたら、特定のパスのイングレストラフィックを ALB から Istio イングレスゲートウェイに転送し、ALB と Istio イングレスゲートウェイ間の相互 TLS 認証に証明書を使用しました。

クリーンアップ

  1. Gateway 設定、VirtualService、およびシークレットを削除します。

    $ kubectl delete ingress alb-ingress -n istio-system
    $ kubectl delete virtualservice default-ingress -n httptools
    $ kubectl delete gateway default-ingress-gateway -n httptools
    $ kubectl delete secrets alb-certs -n istio-system
    $ rm -rf alb_certs trusted.crt
    $ unset CLUSTER_NAME ALB_INGRESS_DOMAIN ALB_SECRET INGRESS_GATEWAY_DOMAIN INGRESS_GATEWAY_SECRET
    
  2. httpbin サービスをシャットダウンします。

    Zip
    $ kubectl delete -f @samples/httpbin/httpbin.yaml@ -n httptools
    
  3. httptools 名前空間を削除します。

    $ kubectl delete namespace httptools
    
この記事を共有