5.10. 令牌身份验证
5.10.1. 云供应商上的 Operator 的令牌身份验证
许多云提供商可以通过使用提供短期、有限权限安全凭据的帐户令牌来启用身份验证。
OpenShift Container Platform 包含 Cloud Credential Operator (CCO),用于将云供应商凭证作为自定义资源定义(CRD)进行管理。CredentialsRequest
自定义资源(CR)的 CCO 同步,允许 OpenShift Container Platform 组件使用所需的特定权限请求云供应商凭证。
在以前的版本中,在 CCO 处于 手动模式 的集群中,由 Operator Lifecycle Manager (OLM)管理的 Operator 通常会在 OperatorHub 中提供详细说明,供用户手动置备任何所需的云凭证。
从 OpenShift Container Platform 4.14 开始,CCO 可以在启用的集群中检测它,以便在某些云供应商中使用短期凭证。然后,它可以半自动置备某些凭证,只要 Operator 作者启用了其 Operator 来支持更新的 CCO。
5.10.2. 使用 AWS STS 的 OLM 管理的 Operator 基于 CCO 的工作流
当在 AWS 上运行的 OpenShift Container Platform 集群处于 Security Token Service (STS) 模式时,这意味着集群会利用 AWS 和 OpenShift Container Platform 的功能在应用程序级别使用 IAM 角色。STS 使应用程序能够提供可假定 IAM 角色的 JSON Web Token (JWT)。
JWT 包含用于 sts:AssumeRoleWithWebIdentity
IAM 操作的 Amazon 资源名称 (ARN),以允许服务帐户临时获得权限。JWT 包含 AWS IAM 可验证的 ProjectedServiceAccountToken
的签名密钥。服务帐户令牌本身(已签名)用作假定 AWS 角色所需的 JWT。
Cloud Credential Operator (CCO) 是在云供应商上运行的 OpenShift Container Platform 集群中默认安装的集群 Operator。对于 STS,CCO 提供以下功能:
- 检测它是否在启用了 STS 的集群中运行的
-
检查
CredentialsRequest
对象是否有字段,它们提供授予 Operator 对 AWS 资源的访问权限所需的信息
即使处于手动模式,CCO 也会执行此检测。正确配置后,CCO 将带有所需访问信息的 Secret
对象项目到 Operator 命名空间中。
从 OpenShift Container Platform 4.14 开始,CCO 可以通过扩大使用 CredentialsRequest
对象来自动处理此任务,该对象可请求创建包含 STS 工作流所需的信息的 Secret
。通过 Web 控制台或 CLI 安装 Operator 时,用户可以提供角色 ARN。
不建议使用具有自动批准更新的订阅,因为在更新前可能会进行权限更改。具有手动批准更新的订阅可确保管理员有机会验证后续版本的权限,执行任何必要的步骤,然后进行更新。
作为 Operator 作者准备一个 Operator 以用于 OpenShift Container Platform 4.14 或更高版本中更新的 CCO,您应该指示用户并添加代码来处理之前 CCO 版本的比较,除了处理 STS 令牌身份验证外(如果您的 Operator 还没有启用 STS)。推荐的方法是为 CredentialsRequest
对象提供正确填充的 STS 相关字段,并让 CCO 为您的 Secret 创建 Secret
。
如果您计划支持早于版本 4.14 的 OpenShift Container Platform 集群,请考虑为用户提供有关如何使用 CCO 实用程序(ccoctl
)手动创建带有 STS 额外信息的 secret 的说明。早期 CCO 版本不知道集群中的 STS 模式,且无法为您创建 secret。
您的代码应检查永远不会出现的 secret,并警告用户以遵循您提供的回退指令。如需更多信息,请参阅"Alternative method"子部分。
5.10.2.1. 启用 Operator 以支持使用 AWS STS 的基于 CCO 的工作流
作为 Operator 作者设计在 Operator Lifecycle Manager (OLM)上运行的项目,您可以通过自定义项目来支持 Cloud Credential Operator (CCO),使 Operator 能够对启用了 STS 的 OpenShift Container Platform 集群上的 AWS 进行身份验证。
使用此方法,Operator 负责创建 CredentialsRequest
对象并读取生成的 Secret
对象,并需要 RBAC 权限。
默认情况下,与 Operator 部署相关的 pod 会挂载 serviceAccountToken
卷,以便在生成的 Secret
对象中引用服务帐户令牌。
先决条件
- OpenShift Container Platform 4.14 或更高版本
- 处于 STS 模式的集群
- 基于 OLM 的 Operator 项目
流程
更新 Operator 项目的
ClusterServiceVersion
(CSV)对象:确保 Operator 有 RBAC 权限来创建
CredentialsRequests
对象:例 5.16.
clusterPermissions
列表示例# ... install: spec: clusterPermissions: - rules: - apiGroups: - "cloudcredential.openshift.io" resources: - credentialsrequests verbs: - create - delete - get - list - patch - update - watch
添加以下注解来声明对使用 AWS STS 的基于 CCO 工作流的方法的支持:
# ... metadata: annotations: features.operators.openshift.io/token-auth-aws: "true"
更新 Operator 项目代码:
从 pod 上由
Subscription
对象设置的环境变量获取角色 ARN。例如:// Get ENV var roleARN := os.Getenv("ROLEARN") setupLog.Info("getting role ARN", "role ARN = ", roleARN) webIdentityTokenPath := "/var/run/secrets/openshift/serviceaccount/token"
确保具有
CredentialsRequest
对象已准备好修补并应用。例如:例 5.17.
CredentialsRequest
对象创建示例import ( minterv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var in = minterv1.AWSProviderSpec{ StatementEntries: []minterv1.StatementEntry{ { Action: []string{ "s3:*", }, Effect: "Allow", Resource: "arn:aws:s3:*:*:*", }, }, STSIAMRoleARN: "<role_arn>", } var codec = minterv1.Codec var ProviderSpec, _ = codec.EncodeProviderSpec(in.DeepCopyObject()) const ( name = "<credential_request_name>" namespace = "<namespace_name>" ) var CredentialsRequestTemplate = &minterv1.CredentialsRequest{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "openshift-cloud-credential-operator", }, Spec: minterv1.CredentialsRequestSpec{ ProviderSpec: ProviderSpec, SecretRef: corev1.ObjectReference{ Name: "<secret_name>", Namespace: namespace, }, ServiceAccountNames: []string{ "<service_account_name>", }, CloudTokenPath: "", }, }
另外,如果您从 YAML 表单的
CredentialsRequest
对象开始(例如,作为 Operator 项目代码的一部分),您可以以不同的方式处理它:例 5.18. 以 YAML 格式创建
CredentialsRequest
对象示例// CredentialsRequest is a struct that represents a request for credentials type CredentialsRequest struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` Metadata struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` } `yaml:"metadata"` Spec struct { SecretRef struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` } `yaml:"secretRef"` ProviderSpec struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` StatementEntries []struct { Effect string `yaml:"effect"` Action []string `yaml:"action"` Resource string `yaml:"resource"` } `yaml:"statementEntries"` STSIAMRoleARN string `yaml:"stsIAMRoleARN"` } `yaml:"providerSpec"` // added new field CloudTokenPath string `yaml:"cloudTokenPath"` } `yaml:"spec"` } // ConsumeCredsRequestAddingTokenInfo is a function that takes a YAML filename and two strings as arguments // It unmarshals the YAML file to a CredentialsRequest object and adds the token information. func ConsumeCredsRequestAddingTokenInfo(fileName, tokenString, tokenPath string) (*CredentialsRequest, error) { // open a file containing YAML form of a CredentialsRequest file, err := os.Open(fileName) if err != nil { return nil, err } defer file.Close() // create a new CredentialsRequest object cr := &CredentialsRequest{} // decode the yaml file to the object decoder := yaml.NewDecoder(file) err = decoder.Decode(cr) if err != nil { return nil, err } // assign the string to the existing field in the object cr.Spec.CloudTokenPath = tokenPath // return the modified object return cr, nil }
注意目前不支持在 Operator 捆绑包中添加
CredentialsRequest
对象。在凭证请求中添加角色 ARN 和 Web 身份令牌路径,并在 Operator 初始化过程中应用它:
例 5.19. 在 Operator 初始化过程中应用
CredentialsRequest
对象示例// apply CredentialsRequest on install credReq := credreq.CredentialsRequestTemplate credReq.Spec.CloudTokenPath = webIdentityTokenPath c := mgr.GetClient() if err := c.Create(context.TODO(), credReq); err != nil { if !errors.IsAlreadyExists(err) { setupLog.Error(err, "unable to create CredRequest") os.Exit(1) } }
确保 Operator 可以等待
Secret
对象从 CCO 显示,如下例所示,以及您在 Operator 中协调的其他项目:例 5.20. 等待
Secret
对象示例// WaitForSecret is a function that takes a Kubernetes client, a namespace, and a v1 "k8s.io/api/core/v1" name as arguments // It waits until the secret object with the given name exists in the given namespace // It returns the secret object or an error if the timeout is exceeded func WaitForSecret(client kubernetes.Interface, namespace, name string) (*v1.Secret, error) { // set a timeout of 10 minutes timeout := time.After(10 * time.Minute) 1 // set a polling interval of 10 seconds ticker := time.NewTicker(10 * time.Second) // loop until the timeout or the secret is found for { select { case <-timeout: // timeout is exceeded, return an error return nil, fmt.Errorf("timed out waiting for secret %s in namespace %s", name, namespace) // add to this error with a pointer to instructions for following a manual path to a Secret that will work on STS case <-ticker.C: // polling interval is reached, try to get the secret secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // secret does not exist yet, continue waiting continue } else { // some other error occurred, return it return nil, err } } else { // secret is found, return it return secret, nil } } } }
- 1
timeout
值基于 CCO 检测添加的CredentialsRequest
对象并生成Secret
对象的速度。您可能会考虑降低时间或为集群管理员创建自定义反馈,这可能会导致 Operator 尚未访问云资源的原因。
通过从凭证请求中读取 CCO 创建的 secret 并设置 AWS 配置,并创建包含该 secret 数据的 AWS 配置文件:
例 5.21. AWS 配置创建示例
func SharedCredentialsFileFromSecret(secret *corev1.Secret) (string, error) { var data []byte switch { case len(secret.Data["credentials"]) > 0: data = secret.Data["credentials"] default: return "", errors.New("invalid secret for aws credentials") } f, err := ioutil.TempFile("", "aws-shared-credentials") if err != nil { return "", errors.Wrap(err, "failed to create file for shared credentials") } defer f.Close() if _, err := f.Write(data); err != nil { return "", errors.Wrapf(err, "failed to write credentials to %s", f.Name()) } return f.Name(), nil }
重要secret 被假定为存在,但在使用此 secret 时,您的 Operator 代码应等待和重试,以提供 CCO 创建 secret 的时间。
另外,等待周期最终应该超时,并警告用户 OpenShift Container Platform 集群版本,因此 CCO 可能会是一个较早的版本,它不支持使用 STS 检测的
CredentialsRequest
对象工作流。在这种情况下,指示用户必须使用其他方法添加 secret。配置 AWS SDK 会话,例如:
例 5.22. AWS SDK 会话配置示例
sharedCredentialsFile, err := SharedCredentialsFileFromSecret(secret) if err != nil { // handle error } options := session.Options{ SharedConfigState: session.SharedConfigEnable, SharedConfigFiles: []string{sharedCredentialsFile}, }
5.10.2.2. 角色规格
Operator 描述应包含在安装前创建的角色的具体信息,最好是管理员可以运行的脚本的形式。例如:
例 5.23. 角色创建脚本示例
#!/bin/bash set -x AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) OIDC_PROVIDER=$(oc get authentication cluster -ojson | jq -r .spec.serviceAccountIssuer | sed -e "s/^https:\/\///") NAMESPACE=my-namespace SERVICE_ACCOUNT_NAME="my-service-account" POLICY_ARN_STRINGS="arn:aws:iam::aws:policy/AmazonS3FullAccess" read -r -d '' TRUST_RELATIONSHIP <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "${OIDC_PROVIDER}:sub": "system:serviceaccount:${NAMESPACE}:${SERVICE_ACCOUNT_NAME}" } } } ] } EOF echo "${TRUST_RELATIONSHIP}" > trust.json aws iam create-role --role-name "$SERVICE_ACCOUNT_NAME" --assume-role-policy-document file://trust.json --description "role for demo" while IFS= read -r POLICY_ARN; do echo -n "Attaching $POLICY_ARN ... " aws iam attach-role-policy \ --role-name "$SERVICE_ACCOUNT_NAME" \ --policy-arn "${POLICY_ARN}" echo "ok." done <<< "$POLICY_ARN_STRINGS"
5.10.2.3. 故障排除
5.10.2.3.1. 身份验证失败
如果身份验证不成功,请确保您可以使用提供给 Operator 的令牌假设具有 Web 身份的角色。
流程
从 pod 中提取令牌:
$ oc exec operator-pod -n <namespace_name> \ -- cat /var/run/secrets/openshift/serviceaccount/token
从 pod 中提取角色 ARN:
$ oc exec operator-pod -n <namespace_name> \ -- cat /<path>/<to>/<secret_name> 1
- 1
- 不要将 root 用于路径。
尝试使用 Web 身份令牌假定角色:
$ aws sts assume-role-with-web-identity \ --role-arn $ROLEARN \ --role-session-name <session_name> \ --web-identity-token $TOKEN
5.10.2.3.2. Secret 无法正确挂载
以非 root 用户身份运行的 Pod 无法写入默认存在 AWS 共享凭证文件的 /root
目录。如果 secret 没有正确挂载到 AWS 凭证文件路径,请考虑将 secret 挂载到不同的位置,并在 AWS SDK 中启用共享凭证文件选项。
5.10.2.4. 其它方法
作为 Operator 作者的替代方法,您可以指定用户在安装 Operator 前负责为 Cloud Credential Operator (CCO) 创建 CredentialsRequest
对象。
Operator 指令必须向用户指示以下内容:
-
通过在说明中内联提供 YAML 或将用户指向下载位置,提供
CredentialsRequest
对象的 YAML 版本 -
指示用户创建
CredentialsRequest
对象
在 OpenShift Container Platform 4.14 及更高版本中,当 CredentialsRequest
对象出现在添加了适当 STS 信息的集群上后,Operator 可以读取 CCO 生成的 Secret
或挂载它,并在集群服务版本(CSV)中定义挂载。
对于早期版本的 OpenShift Container Platform,Operator 指令还必须向用户指示以下内容:
-
使用 CCO 实用程序(
ccoctl
)从CredentialsRequest
对象生成Secret
YAML 对象 -
将
Secret
对象应用到适当的命名空间中的集群
Operator 仍然必须能够使用生成的 secret 与云 API 通信。因为在这种情况下,用户会在安装 Operator 前创建 secret,所以 Operator 可以执行以下操作之一:
-
在 CSV 中的
Deployment
对象中定义显式挂载 -
从 API 服务器以编程方式读取
Secret
对象,如推荐的"启用 Operator 以支持使用 AWS STS 的基于 CCO 的工作流"方法所示
5.10.3. 使用 Microsoft Entra Workload ID 为 OLM 管理的 Operator 基于 CCO 的工作流
当在 Azure 上运行的 OpenShift Container Platform 集群处于 Workload Identity / Federated Identity 模式时,这意味着集群会利用 Azure 和 OpenShift Container Platform 的功能在应用程序级别应用用户分配的管理的身份 或 Microsoft Entra Workload ID 中的 app 注册。
Cloud Credential Operator (CCO) 是在云供应商上运行的 OpenShift Container Platform 集群中默认安装的集群 Operator。从 OpenShift Container Platform 4.14.8 开始,CCO 支持使用 Workload ID 的 OLM 管理的 Operator 工作流。
对于 Workload ID,CCO 提供以下功能:
- 检测在启用了 Workload ID 的集群中运行的时间
-
检查
CredentialsRequest
对象是否有字段,它们提供授予 Operator 对 Azure 资源的访问权限所需的信息
CCO 可以通过扩展 CredentialsRequest
对象来自动处理这个过程,该对象可以请求创建包含 Workload ID 工作流所需的信息的 Secret
。
不建议使用具有自动批准更新的订阅,因为在更新前可能会进行权限更改。具有手动批准更新的订阅可确保管理员有机会验证后续版本的权限,执行任何必要的步骤,然后进行更新。
作为 Operator 作者准备一个 Operator 以用于 OpenShift Container Platform 4.14 及之后的版本中的更新的 CCO,您应该指示用户并添加代码来处理早期 CCO 版本的划分,除了处理 Workload ID token 身份验证(如果您的 Operator 还没有启用)。推荐的方法是使用正确的 Workload ID 相关字段提供 CredentialsRequest
对象,并让 CCO 为您创建 Secret
对象。
如果您计划支持早于版本 4.14 的 OpenShift Container Platform 集群,请考虑为用户提供有关如何使用 CCO 实用程序(ccoctl
)手动创建带有 Workload ID 启用信息的 secret 的说明。早期 CCO 版本不知道集群中的 Workload ID 模式,且无法为您创建 secret。
您的代码应检查永远不会出现的 secret,并警告用户以遵循您提供的回退指令。
使用 Workload ID 进行身份验证需要以下信息:
-
azure_client_id
-
azure_tenant_id
-
azure_region
-
azure_subscription_id
-
azure_federated_token_file
Web 控制台中的 Install Operator 页面允许集群管理员在安装时提供此信息。然后,此信息会作为 Operator pod 上的环境变量传播到 Subscription
对象中。
其他资源
5.10.3.1. 启用 Operator 以支持使用 Microsoft Entra Workload ID 的基于 CCO 的工作流
作为 Operator 作者设计在 Operator Lifecycle Manager (OLM) 上运行的项目,您可以通过自定义项目来支持 Cloud Credential Operator (CCO),使 Operator 能够对启用了 Microsoft Entra Workload ID 的 OpenShift Container Platform 集群进行身份验证。
使用此方法,Operator 负责创建 CredentialsRequest
对象并读取生成的 Secret
对象,并需要 RBAC 权限。
默认情况下,与 Operator 部署相关的 pod 会挂载 serviceAccountToken
卷,以便在生成的 Secret
对象中引用服务帐户令牌。
先决条件
- OpenShift Container Platform 4.14 或更高版本
- 处于 Workload ID 模式的集群
- 基于 OLM 的 Operator 项目
流程
更新 Operator 项目的
ClusterServiceVersion
(CSV)对象:确保 Operator 有 RBAC 权限来创建
CredentialsRequests
对象:例 5.24.
clusterPermissions
列表示例# ... install: spec: clusterPermissions: - rules: - apiGroups: - "cloudcredential.openshift.io" resources: - credentialsrequests verbs: - create - delete - get - list - patch - update - watch
添加以下注解来声明对使用 Workload ID 的基于 CCO 工作流的方法的支持:
# ... metadata: annotations: features.operators.openshift.io/token-auth-azure: "true"
更新 Operator 项目代码:
从由
Subscription
对象设置的环境变量中获取客户端 ID、租户 ID 和订阅 ID。例如:// Get ENV var clientID := os.Getenv("CLIENTID") tenantID := os.Getenv("TENANTID") subscriptionID := os.Getenv("SUBSCRIPTIONID") azureFederatedTokenFile := "/var/run/secrets/openshift/serviceaccount/token"
确保具有
CredentialsRequest
对象已准备好修补并应用。注意目前不支持在 Operator 捆绑包中添加
CredentialsRequest
对象。在凭证请求中添加 Azure 凭证信息和 Web 身份令牌路径,并在 Operator 初始化过程中应用它:
例 5.25. 在 Operator 初始化过程中应用
CredentialsRequest
对象示例// apply CredentialsRequest on install credReqTemplate.Spec.AzureProviderSpec.AzureClientID = clientID credReqTemplate.Spec.AzureProviderSpec.AzureTenantID = tenantID credReqTemplate.Spec.AzureProviderSpec.AzureRegion = "centralus" credReqTemplate.Spec.AzureProviderSpec.AzureSubscriptionID = subscriptionID credReqTemplate.CloudTokenPath = azureFederatedTokenFile c := mgr.GetClient() if err := c.Create(context.TODO(), credReq); err != nil { if !errors.IsAlreadyExists(err) { setupLog.Error(err, "unable to create CredRequest") os.Exit(1) } }
确保 Operator 可以等待
Secret
对象从 CCO 显示,如下例所示,以及您在 Operator 中协调的其他项目:例 5.26. 等待
Secret
对象示例// WaitForSecret is a function that takes a Kubernetes client, a namespace, and a v1 "k8s.io/api/core/v1" name as arguments // It waits until the secret object with the given name exists in the given namespace // It returns the secret object or an error if the timeout is exceeded func WaitForSecret(client kubernetes.Interface, namespace, name string) (*v1.Secret, error) { // set a timeout of 10 minutes timeout := time.After(10 * time.Minute) 1 // set a polling interval of 10 seconds ticker := time.NewTicker(10 * time.Second) // loop until the timeout or the secret is found for { select { case <-timeout: // timeout is exceeded, return an error return nil, fmt.Errorf("timed out waiting for secret %s in namespace %s", name, namespace) // add to this error with a pointer to instructions for following a manual path to a Secret that will work on STS case <-ticker.C: // polling interval is reached, try to get the secret secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // secret does not exist yet, continue waiting continue } else { // some other error occurred, return it return nil, err } } else { // secret is found, return it return secret, nil } } } }
- 1
timeout
值基于 CCO 检测添加的CredentialsRequest
对象并生成Secret
对象的速度。您可能会考虑降低时间或为集群管理员创建自定义反馈,这可能会导致 Operator 尚未访问云资源的原因。
-
从
CredentialsRequest
对象读取 CCO 创建的 secret,以与 Azure 进行身份验证并接收必要的凭证。
5.10.4. 使用 GCP Workload Identity 的 OLM 管理的 Operator 基于 CCO 的工作流
当 Google Cloud Platform (GCP) 上运行的 OpenShift Container Platform 集群位于 GCP Workload Identity / Federated Identity 模式中时,这意味着集群使用 Google Cloud Platform (GCP) 和 OpenShift Container Platform 的功能,以便在应用程序级别的 GCP Workload Identity 中应用权限。
Cloud Credential Operator (CCO) 是在云供应商上运行的 OpenShift Container Platform 集群中默认安装的集群 Operator。从 OpenShift Container Platform 4.17 开始,CCO 支持使用 GCP Workload Identity 的 OLM 管理的 Operator 的工作流。
对于 GCP Workload Identity,CCO 提供以下功能:
- 检测它在启用了 GCP Workload Identity 的集群中运行时
-
检查
CredentialsRequest
对象是否有提供授予 Operator 对 GCP 资源访问权限所需的信息的字段
CCO 可以通过扩展使用 CredentialsRequest
对象来半自动化这个过程,这可以请求创建包含 GCP Workload Identity 工作流所需的信息的 Secret
。
不建议使用具有自动批准更新的订阅,因为在更新前可能会进行权限更改。具有手动批准更新的订阅可确保管理员有机会验证后续版本的权限,执行任何必要的步骤,然后进行更新。
作为 Operator 作者准备一个 Operator 以用于 OpenShift Container Platform 4.17 及之后的版本中的更新的 CCO,您应该指示用户并添加代码来处理早期 CCO 版本的划分,除了处理 GCP Workload Identity token 身份验证(如果您的 Operator 还没有启用)。推荐的方法是提供一个 CredentialsRequest
对象,带有正确填充的 GCP Workload Identity 字段,并让 CCO 为您创建 Secret
对象。
如果您计划支持早于 4.17 的 OpenShift Container Platform 集群,请考虑为用户提供有关如何使用 CCO 实用程序(ccoctl
)手动创建带有 GCP Workload Identity-enabling 信息的 secret 的说明。早期 CCO 版本不知道集群中的 GCP Workload Identity 模式,且无法为您创建 secret。
您的代码应检查永远不会出现的 secret,并警告用户以遵循您提供的回退指令。
要通过 Google Cloud Platform Workload Identity 使用简短令牌与 GCP 进行身份验证,Operator 必须提供以下信息:
AUDIENCE
当管理员设置 GCP Workload Identity 时,在 GCP 中创建,
AUDIENCE
值必须是以下格式的 URL://iam.googleapis.com/projects/<project_number>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>
SERVICE_ACCOUNT_EMAIL
SERVICE_ACCOUNT_EMAIL
值是在 Operator 操作过程中模拟的 GCP 服务帐户电子邮件,例如:<service_account_name>@<project_id>.iam.gserviceaccount.com
Web 控制台中的 Install Operator 页面允许集群管理员在安装时提供此信息。然后,此信息会作为 Operator pod 上的环境变量传播到 Subscription
对象中。
其他资源
5.10.4.1. 启用 Operator 以支持使用 GCP Workload Identity 的基于 CCO 的工作流
作为 Operator 作者设计在 Operator Lifecycle Manager (OLM) 上运行的项目,您可以通过自定义项目来支持 Cloud Credential Operator (CCO),使 Operator 能够对启用了 Google Cloud Platform Workload Identity-enabled OpenShift Container Platform 集群进行身份验证。
使用此方法,Operator 负责创建 CredentialsRequest
对象并读取生成的 Secret
对象,并需要 RBAC 权限。
默认情况下,与 Operator 部署相关的 pod 会挂载 serviceAccountToken
卷,以便在生成的 Secret
对象中引用服务帐户令牌。
先决条件
- OpenShift Container Platform 4.17 或更高版本
- 在 GCP Workload Identity / Federated Identity 模式中的集群
- 基于 OLM 的 Operator 项目
流程
更新 Operator 项目的
ClusterServiceVersion
(CSV)对象:确保 CSV 中的 Operator 部署有以下
volumeMounts
和volumes
字段,以便 Operator 可以假定角色具有 Web 身份:例 5.27.
volumeMounts
和volumes
字段示例# ... volumeMounts: - name: bound-sa-token mountPath: /var/run/secrets/openshift/serviceaccount readOnly: true volumes: # This service account token can be used to provide identity outside the cluster. - name: bound-sa-token projected: sources: - serviceAccountToken: path: token audience: openshift
确保 Operator 有 RBAC 权限来创建
CredentialsRequests
对象:例 5.28.
clusterPermissions
列表示例# ... install: spec: clusterPermissions: - rules: - apiGroups: - "cloudcredential.openshift.io" resources: - credentialsrequests verbs: - create - delete - get - list - patch - update - watch
添加以下注解来声明对使用 GCP Workload Identity 的基于 CCO 工作流的方法的支持:
# ... metadata: annotations: features.operators.openshift.io/token-auth-gcp: "true"
更新 Operator 项目代码:
从由订阅配置在 pod 上设置的环境变量中获取
audience
和serviceAccountEmail
值:// Get ENV var audience := os.Getenv("AUDIENCE") serviceAccountEmail := os.Getenv("SERVICE_ACCOUNT_EMAIL") gcpIdentityTokenFile := "/var/run/secrets/openshift/serviceaccount/token"
确保具有
CredentialsRequest
对象已准备好修补并应用。注意目前不支持在 Operator 捆绑包中添加
CredentialsRequest
对象。将 GCP Workload Identity 变量添加到凭证请求中,并在 Operator 初始化过程中应用它:
例 5.29. 在 Operator 初始化过程中应用
CredentialsRequest
对象示例// apply CredentialsRequest on install credReqTemplate.Spec.GCPProviderSpec.Audience = audience credReqTemplate.Spec.GCPProviderSpec.ServiceAccountEmail = serviceAccountEmail credReqTemplate.CloudTokenPath = gcpIdentityTokenFile c := mgr.GetClient() if err := c.Create(context.TODO(), credReq); err != nil { if !errors.IsAlreadyExists(err) { setupLog.Error(err, "unable to create CredRequest") os.Exit(1) } }
确保 Operator 可以等待
Secret
对象从 CCO 显示,如下例所示,以及您在 Operator 中协调的其他项目:例 5.30. 等待
Secret
对象示例// WaitForSecret is a function that takes a Kubernetes client, a namespace, and a v1 "k8s.io/api/core/v1" name as arguments // It waits until the secret object with the given name exists in the given namespace // It returns the secret object or an error if the timeout is exceeded func WaitForSecret(client kubernetes.Interface, namespace, name string) (*v1.Secret, error) { // set a timeout of 10 minutes timeout := time.After(10 * time.Minute) 1 // set a polling interval of 10 seconds ticker := time.NewTicker(10 * time.Second) // loop until the timeout or the secret is found for { select { case <-timeout: // timeout is exceeded, return an error return nil, fmt.Errorf("timed out waiting for secret %s in namespace %s", name, namespace) // add to this error with a pointer to instructions for following a manual path to a Secret that will work case <-ticker.C: // polling interval is reached, try to get the secret secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // secret does not exist yet, continue waiting continue } else { // some other error occurred, return it return nil, err } } else { // secret is found, return it return secret, nil } } } }
- 1
timeout
值基于 CCO 检测添加的CredentialsRequest
对象并生成Secret
对象的速度。您可能会考虑降低时间或为集群管理员创建自定义反馈,这可能会导致 Operator 尚未访问云资源的原因。
从 secret 中读取
service_account.json
字段,并使用它来验证您的 GCP 客户端:service_account_json := secret.StringData["service_account.json"]