容器服务 tcr-assistant-oidc 跨账号动态免密拉取配置

在多账号治理的企业中,镜像资产(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-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"

赞 (0)
上一篇 4天前
下一篇 4天前