一、操作场景
·凭证难以管理:长期凭证需要人工在多账号间同步、轮换,运维成本高。
·安全风险高:长期凭证一旦泄露即长期有效,且难以追溯到具体使用方。
tcr-assistant-oidc 插件基于 OIDC 身份认证机制,通过 CAM 角色与 STS 动态临时凭证实现跨账号免密拉取镜像,适用于以下典型业务场景:
·多账号镜像统一治理:镜像账号统一存放 TCR 企业版实例,业务账号 TKE 集群跨账号免密拉取。
·临时凭证自动轮转:需要满足合规要求,禁止在集群中长期保存镜像仓库长期凭证的场景。
·按仓库/命名空间精细授权:业务方仅能拉取被授权的镜像仓库或命名空间下的镜像。
·自定义域名拉取:TCR 实例已绑定企业自定义域名(如 docker.mycompany.com),需要通过自定义域名拉取镜像。
与原有 tcr-assistant 插件相比,tcr-assistant-oidc 具备以下增强能力:
| 能力 | tcr-assistant | tcr-assistant-oidc |
| 凭证类型 | 静态长期凭证(用户名+密码) | 动态临时密钥,自动轮转 |
| 跨账号拉取 | 仅支持同账号 | 支持同账号和跨账号 |
注意:说明:
注意:如仅需同账号免密拉取(TKE 集群与 TCR 实例属于同一个腾讯云账号),请参见 同账号免密拉取配置。
二、前提条件
·已创建 TKE 集群(B 账号),且集群版本 ≥ 1.20(Kubernetes 1.20 版本将 ServiceAccountIssuerDiscovery 特性升级为正式可用)。
·已创建 TCR 企业版实例(A 账号),且实例状态为“运行中”。
三、实现原理
跨账号场景下,TKE 集群位于 B 账号(镜像消费方),TCR 实例位于 A 账号(镜像提供方),信任边界从“账号内部”扩展到“跨账号”。端到端调用可抽象为五段协作:① OIDC 身份注入 → ② 跨账号 CAM 授权换取临时凭证 → ③ 换取 docker 登录密码 → ④ Secret 分发 → ⑤ 跨 VPC 免密拉取。
链路解读:
| 编号 | 环节 | 关键动作 |
| ① | OIDC 身份注入 | pod-identity-webhook 将 B 账号 TKE 集群签发的 OIDC JWT 挂载到 Controller Pod,JWT 的 iss 指向 B 集群 |
| ② | 跨账号 CAM 授权 | Controller 携 B 集群的 JWT 跨账号调用 STS;STS 用登记在 A 账号的 B 集群 JWKS 验签,匹配 Role 信任策略中的 federated OIDC Provider(B),返回 A 账号 2h 临时凭证 |
| ③ | 换取 docker 密码 | Controller 用 A 账号临时凭证调用 A 账号 TCR 获取 docker 登录用户名/密码 |
| ④ | Secret 分发 | Controller 渲染 dockerconfigjson(同时包含公网、VPC、自定义域名),按 CR 规则分发到 B 账号集群目标 ns / SA |
| ⑤ | 跨 VPC 免密拉取 | 业务 Pod 通过 SA 的 imagePullSecrets 完成 docker pull,依赖 VPC 对等/云联网 + Private DNS 保障网络可达 |
注意:说明:
注意:跨账号关键特征:A 账号 CAM 需要手动登记 B 账号 TKE 集群的 OIDC Issuer 与 JWKS,并在 Role 信任策略中显式声明“允许 B 账号 OIDC 身份扮演此角色”;此外,业务 Pod 拉取镜像时需要跨 VPC 网络互通。
四、账号角色说明
这篇操作步骤涉及两个腾讯云账号,为便于区分,统一使用 A / B 指代:
| 角色 | 说明 | 示例 UIN |
| A 账号 | TCR 实例所在账号(镜像提供方),负责创建 CAM OIDC 身份提供商、CAM 策略、CAM 角色。 | 100001113387 |
| B 账号 | TKE 集群所在账号(镜像消费方),负责开启 OIDC、安装 tcr-assistant-oidc 插件。 | 100002223233 |
跨账号免密拉取的核心机制是:B 账号 TKE 集群通过 OIDC 向 A 账号申请扮演 CAM 角色,STS 返回临时密钥后由插件写入 ImagePullSecret,实现跨账号镜像拉取。
六、步骤1:B 账号 TKE 集群开启 OIDC 功能
1. 登录 容器服务控制台,在左侧导航栏选择集群管理。
2. 选择目标集群,进入集群详情页,单击基本信息 > API Server 信息。
3. 单击 ServiceAccountIssuerDiscovery 右侧的编辑按钮。如下:
4. 进入修改 ServiceAccountIssuerDiscovery 相关参数页面。若系统提示你无法修改相关参数,请先进行服务授权。如下:
5. 在角色管理页面,查看授权策略 QcloudAccessForTKERoleInOIDCConfig,单击同意授权。如下:
6. 授权完成后,勾选创建 CAM OIDC 提供商和创建 webhook 组件,并填写客户端 ID,客户端 ID 的值固定为 sts.cloud.tencent.com,单击确定。如下:
7. 返回基本信息 > API Server 信息页,当 ServiceAccountIssuerDiscovery 可再次编辑时,表明本次开启 OIDC 资源访问控制结束。
注意:说明:
注意:service-account-issuer 和 service-account-jwks-uri 参数值不允许编辑,采用默认规则。
七、步骤2:B 账号确认 OIDC 身份提供商及 Webhook 组件创建成功
开启 OIDC 功能后,需要确认 CAM OIDC 身份提供商已创建且 pod-identity-webhook 组件已成功部署:
1. 在集群详情页,切换到 API Server 信息选项,单击 ServiceAccountIssuerDiscovery 右侧的编辑按钮。
2. 进入修改 ServiceAccountIssuerDiscovery 相关参数页面,系统将提示“你创建的身份提供商已存在,前往查看”。单击前往查看,确认 CAM OIDC 身份提供商已创建成功。
3. 查看刚创建的 CAM OIDC 身份提供商详细信息,确认客户端 ID 配置正确。如下:
4. 在集群信息 > 组件管理中,确认列表中 pod-identity-webhook 组件状态为"成功",即表示组件安装成功。
八、步骤3:A 账号创建 OIDC 身份提供商
B 账号 TKE 集群开启 OIDC 功能会默认在 B 账号创建一个身份提供商。跨账号访问 A 的 TCR 资源时,需要手动在 A 账号中也创建一个身份提供商,通过角色载体方式明确允许 B 账号通过 OIDC 访问 TCR 资源。
1. 通过 B 账号登录 CAM 控制台-身份提供商,选择对应集群查看 B 账号的 OIDC 身份提供商信息。创建 A 账号身份提供商需要填写的下列信息均可以在详情中获取。如下:
2. 通过 A 账号登录 CAM 控制台-身份提供商,新建提供商,需填写以下信息:
·身份提供商名称:填写 B 账号 TKE 集群的 ClusterID(如 cls-xxxxxxxx)。
·身份提供商 URL:填写 B 账号 TKE 集群 APIServer 信息中的 service-account-issuer 值。
·客户端 ID:固定填写 sts.cloud.tencent.com。
·签名公钥:填写 B 账号 TKE 集群的身份提供商公钥。
3. 准备好上述信息后,单击新建完成身份提供商创建。如下:
九、步骤4:A 账号创建 TCR 实例的 CAM 自定义策略
根据你的业务需求,创建自定义策略来控制 TCR 实例的访问权限。策略可以精确控制命名空间级别的访问权限。
注意:资源六段式中不能增加 region 和 uin 信息。例如 qcs::tcr:::instance/tcr-5jm4cabc,仅需替换最后的 TCR 实例 ID 部分。
策略示例:
{ "version": "2.0", "statement": [ { "action": [ "tcr:DescribeInstances" ], "effect": "allow", "resource": [ "qcs::tcr:::instance/tcr-5jm4cabc" ] }, { "action": [ "tcr:PullRepository", "tcr:PushRepository", "tcr:CreateRepository" ], "effect": "allow", "resource": [ "qcs::tcr:::repository/tcr-5jm4cabc/production-backend/*" ] } ]}
策略说明:
·tcr:DescribeInstances:login 时需要的权限。
·tcr:PullRepository:拉取镜像时需要的策略权限。
·tcr:PushRepository:按需授予,不影响镜像拉取。
·tcr:CreateRepository:按需授予,推送时如果 repo 不存在,则需要此权限。
·resource 中 production-backend/* 表示仅控制命名空间粒度。
十、步骤5:A 账号创建角色并绑定自定义策略
1. 登录 CAM 控制台-角色。
2. 在角色页中,单击新建角色 > 身份提供商。
3. 在新建自定义角色页,参考以下信息进行设置。如下:
注意:说明:
4. 绑定步骤4中创建的自定义策略,完成角色创建。如下:
十一、步骤6:打通不同账号间的 VPC 网络
B 账号 TKE 集群所在 VPC 与 A 账号 TCR 实例所在 VPC 之间需要网络互通。可参考 对等连接文档 完成 VPC 网络打通。
十二、步骤7:B 账号在 TKE 集群中安装 tcr-assistant-oidc 插件
在 B 账号的 TKE 集群中安装 tcr-assistant-oidc 插件,插件会基于 OIDC 身份动态生成临时 docker login 凭证,用于访问 A 账号的 TCR 资源。
根据实际场景选择以下安装方式之一:
十三、方式一:通过云 API 安装(最新版本 1.0.2)
通过云 API 调用 InstallAddon 接口安装插件。如下:
字段说明:
·A 账号角色 RoleArn 可在 CAM 控制台角色详情页获取。如下:
·TCR 实例名称 registries 可在 TCR 控制台实例列表获取。如下:
·如果 A 账号的 TCR 实例绑定了自定义域名(如 docker.mycompany.com),需要额外配置 customDomains。
请注意:Values 参数已弃用。如需使用,请通过 RawValues 传入,参数值为 JSON 进行 base64 编码后的内容。示例如下:
echo -n '{ "roleArn": "qcs::cam::uin/100000000000:roleName/your-role-name", "registries": ["your-tcr-registry"]}' | base64
RawValues JSON 完整示例:
{ "roleArn": "qcs::cam::uin/100001113387:roleName/tcr-assistant-oidc-read-harbor", "registries": ["your-tcr-registry"], "customDomains": ["docker.mycompany.com"], "tokenExpiration": 3600, "namespaces": "*", "serviceAccounts": "*"}
十四、方式二:通过 Terraform 安装
resource "tencentcloud_kubernetes_addon" "tcr_assistant_oidc" { cluster_id = "cls-xxxxxxxx" # 替换为你的 TKE 集群 ID addon_name = "tcr-assistant-oidc" raw_values = jsonencode({ # CAM 角色 ARN(必填) # 格式: qcs::cam::uin/{UIN}:roleName/{ROLE_NAME} roleArn = "qcs::cam::uin/100000000000:roleName/your-role-name" # TCR 镜像仓库名称列表(必填) registries = ["your-tcr-registry"] # 目标 namespace("*" 表示所有,或 "ns1,ns2,ns3") namespaces = "*" # 目标 ServiceAccount("*" 表示所有,或 "sa1,sa2") serviceAccounts = "*" # 自定义域名(选填) customDomains = ["docker.mycompany.com"] })}
十五、验证 B 账号验证插件安装状态
安装完成后,在 B 账号的 TKE 集群中执行以下命令确认插件正常运行:
# 确认 Controller Pod 正常运行kubectl get pods -n tcr-assistant-system# 预期:Pod 为 Running 状态# 检查 CR 状态,确认已成功从 A 账号获取临时密钥kubectl get oips tcr-oidc-public -o yaml# 关注 status.credentialStatus 应为 Ready# 确认 imagePullSecret 已分发到目标命名空间kubectl get secret tcr.ips.tcr-oidc-public -n default \\ -o jsonpath='{.data.\\.dockerconfigjson}' | base64 -d | jq '.auths | keys'
预期输出包含 A 账号 TCR 实例的所有域名:
[ "your-tcr-registry.tencentcloudcr.com", "docker.mycompany.com"]
十六、步骤8:B 账号添加 TCR 实例的 Private DNS 解析
在 B 账号侧为 A 账号的 TCR 实例添加私有域解析,确保集群内可通过内网域名访问 TCR 实例。如下:
十八、场景一:多个账号(B、C)共享访问 A 账号 TCR 镜像
A 账号的 TCR 实例需要被多个不同账号的 TKE 集群访问:
1. A 账号创建角色时配置多个载体:
qcs::cam::uin/222:roleName/a-policy-for-tcr # B 账号qcs::cam::uin/333:roleName/a-policy-for-tcr # C 账号
2. 各账号集群分别安装插件,使用相同的 roleArn。
十九、场景二:通过自定义域名跨账号拉取镜像
A 账号的 TCR 实例绑定了自定义域名 docker.mycompany.com,B 账号需要通过该域名拉取 。在安装组件时,指定 customDomains=["docker.mycompany.com"]。安装后 B 账号集群即可通过自定义域名拉取 A 账号的镜像:
kubectl run nginx –image=docker.mycompany.com/my-namespace/nginx:latest
20、场景三:限定生效范围(指定命名空间和 ServiceAccount)
只在 B 账号集群的特定命名空间和 ServiceAccount 中生效:
helm install tcr-assistant-oidc ./tcr-assistant-oidc \\ –namespace tcr-assistant-system –create-namespace \\ –set roleArn="qcs::cam::uin/100000000000:roleName/your-role-name" \\ –set "registries={your-tcr-registry}" \\ –set namespaces="production,staging" \\ –set serviceAccounts="default,app-sa"
22、升级组件版本
使用 UpdateAddon 升级 tcr-assistant-oidc 组件版本。升级时的参数无需重复指定,只需要更新组件版本和预期要更新的参数配置。tcr-assistant-oidc 组件目标版本列表参考 组件版本维护说明。
注意:说明:
注意:升级过程中已有 Secret 不会中断,Controller 重启后自动恢复 reconcile。
23、验证升级结果
升级完成后,确认 Controller Pod 已更新并正常运行:
# 检查 Pod 状态和镜像版本kubectl get pods -n tcr-assistant-system -o wide# 检查 CR 状态kubectl get oips tcr-oidc-public -o jsonpath='{.status.credentialStatus}'# 预期输出:Ready
25、Pod 拉取镜像失败(ImagePullBackOff)
排查步骤:
1. 检查 Secret 是否存在:
kubectl get secret tcr.ips.tcr-oidc-public -n <pod-namespace>
2. 检查 ServiceAccount 是否注入了 imagePullSecrets:
kubectl get sa default -n <pod-namespace> -o jsonpath='{.imagePullSecrets}'
3. 检查 Controller 日志:
kubectl logs -n tcr-assistant-system deployment/tcr-assistant-oidc-controller
26、临时密钥过期
动态生成的临时密钥有效期固定为 2 小时(7200 秒),tokenExpiration 控制轮转间隔。如果设置过大(>5400),可能导致密钥过期。
解决方案:确保 tokenExpiration 在 3600~5400 范围内。
27、OIDC 环境变量未注入
如果 Controller Pod 缺少 OIDC 相关环境变量(TKE_WEB_IDENTITY_TOKEN_FILE 等),说明 B 账号集群的 pod-identity-webhook 未正常工作。
解决方案:
1. 确认 B 账号 TKE 集群已开启 OIDC。
2. 确认 pod-identity-webhook Pod 正常运行。
3. 重建 Controller Pod。
28、AssumeRole 失败(权限不足)
如果 Controller 日志中出现 AssumeRoleWithWebIdentity 失败,说明 A 账号的角色载体配置或 B 账号的策略配置有误。
排查步骤:
1. 确认 A 账号角色的信任策略中包含 B 账号 TKE 集群的 OIDC Provider。
2. 确认 A 账号角色已关联 TCR 访问权限。
3. 确认 B 账号策略中的 resource 字段与 A 账号角色 ARN 完全一致。
29、手动验证临时密钥
从 Secret 中提取凭证手动登录验证:
# 获取凭证SECRET_JSON=$(kubectl get secret tcr.ips.tcr-oidc-public -n default \\ -o jsonpath='{.data.\\.dockerconfigjson}' | base64 -d)# 提取用户名和密码,your-tcr-registry.tencentcloudcr.com 要替换成实际的访问域名USERNAME=$(echo $SECRET_JSON | jq -r '.auths["your-tcr-registry.tencentcloudcr.com"].username')PASSWORD=$(echo $SECRET_JSON | jq -r '.auths["your-tcr-registry.tencentcloudcr.com"].password')# 手动登录 A 账号的 TCR 实例,your-tcr-registry.tencentcloudcr.com 要替换成实际的访问域名。注意访问的客户端机器需要能够正常访问TCR域名。docker login your-tcr-registry.tencentcloudcr.com –username "$USERNAME" –password "$PASSWORD"