你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用机密存储扩展提取用于在已启用 Azure Arc 的 Kubernetes 群集中进行脱机访问的机密

适用于 Kubernetes 的 Azure Key Vault 机密存储扩展(SSE)会自动将机密从 Azure Key Vault 同步到 已启用 Azure Arc 的 Kubernetes 群集 进行脱机访问。 这意味着,即使以半断开连接状态运行 Kubernetes 群集,也可使用 Azure Key Vault 存储、维护和轮换机密。 同步的机密存储在群集机密存储中,使其可作为 Kubernetes 机密以所有常用方式使用:作为数据卷装载,或作为环境变量公开给 Pod 中的容器。

同步机密是关键业务资产,因此 SSE 通过隔离的命名空间、基于角色的访问控制 (RBAC) 策略和同步控制器的权限有限来保护它们。 为了获得额外的保护,对群集上的 Kubernetes 机密存储进行加密

本文介绍如何将 SSE 安装并配置为已启用 Azure Arc 的 Kubernetes 扩展

提示

建议将 SSE 用于 Azure 云外部的群集,其中与 Azure Key Vault 的连接可能并不完美。 SSE 本质上会在 Kubernetes 机密存储中创建机密的副本。 如果希望避免创建机密的本地副本,并且群集与 Azure Key Vault 完美连接,则可以使用仅限联机的 Azure Key Vault 机密提供程序扩展 在已启用 Arc 的 Kubernetes 群集中进行机密访问。 不建议在同一群集中同时运行 Azure Key Vault 机密提供程序联机扩展和脱机 SSE 组件。

先决条件

  • 已启用 Arc 的群集。 这可以是一个你自行连接的群集(此指南假设使用 K3s 群集,并提供有关如何启用 Arc 的指导),也可以是一个 Microsoft 托管的 Azure Arc 启用的 AKS群集。 群集必须运行 Kubernetes 版本 1.27 或更高版本。
  • 确保满足群集扩展的常规先决条件,包括最新版本的 k8s-extension Azure CLI 扩展。
  • 需要证书管理器才能支持 TLS 进行群集内日志通信。 本指南后面的示例将指导你完成安装。 有关 cert-manager 的详细信息,请参阅 cert-manager.io

安装 Azure CLI 并登录(如果尚未安装):

az login

在开始之前,设置用于配置 Azure 和群集资源的环境变量。 如果已有托管标识、Azure Key Vault 或此处列出的其他资源,请更新环境变量中的名称以反映这些资源。 请注意,KEYVAULT_NAME必须全局唯一;如果此名称已在 Azure 中使用,则密钥保管库创建将失败。

export RESOURCE_GROUP="AzureArcTest"
export CLUSTER_NAME="AzureArcTest1"
export LOCATION="EastUS"
export SUBSCRIPTION="$(az account show --query id --output tsv)"
export AZURE_TENANT_ID="$(az account show -s $SUBSCRIPTION --query tenantId --output tsv)"
export CURRENT_USER="$(az ad signed-in-user show --query userPrincipalName --output tsv)"
export KEYVAULT_NAME="my-UNIQUE-kv-name"
export KEYVAULT_SECRET_NAME="my-secret"
export USER_ASSIGNED_IDENTITY_NAME="my-identity"
export FEDERATED_IDENTITY_CREDENTIAL_NAME="my-credential"
export KUBERNETES_NAMESPACE="my-namespace"
export SERVICE_ACCOUNT_NAME="my-service-account"

如有必要,请创建资源组:

az group create --name ${RESOURCE_GROUP}  --location ${LOCATION}

在群集中激活工作负载联合身份验证

SSE 使用称为工作负载联合身份验证的功能来访问和同步 Azure Key Vault 机密。 本部分介绍如何设置功能。 以下部分将详细介绍如何使用它。

提示

以下步骤基于操作指南,该指南介绍如何使用工作负载联合身份验证配置已启用 Arc 的 Kubernetes。 有关任何其他帮助,请参阅该文档。

如果群集尚未连接到 Azure Arc,请遵循这些步骤。 在这些步骤中,在 connect 命令中启用工作负载联合身份验证:

az connectedk8s connect --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --enable-oidc-issuer

如果群集已连接到 Azure Arc,请使用 update 命令启用工作负载标识:

az connectedk8s update --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --enable-oidc-issuer

现在,配置你的群集以使用新的颁发者 URL (service-account-issuer) 颁发服务帐户令牌,该 URL 使 Microsoft Entra ID 能够找到验证这些令牌所需的公钥。 这些公钥适用于群集自己的服务帐户令牌颁发者,由于之前设置的选项,这些公钥是在此 URL --enable-oidc-issuer 上获取和托管的。

  1. kube-apiserver 配置证书颁发者 URL 字段和权限强制实施。 以下示例适用于 k3s 群集。 群集可能有不同的方法来更改 API 服务器参数:--kube-apiserver-arg="--service-account-issuer=${SERVICE_ACCOUNT_ISSUER}" and --kube-apiserver-arg="--enable-admission-plugins=OwnerReferencesPermissionEnforcement"

    • 获取服务帐户颁发者 URL。

      export SERVICE_ACCOUNT_ISSUER="$(az connectedk8s show --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --query "oidcIssuerProfile.issuerUrl" --output tsv)"
      echo $SERVICE_ACCOUNT_ISSUER
      
    • 在文本编辑器中打开 /etc/rancher/k3s/config.yaml。 默认情况下,K3s 没有配置,因此会打开一个空白文件。

      sudo nano /etc/systemd/system/k3s.service
      
    • 添加以下配置设置,然后保存并关闭 nano

       kube-apiserver-arg:
         - 'service-account-issuer=${SERVICE_ACCOUNT_ISSUER}'
         - 'service-account-max-token-expiration=24h'
      

      注意:必须用 echo $SERVICE_ACCOUNT_ISSUER 上述的输出替换 ${SERVICE_ACCOUNT_ISSUER}nano 不会自动替换变量。

  2. 重启 kube-apiserver。

    sudo systemctl restart k3s
    
  3. (可选)验证服务帐户颁发者是否已正确配置:

    kubectl cluster-info dump | grep service-account-issuer
    

创建一个机密并配置一个标识以访问它

若要访问并同步给定的 Azure Key Vault 机密,SSE 需要访问具有相应 Azure 权限以访问该机密的 Azure 托管标识。 托管标识必须使用之前激活的工作负载标识功能链接到 Kubernetes 服务帐户。 SSE 使用关联的共享 Azure 托管标识,将机密从 Azure 密钥保管库拉取到 Kubernetes 的机密存储库中。 以下各节介绍如何对此进行设置。

创建 Azure Key Vault

创建 Azure Key Vault 并添加机密。 如果已有一个 Azure Key Vault 和机密,可跳过此节。

  1. 创建 Azure 密钥保管库:

    az keyvault create --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --name "${KEYVAULT_NAME}" --enable-rbac-authorization
    
  2. 向自己授予对保管库的“机密管理人员”权限,你便可创建机密:

    az role assignment create --role "Key Vault Secrets Officer" --assignee ${CURRENT_USER} --scope /subscriptions/${SUBSCRIPTION}/resourcegroups/${RESOURCE_GROUP}/providers/Microsoft.KeyVault/vaults/${KEYVAULT_NAME}
    
  3. 创建一个机密并对其进行更新,这样你就有两个版本:

    az keyvault secret set --vault-name "${KEYVAULT_NAME}" --name "${KEYVAULT_SECRET_NAME}" --value 'Hello!'
    az keyvault secret set --vault-name "${KEYVAULT_NAME}" --name "${KEYVAULT_SECRET_NAME}" --value 'Hello2'
    

创建用户分配的托管标识

接下来,创建用户分配的托管标识,并向其授予访问 Azure Key Vault 的权限。 如果已有一个具有对 Azure Key Vault 的“Key Vault 读取者”和“Key Vault 机密用户”权限的托管标识,则可跳过本节。 有关详细信息,请参阅使用 Key Vault 创建用户分配的托管标识 和使用 Azure RBAC 机密、密钥和证书权限

  1. 创建用户分配的托管标识:

    az identity create --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --subscription "${SUBSCRIPTION}"
    
  2. 向标识授予“Key Vault 读取者”和“Key Vault 机密”用户权限。 在这些命令成功之前,可能需要等待一段时间,以便标识创建的复制完成:

    export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)"
    az role assignment create --role "Key Vault Reader" --assignee "${USER_ASSIGNED_CLIENT_ID}" --scope /subscriptions/${SUBSCRIPTION}/resourcegroups/${RESOURCE_GROUP}/providers/Microsoft.KeyVault/vaults/${KEYVAULT_NAME}
    az role assignment create --role "Key Vault Secrets User" --assignee "${USER_ASSIGNED_CLIENT_ID}" --scope /subscriptions/${SUBSCRIPTION}/resourcegroups/${RESOURCE_GROUP}/providers/Microsoft.KeyVault/vaults/${KEYVAULT_NAME}
    

创建联合标识凭据

为需要访问机密的工作负载创建 Kubernetes 服务帐户。 然后,创建联合标识凭据,以在托管标识、OIDC 服务帐户颁发者和 Kubernetes 服务帐户之间建立链接。

  1. 创建一个将与托管标识联合的 Kubernetes 服务帐户。 使用关联的用户分配的托管标识的详细信息,对它进行批注。

    kubectl create ns ${KUBERNETES_NAMESPACE}
    
    cat <<EOF | kubectl apply -f -
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: ${SERVICE_ACCOUNT_NAME}
        namespace: ${KUBERNETES_NAMESPACE}
    EOF
    
  2. 创建联合标识凭据:

    az identity federated-credential create --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} --identity-name ${USER_ASSIGNED_IDENTITY_NAME} --resource-group ${RESOURCE_GROUP} --issuer ${SERVICE_ACCOUNT_ISSUER} --subject system:serviceaccount:${KUBERNETES_NAMESPACE}:${SERVICE_ACCOUNT_NAME} --audience api://AzureADTokenExchange
    

安装 SSE

SSE 以 Azure Arc 扩展的形式提供。 已启用 Azure Arc 的 Kubernetes 群集可通过已启用 Azure Arc 的 Kubernetes 扩展进行扩展。 扩展在已连接的群集上启用 Azure 功能,并为扩展安装和生命周期管理提供 Azure 资源管理器驱动的体验。

如要在群集服务之间针对日志展开安全通信,cert-managertrust-manager 也是必要项,必须在安装 Arc 扩展之前安装。

  1. 安装 cert-manager。

    helm repo add jetstack https://charts.jetstack.io/ --force-update
    helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set crds.enabled=true 
    
  2. 安装 trust-manager。

    helm upgrade trust-manager jetstack/trust-manager --install --namespace cert-manager --wait
    
  3. 使用以下命令,将 SSE 安装到已启用 Arc 的群集:

    az k8s-extension create \
      --cluster-name ${CLUSTER_NAME} \
      --cluster-type connectedClusters \
      --extension-type microsoft.azure.secretstore \
      --resource-group ${RESOURCE_GROUP} \
      --name ssarcextension \
      --scope cluster \
    

    如果需要,可以选择通过添加 --configuration-settings rotationPollIntervalInSeconds=<time_in_seconds> (请参阅 配置参考)来修改默认轮询间隔。

配置 SSE

通过定义 Kubernetes 自定义资源的实例,使用有关 Azure 密钥保管库以及要同步到群集的机密的信息来配置已安装的扩展。 创建两种类型的自定义资源:

  • 用于定义与 Key Vault 的连接的 SecretProviderClass 对象。
  • 要同步的每个机密的 SecretSync 对象。

创建 SecretProviderClass 资源

SecretProviderClass 资源用于定义与 Azure Key Vault 的连接、用于访问保管库的标识、要同步的机密以及要在本地保留的每个机密的版本数。

对于每个要同步的 Azure Key Vault、每个用于访问 Azure Key Vault 的标识以及每个目标 Kubernetes 命名空间,需要单独的 SecretProviderClass

按照以下示例,使用 Key Vault 和机密的适当值创建一个或多个 SecretProviderClass YAML 文件。

cat <<EOF > spc.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: secret-provider-class-name                       # Name of the class; must be unique per Kubernetes namespace
  namespace: ${KUBERNETES_NAMESPACE}                     # Kubernetes namespace to make the secrets accessible in
spec:
  provider: azure
  parameters:
    clientID: "${USER_ASSIGNED_CLIENT_ID}"               # Managed Identity Client ID for accessing the Azure Key Vault with.
    keyvaultName: ${KEYVAULT_NAME}                       # The name of the Azure Key Vault to synchronize secrets from.
    objects: |
      array:
        - |
          objectName: ${KEYVAULT_SECRET_NAME}            # The name of the secret to synchronize.
          objectType: secret
          objectVersionHistory: 2                        # [optional] The number of versions to synchronize, starting from latest.
    tenantID: "${AZURE_TENANT_ID}"                       # The tenant ID of the Key Vault 
EOF

有关其他配置指南,请参阅 SecretProviderClass 参考

创建 SecretSync 对象

需要一个 SecretSync 对象来定义通过 SecretsProviderClass 获取的项在 Kubernetes 中的存储方式。 Kubernetes 机密是键值映射,就像ConfigMaps一样,SecretSync对象告知 SSE 如何将链接SecretsProviderClass中定义的项目映射到 Kubernetes 机密中的密钥。 SSE 将创建一个 Kubernetes 机密,该机密与描述它的 SecretSync 同名。

按照此模板为每个 kubernetes 机密创建一个 SecretSync 对象 YAML 文件。 Kubernetes 命名空间应与该 SecretProviderClass命名空间的命名空间匹配。

cat <<EOF > ss.yaml
apiVersion: secret-sync.x-k8s.io/v1alpha1
kind: SecretSync
metadata:
  name: secret-sync-name                                   # Name of the object; must be unique per Kubernetes namespace
  namespace: ${KUBERNETES_NAMESPACE}                       # Kubernetes namespace
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}              # The Kubernetes service account to be given permissions to access the secret.
  secretProviderClassName: secret-provider-class-name      # The name of the matching SecretProviderClass with the configuration to access the AKV storing this secret
  secretObject:
    type: Opaque
    data:
    - sourcePath: ${KEYVAULT_SECRET_NAME}/0                # Name of the secret in Azure Key Vault with an optional version number (defaults to latest)
      targetKey: ${KEYVAULT_SECRET_NAME}-data-key0         # Target name of the secret in the Kubernetes secret store (must be unique)
    - sourcePath: ${KEYVAULT_SECRET_NAME}/1                # [optional] Next version of the AKV secret. Note that versions of the secret must match the configured objectVersionHistory in the secrets provider class 
      targetKey: ${KEYVAULT_SECRET_NAME}-data-key1         # [optional] Next target name of the secret in the K8s secret store
EOF

提示

在从SecretProviderClass中引用objectVersionHistory< 2 里的机密时,请勿包含“/0”。 最新版本是隐式使用的。

有关其他配置指南,请参阅 SecretSync 参考

应用配置 CR

使用 kubectl apply 命令应用配置自定义资源 (CR):

kubectl apply -f ./spc.yaml
kubectl apply -f ./ss.yaml

SSE 自动查找机密,并开始将它们同步到群集。

监视同步到群集的机密

应用配置后,机密就开始以安装 SSE 时指定的节奏自动同步到群集。

查看已同步的机密

运行以下命令,查看已同步到群集的机密:

# View a list of all secrets in the namespace
kubectl get secrets -n ${KUBERNETES_NAMESPACE}

提示

添加 -o yaml-o json 以更改 kubectl getkubectl describe 命令的输出格式。

查看机密值

若要查看现在存储在 Kubernetes 机密存储中的已同步机密值,请使用以下命令:

kubectl get secret secret-sync-name -n ${KUBERNETES_NAMESPACE} -o jsonpath="{.data.${KEYVAULT_SECRET_NAME}-data-key0}" | base64 -d && echo
kubectl get secret secret-sync-name -n ${KUBERNETES_NAMESPACE} -o jsonpath="{.data.${KEYVAULT_SECRET_NAME}-data-key1}" | base64 -d && echo

疑难解答

有关诊断和解决问题的 帮助,请参阅故障排除指南

移除 SSE

若要移除 SSE 并停止同步机密,请使用 az k8s-extension delete 命令将其卸载:

az k8s-extension delete --name ssarcextension --cluster-name $CLUSTER_NAME  --resource-group $RESOURCE_GROUP  --cluster-type connectedClusters    

卸载扩展不会从群集中删除机密、SecretSync 对象或 CRD。 必须使用 kubectl 直接删除这些对象。

删除 SecretSync CRD 会删除所有 SecretSync 对象,且默认会删除所有拥有的机密,但在以下情况下,机密可能会保留:

  • 你修改了任何机密的所有权。
  • 你更改了群集中的垃圾回收设置,包括设置不同的终结器

在此类情况下,必须使用 kubectl 直接删除机密。

后续步骤