在 AKS 整合 Workload Identity

一步步整合 Azure AD Workload Identity 與 AKS,讓 Pod 透過 managed identity 安全存取 Azure 資源,免管理密鑰。

本指南將說明如何把 Azure AD Workload Identity 整合到 Azure Kubernetes Service (AKS)。透過這個整合,AKS 上的工作負載可以使用 managed identity 安全地存取 Azure 資源,而不需要在應用程式中直接管理密鑰或認證。

Prerequisites

Account Permission

開始之前,請確認以下事項:

  • 你已經以使用者身分登入 Azure CLI。
  • 你登入的帳號具備在 Azure 中建立 user-assigned managed identity 的足夠權限。

Tools

  • Azure CLI 版本 ≥ 2.42.0
  • Helm 3

Managed Cluster

  • 版本 ≥ v1.22 的 Kubernetes 叢集

Components Installation

OIDC

OpenID Connect metadata URL 是 identity provider 的 issuer 位置;Azure AD 會在 token exchange protocol 中用它來驗證 token,接著才以 user-assigned managed identity 的身分簽發 token。

如果你還沒有任何叢集,執行以下指令建立:

az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 1 \
    --enable-oidc-issuer \
    --generate-ssh-keys

如果已有叢集,則用以下指令更新:

az aks update --resource-group myResourceGroup --name myAKSCluster --enable-oidc-issuer

接著取得叢集的 OIDC issuer URL,稍後會用到:

az aks show --resource-group <resource_group> --name <cluster_name> --query "oidcIssuerProfile.issuerUrl" -otsv

Mutating Admission Webhook

它具備以下功能:

  • 將簽署過的 service account token 投射到一個已知的路徑。
  • 依據加上註解的 service account,把與驗證相關的環境變數注入到你的 Pod。

Install by Azure CLI

az aks update --resource-group myResourceGroup --name myAKSCluster --enable-workload-identity

Install by Helm

可以參考這份文件

執行上述指令後,AKS 叢集會建立一個 workload identity controller。

Export Environment Variables

先確認我們目前已經有的東西:

  • OIDC issuer URL
  • AKS 中為 Workload Identity Webhook 建立的資源

進入下一步之前,先匯出以下環境變數:

export RESOURCE_GROUP="azwi-quickstart-$(openssl rand -hex 2)"
export USER_ASSIGNED_IDENTITY_NAME="<your user-assigned managed identity name>"
export SERVICE_ACCOUNT_NAMESPACE="default"
export SERVICE_ACCOUNT_NAME="workload-identity-sa"
export SERVICE_ACCOUNT_ISSUER="<your service account issuer URL>"

Create a User-Assigned Managed Identity and Grant Permissions to Access Your Resources

建立一個將與 service account 綁定的 managed identity:

az identity create --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}"

接著為它指派角色。舉例來說,如果你想讓 Pod 能列出叢集憑證,可以為這個 managed identity 指派 Azure Kubernetes Service Cluster Admin Role

Create a Kubernetes Service Account

建立一個用來綁定 managed identity 的 service account:

export USER_ASSIGNED_IDENTITY_CLIENT_ID="$(az identity show --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --query 'clientId' -otsv)"
export USER_ASSIGNED_IDENTITY_OBJECT_ID="$(az identity show --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --query 'principalId' -otsv)"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: ${USER_ASSIGNED_IDENTITY_CLIENT_ID}
  name: ${SERVICE_ACCOUNT_NAME}
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
EOF

如果你的 managed identity 與 service account 位於不同 tenant,你應該替 service account 加上註解,讓 workload identity manager 能夠辨識它。

Establish Federated Identity Credential Between the Identity and the Service Account Issuer & Subject

把 service account 與 managed identity 綁定:

az identity federated-credential create \
  --name "kubernetes-federated-credential" \
  --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
  --resource-group "${RESOURCE_GROUP}" \
  --issuer "${SERVICE_ACCOUNT_ISSUER}" \
  --subject "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}"

Deploy Workload

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: quick-start
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
  labels:
    azure.workload.identity/use: "true"
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}
  containers:
    - image: mcr.microsoft.com/azure-cli
      name: oidc
      command: ["tail", "-f", "/dev/null"]  # 執行 tail -f /dev/null 讓容器保持運作
      resources:
        requests:
          memory: "256Mi"   # 要求 256 MiB 記憶體
          cpu: "100m"       # 要求 100 millicpu (0.1 CPU core)
        limits:
          memory: "512Mi"   # 限制 512 MiB 記憶體
          cpu: "500m"       # 限制 500 millicpu (0.5 CPU core)
  nodeSelector:
    kubernetes.io/os: linux
EOF

你會看到 AZURE_AUTHORITY_HOSTAZURE_CLIENT_IDAZURE_TENANT_IDAZURE_FEDERATED_TOKEN_FILE 都被注入到 quick-start。各屬性的意義可參考這份文件

kubectl describe pod quick-start

Verification

你可以連進 quick-start 這個 Pod,執行以下指令:

az login --service-principal --tenant $AZURE_TENANT_ID --federated-token "$(cat ${AZURE_FEDERATED_TOKEN_FILE})" -u $AZURE_CLIENT_ID

我們也可以用 init container 在一開始就取得憑證,後續再使用:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: quick-start
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
  labels:
    azure.workload.identity/use: "true"
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}
  initContainers:
    - name: azure-login
      image: mcr.microsoft.com/azure-cli
      command:
        - /bin/sh
        - -c
        # 1. 需要加上跳脫符號,因為環境變數不應由目前的 shell 直接展開
        # 2. 需要確保 kube config 的 ACL,否則會出現權限錯誤
        - |
          az login --service-principal --tenant \$AZURE_TENANT_ID --federated-token "\$(cat \$AZURE_FEDERATED_TOKEN_FILE)" -u \$AZURE_CLIENT_ID && \
          az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --admin --file /custom/.kube/config && \
          addgroup -S myUser && \
          adduser -S myUser -G myUser && \
          chown myUser /custom/.kube/config
      volumeMounts:
        - name: aks-credentials
          mountPath: /custom
      resources:
        requests:
          memory: "256Mi"
          cpu: "200m"
        limits:
          memory: "256Mi"
          cpu: "200m"
  containers:
    - image: my-workload:latest
      name: Workload
      command: ["tail", "-f", "/dev/null"]  # 讓容器保持運作
      volumeMounts:
        - name: aks-credentials
          mountPath: /custom
      env:
        - name: KUBECONFIG
          value: /custom/.kube/config
      resources:
        requests:
          memory: "512Mi"
          cpu: "500m"
        limits:
          memory: "512Mi"
          cpu: "500m"
  volumes:
    - name: aks-credentials
      emptyDir: {}
  nodeSelector:
    kubernetes.io/os: linux
EOF

References