//iam.googleapis.com/projects/<project_number>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>
当在Google Cloud Platform (GCP)上运行的OpenShift Container Platform集群处于**GCP工作负载身份/联合身份**模式时,这意味着集群正在利用Google Cloud Platform (GCP)和OpenShift Container Platform的功能,以在应用程序级别应用GCP工作负载身份中的权限。
云凭据运营商(CCO)是在云提供商上运行的OpenShift Container Platform集群中默认安装的集群运营商。从OpenShift Container Platform 4.17开始,CCO支持针对使用GCP工作负载身份的OLM管理的运营商的工作流。
出于GCP工作负载身份的目的,CCO提供以下功能:
检测它是否在启用了GCP工作负载身份的集群上运行
检查CredentialsRequest
对象中是否存在提供授予运营商访问GCP资源所需信息的字段
CCO可以通过扩展使用CredentialsRequest
对象来半自动化此过程,该对象可以请求创建包含GCP工作负载身份工作流所需信息的Secrets
。
不建议使用自动批准更新的订阅,因为在更新之前可能需要进行权限更改。使用手动批准更新的订阅可确保管理员有机会验证更高版本的权限,采取必要的步骤,然后进行更新。 |
作为准备在OpenShift Container Platform 4.17及更高版本中与更新的CCO一起使用的运营商的运营商作者,您应该指示用户并添加代码以处理与早期CCO版本的差异,以及处理GCP工作负载身份令牌身份验证(如果您的运营商尚未启用)。推荐的方法是提供一个包含正确填充的GCP工作负载身份字段的CredentialsRequest
对象,并让CCO为您创建Secret
对象。
如果您计划支持早于4.17版本的OpenShift Container Platform集群,请考虑向用户提供有关如何使用CCO实用程序( 您的代码应检查从未出现的密钥,并警告用户遵循您提供的回退说明。 |
要通过Google Cloud Platform工作负载身份使用短期令牌对GCP进行身份验证,运营商必须提供以下信息:
AUDIENCE
由管理员在设置GCP工作负载身份时在GCP中创建,AUDIENCE
值必须是以下格式的预格式化URL:
//iam.googleapis.com/projects/<project_number>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>
SERVICE_ACCOUNT_EMAIL
SERVICE_ACCOUNT_EMAIL
值是在运营商操作期间被模拟的GCP服务帐户电子邮件,例如:
<service_account_name>@<project_id>.iam.gserviceaccount.com
Web控制台中的**安装运营商**页面允许集群管理员在安装时提供此信息。然后,此信息将作为运营商Pod上的环境变量传播到Subscription
对象。
作为将项目设计为在Operator Lifecycle Manager (OLM)上运行的运营商作者,您可以通过自定义项目以支持云凭据运营商(CCO),从而使您的运营商能够对OpenShift Container Platform集群上的Google Cloud Platform工作负载身份进行身份验证。
使用此方法,运营商负责并需要RBAC权限才能创建CredentialsRequest
对象并读取生成的Secret
对象。
默认情况下,与 Operator 部署相关的 Pod 会挂载一个 |
OpenShift Container Platform 4.17 或更高版本
集群处于**GCP 工作负载身份/联合身份**模式
基于 OLM 的 Operator 项目
更新您的 Operator 项目的ClusterServiceVersion
(CSV) 对象
确保 CSV 中的 Operator 部署具有以下volumeMounts
和volumes
字段,以便 Operator 可以使用 Web 身份承担角色
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 具有创建CredentialsRequests
对象的 RBAC 权限
clusterPermissions
列表示例# ...
install:
spec:
clusterPermissions:
- rules:
- apiGroups:
- "cloudcredential.openshift.io"
resources:
- credentialsrequests
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
添加以下注释以声明支持此基于 CCO 的工作流与 GCP 工作负载身份的方法
# ...
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 包中添加 |
在 Operator 初始化期间,将 GCP 工作负载身份变量添加到凭据请求并应用它
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 可以等待 CCO 中显示Secret
对象,如下例所示,该例与您在 Operator 中协调的其他项目一起调用
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"]