密钥仓库:把密码从 YAML 里赶出去
.cnb.yml 是进版本库的,写死密码等于把密码公开。CNB 的答案是密钥仓库。
一、它特殊在哪
密钥仓库是一个专用仓库类型,和普通仓库相比少了两个能力、多了两个:
| 能力 | 普通仓库 | 密钥仓库 |
|---|---|---|
| Clone 到本地 | ✅ | ❌ 禁止 |
| 本地推送 | ✅ | ❌ 禁止 |
| Web 界面编辑 | ✅ | ✅ 只能这样改 |
| 被流水线引用 | ✅ | ✅ 核心用途 |
也就是说:数据只进不出,所有变更都在受审计的 Web 界面完成。
另外两个安全细节:
- 所有页面带动态水印(当前用户名),截图外泄可溯源
- 完整的审计日志:记录每个文件何时被哪条流水线引用
二、建一个
登录后进入 https://cnb.cool/new/repos,仓库类型选 密钥仓库,填名字即可。
建议按环境或项目拆文件,别塞一个大文件:
密钥仓库/
├── env.prod.yml # 生产
├── env.staging.yml # 预发布
└── env.dev.yml # 开发
格式就一行一个键值:
DOCKER_USER: "prod_username"
DOCKER_TOKEN: "prod_token_123456"
DOCKER_REGISTRY: "registry.prod.com"
三、四种把文件引进来
| 方式 | 配置项 | 用途 |
|---|---|---|
| 环境变量引用 | imports | 把 yml 文件变成环境变量 |
| 流水线模板引用 | include | 复用整段流水线配置 |
| 内置任务参数引用 | optionsFrom | 内置任务的 options 从文件读 |
| 插件任务参数引用 | settingsFrom | 插件任务的 settings 从文件读 |
后三种支持环境变量拼接路径:
.cnb.yml
main:
push:
- env:
SECRET_BASE: https://cnb.cool/my/secrets/-/blob/main
imports:
- ${SECRET_BASE}/env.prod.yml
stages:
- name: 用密钥
script: echo "user=$DOCKER_USER"
optionsFrom / settingsFrom 还支持本地路径——甚至可以是流水线上刚写出来的文件,不用提交进仓库。
四、谁能引用:allow_* 规则
默认规则:只有密钥仓库的管理员和负责人触发的流水线才有权引用。
想让更广的人用,必须在密钥文件里声明四个字段:
allow_slugs: "group/project,group/another-*" # 哪些仓库可以引用
allow_events: # 哪些事件可以引用
- push
- tag_push
- "tag_deploy.*"
allow_branches: # 哪些分支可以引用
- main
- "feature/*"
allow_images: "cnbcool/ssh" # 哪些镜像插件可以引用
:::warning 一配 allow_* 就换规则
只要你声明了任一 allow_*,系统就完全忽略触发者角色,只按规则校验,且所有规则都要通过。
好处是可预期,坏处是配漏一条就全拒。
:::
glob 记法:* 不跨 /,** 跨 /。多级组织路径必须用 **。
五、四条最佳实践
1. 最小权限
allow_events只放tag_push/tag_deploy.*,别放push——每次推代码都摸密钥没意义allow_branches只放main等保护分支
2. 环境分离 生产、预发、开发各用各的文件,别一个文件塞三套。
3. 收敛配置入口
把带 imports 的流水线放进一个权限受控的仓库,通过 include 给其他仓库用。改的人少,风险点就少。
4. 定期轮换 + 审计 换密钥只需在 Web 界面改文件,所有引用的流水线自动拿到新值。定期清掉不用的引用规则。
六、密钥进来了也可能泄漏
密钥仓库本身安全,但注入到流水线之后:
- 脚本里
echo一下就进了日志 - 第三方命令可能把环境变量打进错误输出
所以:不要在脚本里打印任何密钥变量;调试时用 echo "${TOKEN:0:4}***" 这种脱敏写法。