Azure Kubernetes Service 用の Istio サービス メッシュ アドオンのセキュリティで保護されたイングレス ゲートウェイ

外部または内部の Istio イングレスのデプロイに関する記事では、外部または内部トラフィックに HTTP サービスを公開するようにイングレス ゲートウェイを構成する方法について説明しています。 この記事では、単純な TLS または相互 TLS を使用して、セキュリティで保護された HTTPS サービスを公開する方法について説明します。

前提条件

Note

この記事では、デモ用の外部イングレス ゲートウェイを参照します。内部イングレス ゲートウェイの相互 TLS の構成にも同じ手順が適用されます。

必要なクライアント/サーバーの証明書とキー

この記事では、いくつかの証明書とキーが必要です。 お気に入りのツールを使用して作成することも、以下の openssl コマンドを使用することもできます。

  1. サンプル サービスの証明書に署名するためのルート証明書と秘密キーを作成します。

    mkdir bookinfo_certs
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=bookinfo Inc./CN=bookinfo.com' -keyout bookinfo_certs/bookinfo.com.key -out bookinfo_certs/bookinfo.com.crt
    
  2. productpage.bookinfo.com の新しい証明書と秘密キーを生成します。

    openssl req -out bookinfo_certs/productpage.bookinfo.com.csr -newkey rsa:2048 -nodes -keyout bookinfo_certs/productpage.bookinfo.com.key -subj "/CN=productpage.bookinfo.com/O=product organization"
    openssl x509 -req -sha256 -days 365 -CA bookinfo_certs/bookinfo.com.crt -CAkey bookinfo_certs/bookinfo.com.key -set_serial 0 -in bookinfo_certs/productpage.bookinfo.com.csr -out bookinfo_certs/productpage.bookinfo.com.crt
    
  3. クライアントの証明書と秘密キーを生成します。

    openssl req -out bookinfo_certs/client.bookinfo.com.csr -newkey rsa:2048 -nodes -keyout bookinfo_certs/client.bookinfo.com.key -subj "/CN=client.bookinfo.com/O=client organization"
    openssl x509 -req -sha256 -days 365 -CA bookinfo_certs/bookinfo.com.crt -CAkey bookinfo_certs/bookinfo.com.key -set_serial 1 -in bookinfo_certs/client.bookinfo.com.csr -out bookinfo_certs/client.bookinfo.com.crt
    

TLS イングレス ゲートウェイを構成する

イングレス ゲートウェイの Kubernetes TLS シークレットを作成します。Azure Key Vault を使用して証明書とキーをホストし、Azure Key Vault シークレット プロバイダー アドオンを使用してシークレットをクラスターに同期します。

Azure Key Vault を設定してシークレットをクラスターに同期する

  1. Azure Key Vault を作成する

    Istio アドオンに証明書とキー入力を提供するには、Azure Key Vault リソースが必要です。

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. クラスターのシークレット ストア CSI ドライバーに対して Azure Key Vault プロバイダーを有効にします。

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. アクセス ポリシーを使用して、アドオンのユーザー割り当てマネージド ID による Azure Key Vault リソースへのアクセスを承認します。 または、Key Vault でアクセス許可モデルとして Azure RBAC を使用している場合は、こちらの手順 に従って、アドオンのユーザー割り当てマネージド ID に Key Vault の Azure ロールを割り当てます。

    OBJECT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.objectId' -o tsv | tr -d '\r')
    CLIENT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.clientId')
    TENANT_ID=$(az keyvault show --resource-group $RESOURCE_GROUP --name $AKV_NAME --query 'properties.tenantId')
    
    az keyvault set-policy --name $AKV_NAME --object-id $OBJECT_ID --secret-permissions get list
    
  4. 証明書とキーを使用して Azure Key Vault にシークレットを作成します。

    az keyvault secret set --vault-name $AKV_NAME --name test-productpage-bookinfo-key --file bookinfo_certs/productpage.bookinfo.com.key
    az keyvault secret set --vault-name $AKV_NAME --name test-productpage-bookinfo-crt --file bookinfo_certs/productpage.bookinfo.com.crt
    az keyvault secret set --vault-name $AKV_NAME --name test-bookinfo-crt --file bookinfo_certs/bookinfo.com.crt
    
  5. 次のマニフェストを使用して SecretProviderClass をデプロイし、Azure Key Vault 固有のパラメーターを CSI ドライバーに提供します。

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: productpage-credential-spc
      namespace: aks-istio-ingress
    spec:
      provider: azure
      secretObjects:
      - secretName: productpage-credential
        type: tls
        data:
        - objectName: test-productpage-bookinfo-key
          key: key
        - objectName: test-productpage-bookinfo-crt
          key: cert
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-productpage-bookinfo-key
              objectType: secret
              objectAlias: "test-productpage-bookinfo-key"
            - |
              objectName: test-productpage-bookinfo-crt
              objectType: secret
              objectAlias: "test-productpage-bookinfo-crt"
        tenantId: $TENANT_ID
    EOF
    
  6. 次のマニフェストを使用してサンプル ポッドをデプロイします。 シークレット ストア CSI ドライバーを使用するには、Azure Key Vault からクラスターにシークレットが確実に同期されるように、ポッドが SecretProviderClass リソースを参照している必要があります。

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: secrets-store-sync-productpage
      namespace: aks-istio-ingress
    spec:
      containers:
        - name: busybox
          image: mcr.microsoft.com/oss/busybox/busybox:1.33.1
          command:
            - "/bin/sleep"
            - "10"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "productpage-credential-spc"
    EOF
    
    • SecretProviderClass リソースで定義されているクラスター名前空間 aks-istio-ingress に作成された productpage-credential シークレットを確認します。

      kubectl describe secret/productpage-credential -n aks-istio-ingress
      

      出力例:

      Name:         productpage-credential
      Namespace:    aks-istio-ingress
      Labels:       secrets-store.csi.k8s.io/managed=true
      Annotations:  <none>
      
      Type:  tls
      
      Data
      ====
      cert:  1066 bytes
      key:   1704 bytes
      

イングレス ゲートウェイと仮想サービスを構成する

Istio イングレス ゲートウェイを経由してサンプル アプリケーションに HTTPS トラフィックをルーティングします。 次のマニフェストを使用して、ゲートウェイと仮想サービス リソースをデプロイします。

cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: aks-istio-ingressgateway-external
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: productpage-credential
    hosts:
    - productpage.bookinfo.com
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-vs
spec:
  hosts:
  - productpage.bookinfo.com
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        port:
          number: 9080
        host: productpage
EOF

Note

ゲートウェイ定義では、credentialName は SecretProviderClass リソースの secretName と一致する必要があります。また、selector は、外部イングレス ゲートウェイをラベルによって参照する必要があります。このとき、ラベルのキーは istio であり、値は aks-istio-ingressgateway-external です。 内部イングレス ゲートウェイの場合、ラベルは istio で、値は aks-istio-ingressgateway-internal です。

外部イングレスのホストとポートの環境変数を設定します。

export INGRESS_HOST_EXTERNAL=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export SECURE_INGRESS_PORT_EXTERNAL=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
export SECURE_GATEWAY_URL_EXTERNAL=$INGRESS_HOST_EXTERNAL:$SECURE_INGRESS_PORT_EXTERNAL

echo "https://$SECURE_GATEWAY_URL_EXTERNAL/productpage"

検証

HTTPS 経由で productpage サービスにアクセスするための HTTPS 要求を送信します。

curl -s -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" | grep -o "<title>.*</title>"

サンプル アプリケーションの製品ページにアクセス可能であることを確認します。 予想される出力は次のとおりです。

<title>Simple Bookstore App</title>

Note

HTTPS サービスへの HTTPS イングレス アクセスを構成する、つまり、受信要求で TLS 終端ではなく SNI パススルーを実行するようにイングレス ゲートウェイを構成するには、ゲートウェイ定義の tls モードを PASSTHROUGH に更新します。 これにより、TLS を終端せずに、イングレス トラフィックを "そのまま" 渡すようにゲートウェイに指示します。

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

相互 TLS をサポートするようにゲートウェイ定義を拡張します。

  1. 現在のシークレットを削除して新しいものを作成することで、イングレス ゲートウェイの資格情報を更新します。 サーバーは CA 証明書を使用してクライアントを検証します。CA 証明書を保持するには、キー ca.crt を使用する必要があります。

    kubectl delete secretproviderclass productpage-credential-spc -n aks-istio-ingress
    kubectl delete secret/productpage-credential -n aks-istio-ingress
    kubectl delete pod/secrets-store-sync-productpage -n aks-istio-ingress
    

    次のマニフェストを使用し、CA 証明書を使って SecretProviderClass を再作成します。

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: productpage-credential-spc
      namespace: aks-istio-ingress
    spec:
      provider: azure
      secretObjects:
      - secretName: productpage-credential
        type: opaque
        data:
        - objectName: test-productpage-bookinfo-key
          key: tls.key
        - objectName: test-productpage-bookinfo-crt
          key: tls.crt
        - objectName: test-bookinfo-crt
          key: ca.crt
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-productpage-bookinfo-key
              objectType: secret
              objectAlias: "test-productpage-bookinfo-key"
            - |
              objectName: test-productpage-bookinfo-crt
              objectType: secret
              objectAlias: "test-productpage-bookinfo-crt"
            - |
              objectName: test-bookinfo-crt
              objectType: secret
              objectAlias: "test-bookinfo-crt"
        tenantId: $TENANT_ID
    EOF
    

    次のマニフェストを使用して、Azure Key Vault からクラスターにシークレットを同期するサンプル ポッドを再デプロイします。

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: secrets-store-sync-productpage
      namespace: aks-istio-ingress
    spec:
      containers:
        - name: busybox
          image: registry.k8s.io/e2e-test-images/busybox:1.29-4
          command:
            - "/bin/sleep"
            - "10"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "productpage-credential-spc"
    EOF
    
    • クラスター名前空間 aks-istio-ingress に作成された productpage-credential シークレットを確認します。

      kubectl describe secret/productpage-credential -n aks-istio-ingress
      

      出力例:

      Name:         productpage-credential
      Namespace:    aks-istio-ingress
      Labels:       secrets-store.csi.k8s.io/managed=true
      Annotations:  <none>
      
      Type:  opaque
      
      Data
      ====
      ca.crt:   1188 bytes
      tls.crt:  1066 bytes
      tls.key:  1704 bytes
      
  2. 次のマニフェストを使用してゲートウェイ定義を更新し、TLS モードを MUTUAL に設定します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: bookinfo-gateway
    spec:
      selector:
        istio: aks-istio-ingressgateway-external # use istio default ingress gateway
      servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        tls:
          mode: MUTUAL
          credentialName: productpage-credential # must be the same as secret
        hosts:
        - productpage.bookinfo.com
    EOF
    

検証

クライアント証明書を渡さずに、以前の方法を使用して HTTPS 要求の送信を試み、失敗することを確認します。

curl -v -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" 

出力例:


...
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS alert, unknown (628):
* OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0
* Failed receiving HTTP2 data
* OpenSSL SSL_write: SSL_ERROR_ZERO_RETURN, errno 0
* Failed sending HTTP2 data
* Connection #0 to host productpage.bookinfo.com left intact
curl: (56) OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0

--cert フラグを使用してクライアントの証明書を、--key フラグを使用して秘密キーを curl に渡します。

curl -s -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt --cert bookinfo_certs/client.bookinfo.com.crt --key bookinfo_certs/client.bookinfo.com.key "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" | grep -o "<title>.*</title>"

サンプル アプリケーションの製品ページにアクセス可能であることを確認します。 予想される出力は次のとおりです。

<title>Simple Bookstore App</title>

リソースを削除する

Istio サービス メッシュとイングレスをクリーンアップする (クラスターを残す) 場合は、次のコマンドを実行します。

az aks mesh disable --resource-group ${RESOURCE_GROUP} --name ${CLUSTER}

Istio の使用方法ガイダンスのドキュメントに従って作成したすべてのリソースをクリーンアップする場合は、次のコマンドを実行します。

az group delete --name ${RESOURCE_GROUP} --yes --no-wait