帮你快速理解、总结文档立即下载

tcr-assistant-oidc 跨账号动态免密拉取配置

最近更新时间:2026-08-11 16:20:31
我的收藏

操作场景

在多账号治理的企业中,镜像资产(TCR 企业版实例)通常集中托管在一个账号中,而业务应用(TKE 集群)会分散在多个业务账号中。这种情况下,业务账号的 TKE 集群需要拉取镜像账号下的容器镜像,传统做法是将镜像账号的长期访问凭证(用户名/密码)以 ImagePullSecret 的形式分发到各业务账号集群中,存在以下痛点:
凭证难以管理:长期凭证需要人工在多账号间同步、轮换,运维成本高。
安全风险高:长期凭证一旦泄露即长期有效,且难以追溯到具体使用方。
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-webhookB 账号 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-issuerservice-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 组件状态为"成功",即表示组件安装成功。
5. 集群详情 > 工作负载 > Deployment 页面,确认 kube-system 命名空间下 pod-identity-webhook 的状态为 Running 即表示安装成功。

步骤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 账号创建角色并绑定自定义策略

2. 在角色页中,单击新建角色 > 身份提供商
3. 在新建自定义角色页,参考以下信息进行设置。如下图所示:
说明:
oidc:aud 的 value 值需要和 CAM OIDC 提供商的客户端 ID value 值保持一致。
oidc:aud 的 value 值标识为 $my_pod_audience,当 oidc:aud 的 value 值有多个时,任选其中之一即可。

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 参数已弃用。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-odic-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

场景三:限定生效范围(指定命名空间和 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"

升级与变更

升级组件版本

使用 UpdateAddon 升级 tcr-assistant-oidc 组件版本。升级时的参数无需重复指定,只需要更新组件版本和预期要更新的参数配置。tcr-assistant-oidc 组件目标版本列表参考 组件版本维护说明
说明:
升级过程中已有 Secret 不会中断,Controller 重启后自动恢复 reconcile。

验证升级结果

升级完成后,确认 Controller Pod 已更新并正常运行:
# 检查 Pod 状态和镜像版本
kubectl get pods -n tcr-assistant-system -o wide

# 检查 CR 状态
kubectl get oips tcr-oidc-public -o jsonpath='{.status.credentialStatus}'
# 预期输出:Ready

故障排查

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

临时密钥过期

动态生成的临时密钥有效期固定为 2 小时(7200 秒),tokenExpiration 控制轮转间隔。如果设置过大(>5400),可能导致密钥过期。
解决方案:确保 tokenExpiration 在 3600~5400 范围内。

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。

AssumeRole 失败(权限不足)

如果 Controller 日志中出现 AssumeRoleWithWebIdentity 失败,说明 A 账号的角色载体配置或 B 账号的策略配置有误。
排查步骤
1. 确认 A 账号角色的信任策略中包含 B 账号 TKE 集群的 OIDC Provider。
2. 确认 A 账号角色已关联 TCR 访问权限。
3. 确认 B 账号策略中的 resource 字段与 A 账号角色 ARN 完全一致。

手动验证临时密钥

从 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"