Azure Kubernetes Service 用の Istio サービス メッシュ アドオンのセキュリティで保護されたイングレス ゲートウェイ
外部または内部の Istio イングレスのデプロイに関する記事では、外部または内部トラフィックに HTTP サービスを公開するようにイングレス ゲートウェイを構成する方法について説明しています。 この記事では、単純な TLS または相互 TLS を使用して、セキュリティで保護された HTTPS サービスを公開する方法について説明します。
前提条件
Note
この記事では、デモ用の外部イングレス ゲートウェイを参照します。内部イングレス ゲートウェイの相互 TLS の構成にも同じ手順が適用されます。
必要なクライアント/サーバーの証明書とキー
この記事では、いくつかの証明書とキーが必要です。 お気に入りのツールを使用して作成することも、以下の openssl コマンドを使用することもできます。
サンプル サービスの証明書に署名するためのルート証明書と秘密キーを作成します。
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
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
クライアントの証明書と秘密キーを生成します。
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 を設定してシークレットをクラスターに同期する
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
クラスターのシークレット ストア CSI ドライバーに対して Azure Key Vault プロバイダーを有効にします。
az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
アクセス ポリシーを使用して、アドオンのユーザー割り当てマネージド 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
証明書とキーを使用して 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
次のマニフェストを使用して 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
次のマニフェストを使用してサンプル ポッドをデプロイします。 シークレット ストア 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 をサポートするようにゲートウェイ定義を拡張します。
現在のシークレットを削除して新しいものを作成することで、イングレス ゲートウェイの資格情報を更新します。 サーバーは 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
次のマニフェストを使用してゲートウェイ定義を更新し、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
Azure Kubernetes Service