在 Azure Kubernetes Service (AKS) 叢集中使用適用於祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者

適用於密碼存放區容器儲存體介面 (CSI) 驅動程式的 Azure Key Vault 提供者可讓您透過 CSI 磁碟區,將 Azure Key Vault 與 Azure Kubernetes Service (AKS) 叢集整合為密碼存放區。

特性

  • 使用 CSI 磁碟區,將祕密、金鑰和憑證掛接到 Pod。
  • 支援 CSI 內嵌磁碟區。
  • 支援將多個祕密存放區物件掛接為單一磁碟區。
  • 透過SecretProviderClass自訂資源定義 (CRD) 支援 Pod 可攜性。
  • 支援 Windows 容器。
  • 與 Kubernetes 祕密同步。
  • 支援自動輪替掛接的內容和同步的 Kubernetes 祕密。

限制

  • 使用 ConfigMap 或 Secret 作為 subPath 磁碟區掛接的容器不會在密碼變換時接收自動更新,這是 Kubernetes 限制。 若要讓變更生效,應用程式必須藉由監看檔案系統中的變更或重新啟動Pod來重載變更的檔案。 如需詳細資訊,請參閱祕密存放區 CSI 驅動程式的已知限制 (英文)。
  • 外掛會在節點資源群組azurekeyvaultsecretsprovider-xxxxx中建立一個名為MC_的託管身份,並自動指派到虛擬機器規模設定集。 您可以使用此受控識別或自己的受控識別來存取金鑰保存庫。 不支援防止建立身分識別。

先決條件

  • 如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶。
  • 請確定您的 Azure CLI 版本是 2.30.0 或更新版本。 如果是較早的版本,請安裝最新版本。
  • 如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶。

  • Terraform 是 1.6 或更新版本。

  • Azure CLI 已安裝並登入。安裝最新版本。

  • 建立 AKS 和 金鑰保存庫 資源的權限。

  • 請用以下指令在 Azure CLI 中設定你的 Azure 訂閱。 使用您的訂用帳戶 ID 來取代 <subscriptionId> 。

    az account set --subscription <subscriptionId>
    

網路

角色

建立 AKS 叢集

建立一個 AKS 叢集,並使用 Azure Key Vault 提供者支援「Secrets Store CSI Driver」。

  1. 建立用於建立 AKS 叢集與 金鑰保存庫 指令的變數。

    export RANDOM_STRING=$(printf '%05d%05d' "$RANDOM" "$RANDOM")
    export KEYVAULT_NAME=myKeyVault${RANDOM_STRING}
    export RESOURCE_GROUP=myResourceGroup
    export CLUSTER_NAME=myAKSCluster
    export LOCATION=eastus2
    

    Azure Key Vault 名稱必須是全域唯一、包含連字號的字母數字,且字元長度為 3 至 24 個字元。 金鑰庫名稱將 KEYVAULT_NAME 變數 myKeyVault 的值 RANDOM_STRING 與變數的 10 字元字串串接起來。

  2. 使用 az group create 命令來建立 Azure 資源群組。

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
  3. 使用 az aks create (部分機器翻譯) 命令搭配 --enable-addons azure-keyvault-secrets-provider 參數,利用適用於祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者功能來建立 AKS 叢集。

    這個 --enable-addons 參數會建立一個由使用者指派的受控身分識別,名為 azurekeyvaultsecretsprovider-xxxx,你可以用來驗證你的金鑰保存庫。 管理身份會儲存在節點資源群組(MC_)中,並自動分配到虛擬機規模集。 您可以使用此受控識別或自己的受控識別來存取金鑰保存庫。 不支援防止建立身分識別。

    az aks create \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --enable-addons azure-keyvault-secrets-provider \
      --generate-ssh-keys
    

    小提示

    如果你想使用 Microsoft Entra 工作負載 ID, az aks create 指令必須包含 --enable-oidc-issuer and --enable-workload-identity 參數。

建立 AKS 叢集

建立一個 main.tf 檔案,並將以下測試過的範例設定複製到裡面。 Azure Terraform GitHub 倉庫負責維護範例。 範例會建立資源群組、AKS 叢集、Azure Key Vault、範例秘密以及所需的角色指派。

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.116"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

provider "azurerm" {
  features {
    resource_group {
      prevent_deletion_if_contains_resources = false
    }
  }
}

data "azurerm_client_config" "current" {}

resource "random_string" "suffix" {
  length  = 6
  upper   = false
  special = false
}

locals {
  location            = "eastus"
  resource_group_name = "rg-aks-kv-csi-demo-${random_string.suffix.result}"
  aks_name            = "aks-kv-csi-demo"
  key_vault_name      = "kvcsidemo${random_string.suffix.result}"
  secret_name         = "ExampleSecret"
}

resource "azurerm_resource_group" "this" {
  name     = local.resource_group_name
  location = local.location
}

resource "azurerm_log_analytics_workspace" "this" {
  name                = "law-aks-kv-csi-demo"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  sku                 = "PerGB2018"
  retention_in_days   = 30
}

resource "azurerm_kubernetes_cluster" "this" {
  name                      = local.aks_name
  location                  = azurerm_resource_group.this.location
  resource_group_name       = azurerm_resource_group.this.name
  dns_prefix                = "akskvcsidemo"
  kubernetes_version        = null
  oidc_issuer_enabled       = true
  workload_identity_enabled = true

  default_node_pool {
    name       = "system"
    node_count = 1
    vm_size    = "Standard_D2s_v3"

    upgrade_settings {
      drain_timeout_in_minutes      = 0
      max_surge                     = "10%"
      node_soak_duration_in_minutes = 0
    }
  }

  identity {
    type = "SystemAssigned"
  }

  key_vault_secrets_provider {
    secret_rotation_enabled = false
  }

  oms_agent {
    log_analytics_workspace_id = azurerm_log_analytics_workspace.this.id
  }

  network_profile {
    network_plugin    = "azure"
    load_balancer_sku = "standard"
  }

  tags = {
    scenario = "aks-keyvault-csi"
  }
}

resource "azurerm_key_vault" "this" {
  name                          = local.key_vault_name
  location                      = azurerm_resource_group.this.location
  resource_group_name           = azurerm_resource_group.this.name
  tenant_id                     = data.azurerm_client_config.current.tenant_id
  sku_name                      = "standard"
  purge_protection_enabled      = false
  soft_delete_retention_days    = 7
  enable_rbac_authorization     = true
  public_network_access_enabled = true
}

resource "azurerm_role_assignment" "current_user_kv_admin" {
  scope                = azurerm_key_vault.this.id
  role_definition_name = "Key Vault Administrator"
  principal_id         = data.azurerm_client_config.current.object_id
}

resource "azurerm_key_vault_secret" "example" {
  name         = local.secret_name
  value        = "HelloFromKeyVault"
  key_vault_id = azurerm_key_vault.this.id

  depends_on = [
    azurerm_role_assignment.current_user_kv_admin
  ]
}

resource "azurerm_role_assignment" "aks_csi_secrets_user" {
  scope                = azurerm_key_vault.this.id
  role_definition_name = "Key Vault Secrets User"
  # This is the managed identity automatically created by the AKS Key Vault CSI addon.
  principal_id = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].object_id
}

output "resource_group_name" {
  value = azurerm_resource_group.this.name
}

output "aks_cluster_name" {
  value = azurerm_kubernetes_cluster.this.name
}

output "key_vault_name" {
  value = azurerm_key_vault.this.name
}

output "key_vault_secret_name" {
  value = azurerm_key_vault_secret.example.name
}

output "tenant_id" {
  value = data.azurerm_client_config.current.tenant_id
}

output "aks_csi_client_id" {
  value = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].client_id
}

output "aks_csi_object_id" {
  value = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].object_id
}
  1. 部署設定。 在 Bash 會話中,執行以下指令來部署資源:

    terraform init
    terraform validate
    terraform plan
    terraform apply
    
  2. 從 Terraform 的輸出值中設定其餘命令會使用的變數:

    export RESOURCE_GROUP=$(terraform output -raw resource_group_name)
    export CLUSTER_NAME=$(terraform output -raw aks_cluster_name)
    export KEYVAULT_NAME=$(terraform output -raw key_vault_name)
    

更新現有的 AKS 叢集

用 Azure Key Vault provider 更新現有 AKS 叢集,以支援 Secrets Store CSI Driver 。

  1. 建立用於指令的變數。 根據需要替換這些值,以更新你現有的 AKS 叢集或 金鑰保存庫。

    例如,如果您使用的是現有的金鑰保存庫,請在不使用 KEYVAULT_NAME 變數的情況下,取代 RANDOM_STRING 變數的值。

    如果你沒有金鑰保險庫,Azure 金鑰庫名稱必須是全域唯一、包含連字號的字母數字,且 3 到 24 個字元。 金鑰庫名稱將 KEYVAULT_NAME 變數 myKeyVault 的值 RANDOM_STRING 與變數的 10 字元字串串接起來。 你可以在本篇文章後面建立金鑰保險庫。

    export RANDOM_STRING=$(printf '%05d%05d' "$RANDOM" "$RANDOM")
    export KEYVAULT_NAME=myKeyVault${RANDOM_STRING}
    export RESOURCE_GROUP=myResourceGroup
    export CLUSTER_NAME=myAKSCluster
    export LOCATION=eastus2
    
  2. 用az aks enable-addons命令更新現有的 AKS 叢集,啟用 Azure Key Vault 提供者以支持 Secrets Store CSI Driver 功能,並啟用azure-keyvault-secrets-provider附加元件。 此附加元件會建立使用者指派的受控識別,以供您用來向金鑰保存庫進行驗證。

    az aks enable-addons \
      --addons azure-keyvault-secrets-provider \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP
    

    啟用 Azure Key Vault 秘密提供者後,AKS 會建立一個名為 azurekeyvaultsecretsprovider-xxxx Managed Identity 的身份,讓你用來驗證你的 Key Vault 身份。 管理身份會儲存在節點資源群組(MC_)中,並自動分配到虛擬機規模集。 您可以使用此受控識別或自己的受控識別來存取金鑰保存庫。 不支援防止建立身分識別。

驗證受控身份與金鑰保存庫提供者的安裝

以下指令使用 $CLUSTER_NAME and $RESOURCE_GROUP 變數。 Azure CLI 工作流程較早定義這些變數,而 Terraform 工作流程則在部署後從輸出設定這些變數。

驗證管理身份

使用下列步驟確認已建立受控身分識別,並指派給叢集的虛擬機器擴展集。

  1. 請用該 az aks show 指令確認管理身份是被建立並指派給叢集的。

    az aks show \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --query addonProfiles
    
    {
      "azureKeyvaultSecretsProvider": {
        "config": {
          "enableSecretRotation": "false",
          "rotationPollInterval": "2m"
        },
        "enabled": true,
        "identity": {
          "clientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
          "objectId": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
          "resourceId": "/subscriptions/<subscriptionID>/resourcegroups/MC_myResourceGroup_myAKSCluster_eastus2/providers/Microsoft.ManagedIdentity/userAssignedIdentities/azurekeyvaultsecretsprovider-myakscluster"
        }
      }
    }
    

    屬性 resourceId 顯示資源群組及身份名稱 azurekeyvaultsecretsprovider-myakscluster。

  2. 確認受控識別已指派給節點資源群組的虛擬機器擴展集。

    NODE_RG=$(az aks show \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --query nodeResourceGroup --output tsv)
    
    VMSS_NAME=$(az vmss list \
      --resource-group $NODE_RG \
      --query [].name --output tsv)
    
    az vmss show --name $VMSS_NAME --resource-group $NODE_RG --query '[id, identity]'
    

    輸出會顯示虛擬機器擴展集Microsoft.Compute/virtualMachineScaleSets資源 ID,以及 userAssignedIdentities 屬性 (具有 azurekeyvaultsecretsprovider-myakscluster 的資源 ID),確認身分識別已指派給虛擬機器擴展集。

驗證適用於祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者安裝

  1. 使用 az aks get-credentials 命令以取得 AKS 叢集認證。

    az aks get-credentials \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP
    
  2. 請使用 kubectl get pods 指令確認安裝完成,此指令會列出 secrets-store-csi-driver 命名空間中所有帶有 secrets-store-provider-azure 和 kube-system 標籤的 pod。

    kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver,secrets-store-provider-azure)' -o wide
    

    -o wide 旗標在輸出中會包含每個 Pod 正在上面執行的節點。

    您的輸出看起來應類似以下範例輸出:

    NAME                                     READY   STATUS    RESTARTS   AGE    NODE
    aks-secrets-store-csi-driver-4vpkj       3/3     Running   2          4m25s  aks-nodepool1-12345678-vmss000002
    aks-secrets-store-csi-driver-ctjq6       3/3     Running   2          4m21s  aks-nodepool1-12345678-vmss000001
    aks-secrets-store-csi-driver-tlvlq       3/3     Running   2          4m24s  aks-nodepool1-12345678-vmss000000
    aks-secrets-store-provider-azure-5p4nb   1/1     Running   0          4m21s  aks-nodepool1-12345678-vmss000000
    aks-secrets-store-provider-azure-6pqmv   1/1     Running   0          4m24s  aks-nodepool1-12345678-vmss000001
    aks-secrets-store-provider-azure-f5qlm   1/1     Running   0          4m25s  aks-nodepool1-12345678-vmss000002
    

建立新的金鑰保險庫

執行 az keyvault create 命令建立一個啟用 Azure RBAC 的新金鑰保險庫。

az keyvault create \
  --name $KEYVAULT_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --enable-rbac-authorization

即使你沒有包含 --enable-rbac-authorization 該參數,建立新的金鑰保險庫時,Azure RBAC 預設會啟用。

欲了解更多關於金鑰保存庫(金鑰保存庫)權限模型和 Azure 角色型存取控制 (RBAC) 的資訊,請參閱 提供金鑰保存庫的金鑰、憑證和祕密的存取權,並使用 Azure 角色型存取控制。

更新現有的金鑰保險庫

執行 az keyvault update 指令來使用 Azure 角色型存取控制 (Azure RBAC) 更新現有的金鑰保險庫。 --enable-rbac-authorization 參數是啟用 Azure RBAC 的必要條件,當你更新已停用 Azure RBAC 的現有金鑰庫時。

az keyvault update \
  --name $KEYVAULT_NAME \
  --resource-group $RESOURCE_GROUP \
  --enable-rbac-authorization

欲了解更多關於金鑰庫權限模型與 Azure RBAC 的資訊,請參閱「提供 Azure 角色基礎存取控制金鑰金鑰、憑證與秘密的存取」

新增角色指派和祕密至金鑰保險庫

  1. 執行 az keyvault show 指令確認金鑰庫是否啟用 Azure RBAC。

    az keyvault show \
      --name $KEYVAULT_NAME \
      --resource-group $RESOURCE_GROUP \
      --query properties.enableRbacAuthorization
    

    輸出應該是 true。

  2. 用指令 az role assignment create 將你的使用者帳號角色指派加入金鑰庫範圍,這樣你才能在下一步新增金鑰庫祕密。

    新增了帶有唯一識別碼的b86a8fe4-44ce-4948-aee5-eccb2c155cd7角色,你可以使用該名稱或唯一識別碼。 使用角色的唯一識別碼是防止角色名稱變更時產生問題的最佳實務。

    KEYVAULT_ID=$(az keyvault show \
      --name $KEYVAULT_NAME \
      --resource-group $RESOURCE_GROUP \
      --query id -o tsv)
    
    MYID=$(az ad signed-in-user show --query id --output tsv)
    
    az role assignment create \
      --assignee-object-id $MYID \
      --role "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" \
      --scope $KEYVAULT_ID \
      --assignee-principal-type User
    

    角色分配可能需要幾分鐘才能生效。 你可以用以下指令驗證角色分配是否已建立:

    az role assignment list \
      --assignee-object-id $MYID \
      --scope $KEYVAULT_ID \
      --query '[].{Role:roleDefinitionName, Scope:scope}' \
      --output table
    
  3. 用指令ExampleSecret在金鑰庫中建立一個純文字秘密az keyvault secret set。

    您的金鑰保存庫可以儲存金鑰、祕密和憑證。 參數 value 利用變 RANDOM_STRING 數為秘密創造唯一值。

    az keyvault secret set \
      --vault-name $KEYVAULT_NAME \
      --name ExampleSecret \
      --value MyAKSExampleSecret${RANDOM_STRING}
    
  4. 用指令 az keyvault secret show 確認秘密是否被加入金鑰保險庫。

    az keyvault secret show --vault-name $KEYVAULT_NAME --name ExampleSecret
    

建立新的金鑰保險庫

先前包含的 Terraform 範例會建立一個啟用 Azure RBAC 的新金鑰庫,指派所需角色,並建立 ExampleSecret。 不需要另外的設定更新。

  1. 從 Terraform 輸出中取得金鑰庫名稱。

    KEYVAULT_NAME=$(terraform output -raw key_vault_name)
    
  2. Verify ExampleSecret 是透過使用指令 az keyvault secret show 加入金鑰庫的。

    az keyvault secret show \
     --vault-name $KEYVAULT_NAME \
     --name ExampleSecret
    

清理資源

如果你要看下一篇文章並需要這些資源,請忽略以下步驟。 否則,如果你已經完成且不打算繼續下一篇文章,建議刪除本文中建立的資源,以避免不必要的費用。

  1. 從本地的 .kube/config 檔案中移除叢集的憑證。

    KUBE_CONTEXT=$(kubectl config current-context)
    kubectl config delete-context $KUBE_CONTEXT
    
  2. 使用MC_指令刪除資源群組及其內所有資源,包括節點資源群組()az group delete中的資源。

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

此 terraform destroy 指令會移除目前 Terraform 設定與狀態檔案中定義的所有資源。 請只從本文所用的工作目錄執行此指令。

Warning

如果你使用現有或生產資源,執行前請仔細檢視執行計畫:

terraform plan -destroy

除非您確定可以安全移除,否則請避免對共用或已匯入的基礎結構執行 terraform destroy。 欲了解更多資訊,請參閱 terraform destroy 指令的 Terraform 文件。

  1. 從本地的 .kube/config 檔案中移除叢集的憑證。

    KUBE_CONTEXT=$(kubectl config current-context)
    kubectl config delete-context $KUBE_CONTEXT
    
  2. 執行以下指令以移除本文中建立的資源:

    terraform destroy
    

後續步驟

在本文中,您已了解如何在 AKS 叢集中使用適用於祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者。 Terraform 樣本授與附加元件身分識別對金鑰保險庫中秘密的存取權。 若要設定 SecretProviderClass 或使用不同的身分識別,請參閱下一篇文章。