はじめに
こんにちは、ACS事業部の吉川です。
本記事はQiitaのAzure Advent Calendar 2022の2日目の記事です。
さて、先月行われたMicrosoft Igniteで発表されたアップデートの中に、以下のものがありました。
個人的に待ち望んでいたアップデートなので、ここで紹介したいと思います。
従来のAKSにおける問題
Azure上のサーバーからストレージアカウントやKey Vaultなどの他のAzureリソースにアクセスする際、マネージドID を使って認証を行うのが一般的です。
AKS上のPodからもマネージドIDを利用できるのですが、ここに大きな問題があります。
マネージドIDの紐づく範囲がAKSクラスター全体であるため、以下の図に示すように 本来権限を与えたいPod以外からもマネージドIDが利用できてしまいます 。
単一のアプリケーションを動かしているだけのAKSクラスターであればこれでも問題ないでしょうが、複数のアプリケーションを動かすことを考えたときに権限分離ができないのは大きなマイナスです。
この問題を解消するのが Azure AD ワークロード ID です。
仕組みの概要
Azure AD ワークロードIDは、KubernetesのサービスアカウントとマネージドIDを紐づける機構です。
以下のような形で、Kubernetesのサービスアカウントに対しマネージドIDを紐づける ことができるため、1クラスター内のPodそれぞれに別の権限を割り当てることができます。
利用方法
ここからは実際に利用する方法を解説していきます。
公式ドキュメントも存在しますので、合わせてご参照ください。
1. プレビュー機能を有効化する
Azure AD ワークロードIDは現在プレビューの状態です。
これを利用するためにはAzure CLIへの拡張機能のインストール、およびサブスクリプションでの機能フラグ登録が必要です。
# Azure CLIの拡張機能インストール az extension add --name aks-preview # 過去にインストール済みの場合は以下コマンドで最新化する az extension update --name aks-preview # サブスクリプションで機能フラグを登録する az feature register --namespace "Microsoft.ContainerService" --name "EnableWorkloadIdentityPreview" # 登録完了まで数分かかる。以下コマンドの出力結果がRegisteredになればOK。 az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/EnableWorkloadIdentityPreview')].{Name:name,State:properties.state}" # 機能フラグの登録が完了したら以下コマンドを実行する。 az provider register --namespace Microsoft.ContainerService
2. 必要なリソースの作成
必要となるAzure上のリソースを作成します。
AKSクラスターと、マネージドIDでアクセスする先のリソースとしてKey Vaultを作成します。
AKSクラスター作成時に --enable-oidc-issuer --enable-workload-identity
というオプションを付ける必要があります。
# リソース名/リージョンの定義 export RG_NAME="rg-sample" export KEYVAULT_NAME="kvsample" export AKS_NAME="aks-sample" export LOCATION="japaneast" # リソースグループ作成 az group create --location $LOCATION --name $RG_NAME # Key Vault作成 az keyvault create -g $RG_NAME -n $KEYVAULT_NAME -l $LOCATION # AKSクラスター作成 az aks create -g $RG_NAME -n $AKS_NAME -l $LOCATION \ --enable-oidc-issuer --enable-workload-identity \ --node-count 1 # kubectlのインストール sudo az aks install-cli # AKSの認証情報取得 az aks get-credentials -g $RG_NAME -n $AKS_NAME
3. マネージドIDの作成
サービスアカウントと紐づけるマネージドIDを作成します。
MANAGEDID_NAME="mi-sample" az identity create -n $MANAGEDID_NAME -g $RG_NAME -l $LOCATION
作成したマネージドIDがKey Vaultのシークレットにアクセスできるよう、アクセスポリシーの設定も行います。 ここでは、Key Vault内のシークレットに対する全ての操作を許可する設定を入れています。
export USER_ASSIGNED_CLIENT_ID="$(az identity show -g "${RG_NAME}" -n "${MANAGEDID_NAME}" --query 'clientId' -o tsv)" az keyvault set-policy -n "${KEYVAULT_NAME}" --secret-permissions all --spn "${USER_ASSIGNED_CLIENT_ID}"
4. サービスアカウント作成
以下のコマンドでAKS上にサービスアカウントを作成します。
export SERVICE_ACCOUNT_NAME="workload-identity-sa" export SERVICE_ACCOUNT_NAMESPACE="default" cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: annotations: azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}" labels: azure.workload.identity/use: "true" name: "${SERVICE_ACCOUNT_NAME}" namespace: "${SERVICE_ACCOUNT_NAMESPACE}" EOF
5. フェデレーション資格情報の登録
マネージドIDとAKS上のサービスアカウントを紐づける、フェデレーション資格情報の登録を行います。
export AKS_OIDC_ISSUER="$(az aks show -n ${AKS_NAME} -g ${RG_NAME} --query "oidcIssuerProfile.issuerUrl" -o tsv)" az identity federated-credential create --name myfederatedIdentity \ --identity-name "${MANAGEDID_NAME}" \ --resource-group "${RG_NAME}" --issuer "${AKS_OIDC_ISSUER}" \ --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}"
上記のコマンド実行後、ポータルでマネージドIDのリソースを表示すると、以下のように設定が入っていることが確認できます。
これで設定は完了です!
Podから利用してみる
実際にPodを起動して、意図した挙動となるか確認してみましょう。
以下のように、Podにサービスアカウントを割り当てて起動します。
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: test-pod namespace: default spec: serviceAccountName: ${SERVICE_ACCOUNT_NAME} containers: - name: test image: python:3.11 command: ["sleep", "3600"] env: - name: KEY_VAULT_NAME value: ${KEYVAULT_NAME} EOF
Podが起動したら以下コマンドでBashを起動しましょう。
kubectl exec -it test-pod -- /bin/bash
Key Vaultのシークレットを読み書きする以下のPythonスクリプトを動かしてみましょう。 こちらから持ってきたものです。
import os from azure.keyvault.secrets import SecretClient from azure.identity import DefaultAzureCredential keyVaultName = os.environ["KEY_VAULT_NAME"] KVUri = f"https://{keyVaultName}.vault.azure.net" credential = DefaultAzureCredential() client = SecretClient(vault_url=KVUri, credential=credential) secretName = input("Input a name for your secret > ") secretValue = input("Input a value for your secret > ") print(f"Creating a secret in {keyVaultName} called '{secretName}' with the value '{secretValue}' ...") client.set_secret(secretName, secretValue) print(" done.") print(f"Retrieving your secret from {keyVaultName}.") retrieved_secret = client.get_secret(secretName) print(f"Your secret is '{retrieved_secret.value}'.") print(f"Deleting your secret from {keyVaultName} ...") poller = client.begin_delete_secret(secretName) deleted_secret = poller.result() print(" done.")
上記のPythonスクリプトを kv_secrets.py
という名前で作成し、以下コマンドで起動します。
pip install azure-identity azure-keyvault-secrets python kv_secrets.py
対話型でシークレットの名前・値を入力すると以下のように 作成 → 読み出し → 削除 の順に処理が動きます。
サービスアカウントに紐づくマネージドIDに割り当てた権限によって、これらの操作が実現できています。
Incomplete environment configuration. These variables are set: AZURE_CLIENT_ID, AZURE_TENANT_ID Input a name for your secret > test-secret # ← Key Vault内に作成するシークレットの名前を入力する Input a value for your secret > test-value # ← Key Vault内に作成するシークレットの値を入力する Creating a secret in kvsample called 'test-secret' with the value 'test-value' ... done. # ※シークレットの作成が完了したメッセージ Retrieving your secret from kvsample. Your secret is 'test-value'. # ※保存したシークレットの読み出しが完了したメッセージ Deleting your secret from kvsample ... # ※シークレットの削除が完了したメッセージ done.
以下のようにサービスアカウントを割り当てていないPodを起動して同じことを実行しようとすると、権限エラーとなることが確認できます。
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: ng-pod namespace: default spec: containers: - name: test image: python:3.11 command: ["sleep", "3600"] env: - name: KEY_VAULT_NAME value: ${KEYVAULT_NAME} EOF kubectl exec -it ng-pod -- /bin/bash
pip install azure-identity azure-keyvault-secrets python kv_secrets.py
Input a name for your secret > ng-secret Input a value for your secret > ng-value Creating a secret in kvsample called 'ng-secret' with the value 'ng-value' ... Traceback (most recent call last): ~中略~ Inner error: { "code": "AccessDenied" }
本記事冒頭に記載した、1クラスター内のPodそれぞれに別の権限を割り当てる が実現できています!
今回は一例としてKey Vaultにアクセスする例を紹介しましたが、マネージドIDで認証可能なものであれば他のAzureサービスに対しても同様の設定が可能です。
おわりに
ひとつのAKSクラスター内で複数アプリを起動している場合、権限分離したいケースもあるかと思います。
従来でもサービスプリンシパルを別途割り当ててあげれば同様のことは実現できましたが、より取り扱いやすいマネージドIDが利用できるようになったのは便利なアップデートですね。
AKSにおけるAzure ADワークロードIDは現時点ではプレビューの状態です。GAが待ち遠しいですね!