10.5. Zero Trust Workload Identity Manager OIDC 联邦
Zero Trust Workload Identity Manager 通过允许 SPIRE 服务器充当 OIDC 供应商来与 OpenID Connect (OIDC) 集成。这可让工作负载从本地 SPIRE 代理请求和接收可验证的 JSON Web Tokens - SPIFFE Verable Identity Documents (JWT-SVID)。然后,外部系统(如云供应商)可以使用 SPIRE 服务器公开的 OIDC 发现端点来检索公钥。
Red Hat OpenShift 的 Zero Trust Workload Identity Manager 只是一个技术预览功能。技术预览功能不受红帽产品服务等级协议(SLA)支持,且功能可能并不完整。红帽不推荐在生产环境中使用它们。这些技术预览功能可以使用户提早试用新的功能,并有机会在开发阶段提供反馈意见。
有关红帽技术预览功能支持范围的更多信息,请参阅以下链接:
验证以下供应商以用于 SPIRE OIDC 联邦:
- Azure Entra ID
- Vault
10.5.1. 关于 Entra ID OpenID Connect 复制链接链接已复制到粘贴板!
Entra ID 是一个基于云的身份和访问管理服务,可集中管理和访问控制。Entra ID 充当标识提供程序,验证用户身份以及向应用发布和 ID 令牌。此令牌包括基本的用户信息,允许应用在无需管理用户的凭据的情况下确认用户的身份。
将 Entra ID OpenID Connect (OIDC) 与 SPIRE 集成,提供具有自动、短期加密身份的工作负载。SPIRE 发布的身份被发送到 Entra ID,以在没有静态 secret 的情况下安全地验证服务。
10.5.1.1. 为受管 OIDC 发现供应商路由配置外部证书 复制链接链接已复制到粘贴板!
受管路由使用外部 Route 证书功能,将 tls.externalCertificate 字段设置为外部管理的传输层安全 (TLS) secret 名称。
先决条件
- 已安装 Zero Trust Workload Identity Manager 0.2.0 或更高版本。
- 您已在集群中部署了 SPIRE Server、SPIRE Agent、SPIFFEE CSI Driver 和 SPIRE OIDC Discovery Provider operands。
- 您已为 Red Hat OpenShift 安装了 cert-manager Operator。如需更多信息,请参阅为 Red Hat OpenShift 卸载 cert-manager Operator。
-
您已创建了使用一个公共可信 CA 服务配置的
ClusterIssuer或Issuer。例如,使用 "Let's Encrypt ACME" 服务的自动证书管理环境 (ACME) 类型Issuer。如需更多信息,请参阅配置 ACME 签发者
流程
使用以下命令,创建一个
Role来提供路由器服务帐户权限来读取引用的 secret:$ oc create role secret-reader \ --verb=get,list,watch \ --resource=secrets \ --resource-name=$TLS_SECRET_NAME \ -n zero-trust-workload-identity-manager使用以下命令,创建一个
RoleBinding资源,将 router 服务帐户与新创建的 Role 资源绑定:$ oc create rolebinding secret-reader-binding \ --role=secret-reader \ --serviceaccount=openshift-ingress:router \ -n zero-trust-workload-identity-manager运行以下命令,配置
SpireOIDCDIscoveryProvider自定义资源(CR)对象来引用上一步中生成的 Secret:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: externalSecretRef: ${TLS_SECRET_NAME} '
验证
在
SpireOIDCDiscoveryProviderCR 中,运行以下命令来检查ManageRouteReady条件是否设置为True:$ oc wait --for=jsonpath='{.status.conditions[?(@.type=="ManagedRouteReady")].status}'=True SpireOIDCDiscoveryProvider/cluster --timeout=120s运行以下命令,验证 OIDC 端点可以通过 HTTPS 安全地访问:
$ curl https://$JWT_ISSUER_ENDPOINT/.well-known/openid-configuration { "issuer": "https://$JWT_ISSUER_ENDPOINT", "jwks_uri": "https://$JWT_ISSUER_ENDPOINT/keys", "authorization_endpoint": "", "response_types_supported": [ "id_token" ], "subject_types_supported": [], "id_token_signing_alg_values_supported": [ "RS256", "ES256", "ES384" ] }%
10.5.1.2. 禁用受管路由 复制链接链接已复制到粘贴板!
如果要完全控制公开 OIDC Discovery Provider 服务的行为,您可以根据您的要求禁用受管路由。
流程
要手动配置 OIDC Discovery 供应商,请运行以下命令将
managedRoute设置为false:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: managedRoute: "false"
10.5.1.3. 在 Microsoft Azure 中使用 Entra ID 复制链接链接已复制到粘贴板!
完成 Entra ID 配置后,您可以设置 Entra ID 来使用 Azure。
先决条件
- 您已将 SPIRE OIDC Discovery Provider Route 配置为从公开的 CA 提供 TLS 证书。
流程
运行以下命令来登录到 Azure:
$ az login运行以下命令,为您的 Azure 订阅和租户配置变量:
$ export SUBSCRIPTION_ID=$(az account list --query "[?isDefault].id" -o tsv)1 $ export TENANT_ID=$(az account list --query "[?isDefault].tenantId" -o tsv)1 $ export LOCATION=centralus1 运行以下命令来定义资源变量名称:
$ export NAME=ztwim1 $ export RESOURCE_GROUP="${NAME}-rg"1 $ export STORAGE_ACCOUNT="${NAME}storage"1 $ export STORAGE_CONTAINER="${NAME}storagecontainer"1 $ export USER_ASSIGNED_IDENTITY_NAME="${NAME}-identity"1 运行以下命令来创建资源组:
$ az group create \ --name "${RESOURCE_GROUP}" \ --location "${LOCATION}"
10.5.1.4. 配置 Azure blob 存储 复制链接链接已复制到粘贴板!
您需要创建一个新的存储帐户来存储内容。
流程
运行以下命令,创建一个用于存储内容的新存储帐户:
$ az storage account create \ --name ${STORAGE_ACCOUNT} \ --resource-group ${RESOURCE_GROUP} \ --location ${LOCATION} \ --encryption-services blob运行以下命令,获取新创建的存储帐户的存储 ID:
$ export STORAGE_ACCOUNT_ID=$(az storage account show -n ${STORAGE_ACCOUNT} -g ${RESOURCE_GROUP} --query id --out tsv)运行以下命令,在新创建的存储帐户中创建存储容器,以提供支持 blob 存储的位置:
$ az storage container create \ --account-name ${STORAGE_ACCOUNT} \ --name ${STORAGE_CONTAINER} \ --auth-mode login
10.5.1.5. 配置 Azure 用户管理的身份 复制链接链接已复制到粘贴板!
创建一个新的用户管理身份,然后获取与 User Managed Identity 关联的相关服务主体的客户端 ID。
流程
创建一个新的 User Managed Identity,然后运行以下命令来获取与 User Managed Identity 关联的相关服务主体的客户端 ID:
$ az identity create \ --name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP}$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)运行以下命令,检索 Azure 用户提供的管理身份的
CLIENT_ID,并将它保存为环境变量:$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)运行以下命令,将角色与 User Managed Identity 关联的 Service Principal 关联:
$ az role assignment create \ --role "Storage Blob Data Contributor" \ --assignee "${IDENTITY_CLIENT_ID}" \ --scope ${STORAGE_ACCOUNT_ID}
10.5.1.6. 创建演示应用程序 复制链接链接已复制到粘贴板!
演示应用为您提供了查看整个系统是否正常工作的方法。
流程
要创建演示应用程序,请完成以下步骤:
运行以下命令来设置应用程序名称和命名空间:
$ export APP_NAME=workload-app$ export APP_NAMESPACE=demo运行以下命令创建命名空间:
$ oc create namespace $APP_NAMESPACE运行以下命令来创建应用程序 Secret:
$ oc apply -f - << EOF apiVersion: v1 kind: Secret metadata: name: $APP_NAME namespace: $APP_NAMESPACE stringData: AAD_AUTHORITY: https://login.microsoftonline.com/ AZURE_AUDIENCE: "api://AzureADTokenExchange" AZURE_TENANT_ID: "${TENANT_ID}" AZURE_CLIENT_ID: "${IDENTITY_CLIENT_ID}" BLOB_STORE_ACCOUNT: "${STORAGE_ACCOUNT}" BLOB_STORE_CONTAINER: "${STORAGE_CONTAINER}" EOF
10.5.1.7. 部署工作负载应用程序 复制链接链接已复制到粘贴板!
演示应用创建之后。
先决条件
- 演示应用已创建并部署。
流程
要部署应用,请复制提供的整个命令块,并将它直接粘贴到终端中。按 Enter 键。
$ oc apply -f - << EOF apiVersion: v1 kind: ServiceAccount metadata: name: $APP_NAME namespace: $APP_NAMESPACE --- kind: Deployment apiVersion: apps/v1 metadata: name: $APP_NAME namespace: $APP_NAMESPACE spec: selector: matchLabels: app: $APP_NAME template: metadata: labels: app: $APP_NAME deployment: $APP_NAME spec: serviceAccountName: $APP_NAME containers: - name: $APP_NAME image: "registry.redhat.io/ubi9/python-311:latest" command: - /bin/bash - "-c" - | #!/bin/bash pip install spiffe azure-cli cat << EOF > /opt/app-root/src/get-spiffe-token.py #!/opt/app-root/bin/python from spiffe import JwtSource import argparse parser = argparse.ArgumentParser(description='Retrieve SPIFFE Token.') parser.add_argument("-a", "--audience", help="The audience to include in the token", required=True) args = parser.parse_args() with JwtSource() as source: jwt_svid = source.fetch_svid(audience={args.audience}) print(jwt_svid.token) EOF chmod +x /opt/app-root/src/get-spiffe-token.py while true; do sleep 10; done envFrom: - secretRef: name: $APP_NAME env: - name: SPIFFE_ENDPOINT_SOCKET value: unix:///run/spire/sockets/spire-agent.sock securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: false runAsNonRoot: true seccompProfile: type: RuntimeDefault ports: - containerPort: 8080 protocol: TCP volumeMounts: - name: spiffe-workload-api mountPath: /run/spire/sockets readOnly: true volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true EOF
验证
运行以下命令,确保
workload-apppod 成功运行:$ oc get pods -n $APP_NAMESPACE输出示例
NAME READY STATUS RESTARTS AGE workload-app-5f8b9d685b-abcde 1/1 Running 0 60s检索 SPIFFE JWT 令牌 (SVID-JWT):
运行以下命令来动态获取 pod 名称:
$ POD_NAME=$(oc get pods -n $APP_NAMESPACE -l app=$APP_NAME -o jsonpath='{.items[0].metadata.name}')运行以下命令,在 pod 中运行脚本:
$ oc exec -it $POD_NAME -n $APP_NAMESPACE -- \ /opt/app-root/src/get-spiffe-token.py -a "api://AzureADTokenExchange"
10.5.1.8. 使用 SPIFFE 身份联邦配置 Azure 复制链接链接已复制到粘贴板!
您可以使用 SPIFFE 身份联邦配置 Azure,以便为演示应用启用无密码和自动身份验证。
流程
运行以下命令,联邦与工作负载应用程序关联的 User Managed Identity 和 SPIFFE 身份之间的身份:
$ az identity federated-credential create \ --name ${NAME} \ --identity-name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP} \ --issuer https://$JWT_ISSUER_ENDPOINT \ --subject spiffe://$APP_DOMAIN/ns/$APP_NAMESPACE/sa/$APP_NAME \ --audience api://AzureADTokenExchange
10.5.1.9. 验证应用程序工作负载是否可以访问 Azure Blob 存储中的内容 复制链接链接已复制到粘贴板!
您可以检查应用程序工作负载是否可以访问 Azure Blob 存储。
先决条件
- 已创建 Azure Blob 存储。
流程
运行以下命令,从 SPIFFE Workload API 检索 JWT 令牌:
$ oc rsh -n $APP_NAMESPACE deployment/$APP_NAME运行以下命令,创建并导出名为
TOKEN的环境变量:$ export TOKEN=$(/opt/app-root/src/get-spiffe-token.py --audience=$AZURE_AUDIENCE)运行以下命令,登录到 pod 中包含的 Azure CLI:
$ az login --service-principal \ -t ${AZURE_TENANT_ID} \ -u ${AZURE_CLIENT_ID} \ --federated-token ${TOKEN}运行以下命令,使用应用程序工作负载 pod 创建新文件,并将文件上传到 Blob 存储:
$ echo “Hello from OpenShift” > openshift-spire-federated-identities.txt运行以下命令,将文件上传到 Azure Blog Storage:
$ az storage blob upload \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --name openshift-spire-federated-identities.txt \ --file openshift-spire-federated-identities.txt \ --auth-mode login
验证
运行以下命令,确认文件上传成功:
$ az storage blob list \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --auth-mode login \ -o table
10.5.2. 关于 Vault OpenID Connect 复制链接链接已复制到粘贴板!
带有 SPIRE 的 vault OpenID Connect (OIDC)会创建一个安全验证方法,其中 Vault 使用 SPIRE 作为可信 OIDC 供应商。工作负载从其本地 SPIRE 代理请求 JWT-SVID,该代理具有唯一的 SPIFFE ID。然后,工作负载向 Vault 提供此令牌,Vault 根据 SPIRE 服务器上的公钥进行验证。如果满足所有条件,Vault 会向工作负载发出简短的 Vault 令牌,可供工作负载用于访问 secret,并在 Vault 中执行操作。
10.5.2.1. 安装 Vault 复制链接链接已复制到粘贴板!
在 Vault 用作 OIDC 之前,您需要安装 Vault。
先决条件
- 配置路由。如需更多信息,请参阅路由配置
- 已安装 Helm。
- 用于轻松读取 Vault API 输出的命令行 JSON 处理器。
- 添加了 HashiCorp Helm 仓库。
流程
创建
vault-helm-value.yaml文件。global: enabled: true openshift: true1 tlsDisable: true2 injector: enabled: false server: ui: enabled: true image: repository: docker.io/hashicorp/vault tag: "1.19.0" dataStorage: enabled: true3 size: 1Gi standalone: enabled: true4 config: | listener "tcp" { tls_disable = 15 address = "[::]:8200" cluster_address = "[::]:8201" } storage "file" { path = "/vault/data" } extraEnvironmentVars: {}运行
helm install命令:$ helm install vault hashicorp/vault \ --create-namespace -n vault \ --values ./vault-helm-value.yaml运行以下命令公开 Vault 服务:
$ oc expose service vault -n vault将
VAULT_ADDR环境变量设置为从新路由检索主机名,然后运行以下命令来导出它:$ export VAULT_ADDR="http://$(oc get route vault -n vault -o jsonpath='{.spec.host}')"注意带有
http://前缀,因为 TLS 被禁用。
验证
要确保您的 Vault 实例正在运行,请运行以下命令:
$ curl -s $VAULT_ADDR/v1/sys/health | jq输出示例
{ "initialized": true, "sealed": true, "standby": true, "performance_standby": false, "replication_performance_mode": "disabled", "replication_dr_mode": "disabled", "server_time_utc": 1663786574, "version": "1.19.0", "cluster_name": "vault-cluster-a1b2c3d4", "cluster_id": "5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b" }
10.5.2.2. 初始化和取消密封 Vault 复制链接链接已复制到粘贴板!
新安装的 Vault 已密封。这意味着主加密密钥(保护所有其他加密密钥)不会在启动时加载到服务器内存。您需要初始化 Vault 来取消它。
初始化 Vault 服务器的步骤包括:
- 初始化和取消密封 Vault
- 启用键值(KV) secret 引擎并存储测试 secret
- 使用 SPIRE 配置 JSON Web 令牌(JWT)身份验证
- 部署演示应用程序
- 验证并检索 secret
先决条件
- 确保 Vault 正在运行。
- 确保 Vault 没有初始化。您只能初始化 Vault 服务器一次。
流程
运行以下命令,打开一个远程的到
vaultpod 的 shell :$ oc rsh -n vault statefulset/vault运行以下命令,初始化 Vault 以获取您的非密钥和 root 令牌:
$ vault operator init -key-shares=1 -key-threshold=1 -format=json运行以下命令,导出您从之前命令接收的未密封密钥和 root 令牌:
$ export UNSEAL_KEY=<Your-Unseal-Key>$ export ROOT_TOKEN=<Your-Root-Token>运行以下命令,使用您的非密钥取消密封 Vault:
$ vault operator unseal -format=json $UNSEAL_KEY-
通过输入
exit来退出 pod。
验证
要验证 Vault pod 是否已就绪,请运行以下命令:
$ oc get pod -n vault输出示例
NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 65d
10.5.2.3. 启用键值 secret 引擎并存储测试 secret 复制链接链接已复制到粘贴板!
您可以启用键值 secret 引擎来建立一个安全的集中式位置,以管理凭证。
先决条件
- 确保已初始化并取消了 Vault。
流程
运行以下命令,在
Vaultpod 中打开另一个 shell 会话:$ oc rsh -n vault statefulset/vault在此新会话中再次导出您的 root 令牌,并运行以下命令来登录:
$ export ROOT_TOKEN=<Your-Root-Token>$ vault login "${ROOT_TOKEN}"运行以下命令,在
secret/路径中启用 KV secret 引擎并创建测试 secret:$ export NAME=ztwim$ vault secrets enable -path=secret kv$ vault kv put secret/$NAME version=v0.1.0
验证
要验证 secret 是否已正确存储,请运行以下命令:
$ vault kv get secret/$NAME
10.5.2.4. 使用 SPIRE 配置 JSON Web 令牌身份验证 复制链接链接已复制到粘贴板!
您需要设置 JSON Web Token (JWT)身份验证,以便应用程序可以使用 SPIFFE 身份安全地登录到 Vault。
先决条件
- 确保已初始化并取消了 Vault。
- 确保测试 secret 存储在键值 secret 引擎中。
流程
在本地机器上,运行以下命令来检索 SPIRE 证书颁发机构(CA)捆绑包并将其保存到文件中:
$ oc get cm -n zero-trust-workload-identity-manager spire-bundle -o jsonpath='{ .data.bundle\.crt }' > oidc_provider_ca.pem返回到 Vault pod shell,通过运行以下命令创建一个临时文件,并将
oidc_provider_ca.pem的内容粘贴到其中:$ cat << EOF > /tmp/oidc_provider_ca.pem -----BEGIN CERTIFICATE----- <Paste-Your-Certificate-Content-Here> -----END CERTIFICATE----- EOF运行以下命令,为 JWT 配置设置必要的环境变量:
$ export APP_DOMAIN=<Your-App-Domain>$ export JWT_ISSUER_ENDPOINT="oidc-discovery.$APP_DOMAIN"$ export OIDC_URL="https://$JWT_ISSUER_ENDPOINT"$ export OIDC_CA_PEM="$(cat /tmp/oidc_provider_ca.pem)"运行以下命令来定义新环境变量:
$ export ROLE="${NAME}-role"运行以下命令来启用 JWT 验证方法:
$ vault auth enable jwt运行以下命令配置 ODIC 验证方法:
$ vault write auth/jwt/config \ oidc_discovery_url=$OIDC_URL \ oidc_discovery_ca_pem="$OIDC_CA_PEM" \ default_role=$ROLE运行以下命令,创建名为
ztwim-policy的策略:$ export POLICY="${NAME}-policy"运行以下命令,授予之前创建的 secret 的读取访问权限:
$ vault policy write $POLICY -<<EOF path "secret/$NAME" { capabilities = ["read"] } EOF运行以下命令来创建以下环境变量:
$ export APP_NAME=client$ export APP_NAMESPACE=demo$ export AUDIENCE=$APP_NAME运行以下命令,创建一个 JWT 角色,将策略绑定到带有特定 SPIFFE ID 的工作负载:
$ vault write auth/jwt/role/$ROLE -<<EOF { "role_type": "jwt", "user_claim": "sub", "bound_audiences": "$AUDIENCE", "bound_claims_type": "glob", "bound_claims": { "sub": "spiffe://$APP_DOMAIN/ns/$APP_NAMESPACE/sa/$APP_NAME" }, "token_ttl": "24h", "token_policies": "$POLICY" } EOF
10.5.2.5. 部署演示应用程序 复制链接链接已复制到粘贴板!
部署演示应用程序时,您可以创建一个简单的客户端应用程序,它使用其 SPIFFE 身份通过 Vault 进行身份验证。
流程
在本地机器上,运行以下命令为应用程序设置环境变量:
$ export APP_NAME=client$ export APP_NAMESPACE=demo$ export AUDIENCE=$APP_NAME运行以下命令,应用 Kubernetes 清单,为 demo 应用创建命名空间、服务帐户和部署。这个部署挂载 SPIFFE CSI 驱动程序套接字。
$ oc apply -f - <<EOF # ... (paste the full YAML from your provided code here) ... EOF
验证
运行以下命令验证客户端部署是否已就绪:
$ oc get deploy -n $APP_NAMESPACE输出示例
NAME READY UP-TO-DATE AVAILABLE AGE frontend-app 2/2 2 2 120d backend-api 3/3 3 3 120d
10.5.2.6. 进行验证并检索 secret 复制链接链接已复制到粘贴板!
您可以使用演示应用从 SPIFFE Workload API 获取 JWT 令牌,并使用它来登录 Vault 并检索该 secret。
流程
通过在运行的客户端 pod 中运行以下命令来获取 JWT-SVID:
$ oc -n $APP_NAMESPACE exec -it $(oc get pod -o=jsonpath='{.items[*].metadata.name}' -l app=$APP_NAME -n $APP_NAMESPACE) \ -- /opt/spire/bin/spire-agent api fetch jwt \ -socketPath /run/spire/sockets/spire-agent.sock \ -audience $AUDIENCE运行以下命令,从输出复制令牌并将其导出为本地机器上的环境变量:
$ export IDENTITY_TOKEN=<Your-JWT-Token>运行以下命令来定义新环境变量:
$ export ROLE="${NAME}-role"运行以下命令,使用
curl将 JWT 令牌发送到 Vault 登录端点,以获取 Vault 客户端令牌:$ VAULT_TOKEN=$(curl -s --request POST --data '{ "jwt": "'"${IDENTITY_TOKEN}"'", "role": "'"${ROLE}"'"}' "${VAULT_ADDR}"/v1/auth/jwt/login | jq -r '.auth.client_token')
验证
运行以下命令,使用新获取的 Vault 令牌从 KV 存储读取 secret:
$ curl -s -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/secret/$NAME | jq您应该在输出中看到 secret 的内容 (
"version": "v0.1.0"),确认整个工作流成功