@openlibing/huaweicloud-oidc-client
v0.0.6
Published
openlibing 平台与华为云交互 SDK:OIDC 认证获取临时凭证、callApig 一行调用 APIG(V11-HMAC-SHA256 签名)、HTTPS 请求工具(基于 Node 内置 fetch)
Readme
@openlibing/huaweicloud-oidc-client
openlibing 平台对接华为云的 SDK(npm 包):基于 CI 流水线 OIDC(GitHub Actions / GitCode Actions)→ 华为云 STS 免密换取临时凭证,全程零 AK/SK 密钥。
简介
问题
CI 流水线(GitHub Actions / GitCode Actions)调用华为云服务(APIG 接口、OBS 上传等)时,传统做法是把永久 AK/SK 配置成仓库 secret:
- 永久密钥长期有效,一旦泄露即可被任意冒用,泄露面还会随仓库协作者、CI 日志等扩散;
- 轮换依赖人工,密钥管理成本高;
- 密钥权限往往大于流水线实际所需,难以满足最小权限与审计要求。
解决方案
基于 OIDC 联邦认证免密换证,仓库中不存放任何密钥:
CI 流水线 OIDC ID Token(GitHub Actions / GitCode Actions,签发方式一致)
→ 华为云 STS AssumeAgencyWithOIDC 换临时凭证
→ OBS 上传(临时凭证 + esdk-obs-nodejs)
→ APIG 调用(V11-HMAC-SHA256 签名 + X-Security-Token)- 流水线声明
id-token: write权限,运行时向平台申请短期 OIDC ID Token; - SDK 携带令牌调用华为云 STS
AssumeAgencyWithOIDC,换取临时 AK/SK/SecurityToken(默认 1 小时有效,SDK 自动缓存、临期刷新、并发去重); - 用临时凭证访问目标服务:调用 APIG 时 SDK 自动完成 V11-HMAC-SHA256 签名并携带
X-Security-Token;上传 OBS 时将临时凭证注入esdk-obs-nodejs客户端。
华为云 IAM 侧通过「身份提供商 + 信任委托」限定可换证的令牌来源(iss / aud / sub 逐项校验),只有指定仓库的流水线能换取凭证;IAM 侧配置见下方「华为云侧配置」。
快速开始
最常见的使用场景:在 GitCode Actions / GitHub Actions 流水线的本地自定义插件(Action)中引入 SDK,免 AK/SK 调用 APIG 接口或上传文件到 OBS。两类流水线的接入方式完全一致(OIDC Token 获取方式相同),完整可运行示例见仓库自带的 .gitcode/actions/test-apig-action/ 与 .gitcode/actions/test-obs-action/,对应的使用方 workflow 见 test-action-workflow.yml。
1. 插件声明依赖
在插件目录的 package.json 中引入 SDK;OBS 上传场景还需引入 esdk-obs-nodejs(固定 3.26.2,后续版本存在构造后立即上传的竞态问题),仅调用 APIG 时无需引入。要求 Node 18+(SDK HTTP 层基于内置 fetch):
{
"dependencies": {
"@openlibing/huaweicloud-oidc-client": "^0.0.5",
"esdk-obs-nodejs": "3.26.2"
}
}2. 初始化配置
SDK 内置 openlibing 平台(GitCode Actions)的华为云账号配置,不调用 configure() 也能直接开箱即用。内置默认值即以下代码注释中标注的值:
const { configure } = require("@openlibing/huaweicloud-oidc-client");
// 例:接入其他华为云账号 / GitHub 平台流水线时,按需覆盖对应字段
// (未传字段保持内置默认值;全部可覆盖字段见「核心 API」的配置项说明)
configure({
accountId: "4d29a984c4fe4e6eb5d404a853d0084e", // 默认:openlibing 华为云账号 ID
audience: "huawei-cloud-service", // 默认:申请 OIDC ID Token 的 audience,需与身份提供商注册的客户端 ID 一致
agencyName: "gitcode-actions", // 默认:IAM 信任委托名称
oidcProviderName: "GitCodeActions", // 默认:华为云侧 OIDC 提供商(GitHub 平台接入时覆盖为 'GitHubActions')
region: "cn-southwest-2", // 默认:STS 端点区域,换证走任一支持的 region 均可(按网络可达性与速度选择);callApig 默认还以它作 APIG 签名区域(可 opts.region 覆盖)
durationSeconds: 3600, // 默认:临时凭证有效期(秒)
refreshBufferSeconds: 300, // 默认:提前刷新缓冲(秒),避免凭证在边界过期
debug: false, // 默认:调试日志开关,开启后额外打印请求/响应详情(敏感字段自动脱敏)
});自定义配置:接入其他华为云账号(或 GitHub 平台流水线)时,按需覆盖对应字段,未传字段保持默认值。以接入自有账号为例,字段需与 IAM 侧注册一致(见「华为云侧配置」):
// 例:接入自己的华为云账号
configure({
accountId: "<华为云账号ID>",
oidcProviderName: "GitHubActions", // 或 'GitCodeActions',取决于流水线平台
agencyName: "my-agency",
audience: "my-audience",
region: "cn-north-4",
});3. 场景一:调用 APIG 接口
插件代码中引入 SDK,callApig 一行完成 OIDC 免密换证 + V11 签名 + 请求发送:
const core = require("@actions/core");
const { callApig, configure } = require("@openlibing/huaweicloud-oidc-client");
configure({ debug: false }); // 可选:开启后额外打印请求/响应详情(敏感字段自动脱敏)
const res = await callApig(
"GET",
"https://{apig-instance-id}.apic.cn-southwest-2.huaweicloudapis.com/version",
);
// => { status, headers, data }
if (res.status >= 200 && res.status < 300) {
core.info(`调用成功: ${JSON.stringify(res.data)}`);
} else {
core.setFailed(`调用失败 (HTTP ${res.status}): ${JSON.stringify(res.data)}`);
}4. 场景二:上传文件到 OBS
getCredentials() 换取临时凭证后,交给 OBS SDK 客户端执行上传:
const ObsClient = require("esdk-obs-nodejs");
const {
getCredentials,
configure,
} = require("@openlibing/huaweicloud-oidc-client");
const { region } = configure({ debug: false });
const cred = await getCredentials();
// => { accessKeyId, secretAccessKey, securityToken, expiresAt, expiresIn }
const client = new ObsClient({
access_key_id: cred.accessKeyId,
secret_access_key: cred.secretAccessKey,
security_token: cred.securityToken,
server: `https://obs.${region}.myhuaweicloud.com`,
});
const result = await client.putObject({
Bucket: "your-bucket-name",
Key: "path/to/object.csv",
SourceFile: "./local-file.csv",
});
await client.close();5. workflow 声明 OIDC 权限并使用插件
流水线必须声明 id-token: write 权限,否则插件无法向平台申请 OIDC ID Token。完整 workflow 示例(含手动触发与两个场景的插件调用):
name: Deploy via OIDC
on:
workflow_dispatch:
inputs:
debug:
description: "开启 SDK 调试定位日志"
type: boolean
required: false
default: false
push:
branches:
- main
permissions:
repository: read
id-token: write # 必须声明此权限,否则无法申请 OIDC ID Token
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: checkout
- name: Call APIG via OIDC
uses: ./.gitcode/actions/my-apig-action
with:
debug: ${{ inputs.debug }} # 可选:开启 SDK 调试日志
- name: Upload to OBS via OIDC
uses: ./.gitcode/actions/my-obs-action
with:
file-path: ./dist/app.zip
debug: ${{ inputs.debug }}提示:流水线 runner 不会为本地插件执行
npm install,插件需用ncc build将依赖(含 SDK)构建为自包含产物并提交dist/,构建方式参考两个测试插件。
华为云侧配置
SDK 接入前需在华为云 IAM 侧完成联邦认证配置(按流水线平台分别注册),共三步:创建 OIDC 身份提供商 → 创建信任委托并配置信任策略 → 为委托授予身份策略。
创建 OIDC 身份提供商
在 IAM 控制台「身份提供商」中按流水线平台分别创建 OIDC 类型身份提供商:
| 平台 | 提供商名称 | 颁发者 URL | 客户端 ID(audience) |
| --------------- | ---------------- | --------------------------------------------- | ---------------------- |
| GitCode Actions | GitCodeActions | https://actions-results.atomgit.com | huawei-cloud-service |
| GitHub Actions | GitHubActions | https://token.actions.githubusercontent.com | huawei-cloud-service |
- 提供商 URN 格式:
iam::<华为云账号ID>:oidcProvider:<提供商名称> - 客户端 ID 必须与 SDK 申请 OIDC ID Token 时使用的
audience一致(内置默认huawei-cloud-service)。
创建信任委托
在 IAM「委托」中创建信任委托(名称如 gitcode-actions),信任主体选择上一步创建的 OIDC 身份提供商。委托 URN 为 iam::<华为云账号ID>:agency:gitcode-actions。
信任策略通过 Condition 限定可换取凭证的令牌来源,仅允许来自指定仓库的令牌通过。oidc:sub 建议使用 StringMatch 运算符(区分大小写,支持 * 与 ? 通配符,比 StringEquals 精确匹配更灵活)。以下为 GitCode Actions 平台示例(GitHub 平台将 Federated 的提供商 URN 与 oidc:iss 替换为 GitHub 侧对应值):
{
"Version": "5.0",
"Statement": [
{
"Action": ["sts:agencies:assumeWithOIDC"],
"Effect": "Allow",
"Principal": {
"Federated": ["iam::<华为云账号ID>:oidcProvider:GitCodeActions"]
},
"Condition": {
"StringEquals": {
"oidc:iss": ["https://actions-results.atomgit.com"],
"oidc:aud": ["huawei-cloud-service"]
},
"StringMatch": {
"oidc:sub": ["repo:<组织>/<仓库>:ref:refs/heads/main"]
}
}
}
]
}oidc:sub 的取值格式(标识哪个仓库的哪条分支在换证,匹配区分大小写,组织名/仓库名/分支名的大小写必须与平台完全一致):
- GitCode Actions:
repo:<组织>/<仓库>:ref:refs/heads/<分支>,与 GitHub 名称格式一致;实际取值以流水线签发的令牌为准,可从调试日志读取。 - GitHub Actions 有两种格式,同时存在且无法相互转换:
- 名称格式(存量仓库沿用):
repo:<组织>/<仓库>:ref:refs/heads/<分支> - 不可变 ID 格式(2026-07-15 起新建、重命名或转移的仓库使用,名称后追加
@<组织ID>/@<仓库ID>):repo:<组织>@<组织ID>/<仓库>@<仓库ID>:ref:refs/heads/<分支> - 兼容两类仓库时,把两种格式同时放入
oidc:sub数组即可(StringEquals/StringMatch均按「请求值与任意一个条件值匹配」判定)。
- 名称格式(存量仓库沿用):
通配符放宽范围(StringMatch 支持 * 匹配任意多字符、? 匹配单字符):
"StringMatch": {
"oidc:sub": [
"repo:<组织>/<仓库>:*", // 放行该仓库的所有分支
"repo:<组织>/*:*" // 放行该组织内所有仓库的所有分支
]
}oidc:iss与oidc:aud是必选条件键,缺少任一会导致创建或修改信任委托时校验失败;oidc:sub强烈建议配置,用于在共享身份提供商场景下防范「混淆代理」攻击(限定只有指定仓库的流水线能换证),未限定oidc:sub时任何仓库的流水线都可能换证。- 信任策略元素与条件运算符的完整定义见华为云《身份策略 JSON 元素参考》(
StringMatch推荐使用、StringLike已标注不推荐);GitHub 平台以oidc:sub限定组织/仓库/分支的说明见《创建 OIDC 身份提供商的信任委托》。
为委托授予身份策略
- 调用 APIG:IAM 自定义身份策略不支持控制可调用的 APIG 接口范围,无需额外配置身份策略——默认即可调用全部以 IAM 认证的 APIG 接口。前提是 APIG 接口的安全认证方式需配置为 IAM 认证。建议为流水线调用新增接口,不要直接修改存量接口的认证方式,避免影响现有业务。
- 上传 OBS:需在 IAM「策略」中配置自定义身份策略并按最小权限授予。示例(允许读写
openlibing-gitcode-action桶内所有对象):
{
"Version": "5.0",
"Statement": [
{
"Effect": "Allow",
"Action": ["obs:object:PutObject", "obs:object:GetObject"],
"Resource": ["obs:*:*:object:openlibing-gitcode-action/*"]
}
]
}关键配置项对照
| 配置项 | 示例值 | 说明 |
| -------------------------- | ---------------------------------- | --------------------------------------------------------- |
| 华为云账号 ID | <华为云账号ID> | configure({ accountId }) 必须使用真实账号 ID |
| OIDC 身份提供商 | GitCodeActions / GitHubActions | configure({ oidcProviderName }) 需与 IAM 侧注册名一致 |
| OIDC 客户端 ID(audience) | huawei-cloud-service | configure({ audience }) 需与 IAM 侧注册的客户端 ID 一致 |
| IAM 信任委托 | gitcode-actions | configure({ agencyName }) 需与 IAM 侧委托名一致 |
| 区域 | cn-southwest-2 | configure({ region }) 影响 STS 端点与 APIG 签名 scope |
排错时优先核对三处一致性:SDK 配置与华为云侧命名(身份提供商、委托、audience)、区域(APIG 域名、STS、OBS)、签名头完整性(含 X-Security-Token)。开启 configure({ debug: true }) 后,Token 的 iss/aud/azp/sub 声明会在调试日志中打印,可与华为云信任策略逐项比对;换证失败时这些声明也会以 error 级自动补打,无需手动开启。
npm 包构建与发布
npm test # 运行 SDK 测试
npm run build # npm pack,产出 openlibing-huaweicloud-oidc-client-x.y.z.tgz(仅含 src/)
npm publish --access public # 发布到 npm(scoped 包需显式 public)发布流水线见 .gitcode/workflows/deploy.yml,打 tag 或手动触发即可发布(依赖仓库 secret NPM_TOKEN)。
