CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
GitHub Actions CI/CD/课时/Secrets 与环境
RawBlame
课时目录12 节

GitHub Actions CI/CD

  1. 01CI/CD 与 GitHub Actions
  2. 02Workflow 文件结构
  3. 03Jobs 与运行器
  4. 04Steps 与 Actions
  5. 05Secrets 与环境
  6. 06依赖缓存
  7. 07构建 Spring Boot
  8. 08构建 Next.js platform
  9. 09构建推送镜像
  10. 10部署策略
  11. 11PR 检查与分支保护
  12. 12综合练习

Secrets 与环境

repository secrets、environments、OIDC 概念。

25 分钟

Secrets 与环境

这一课你将能做到

  1. 在仓库 Settings → Secrets and variables → Actions 创建 secret,并在 workflow 里用 ${{ secrets.NAME }} 引用
  2. 解释为什么密码不能写进 YAML,以及日志里 secret 为什么显示为 ***
  3. 给部署类 job 挂上 environment: production,并说清「环境保护规则(审批/等待)」与普通 repository secret 的差别

为什么要学

CI 迟早要碰真实凭据:推 GHCR、SSH 到 VPS、读云厂商 API。把密钥写进 ci.yml 再 push,等于把钥匙贴在大门上——Git 历史还会长期留下,删文件也删不掉历史。

GitHub Secrets 把值存在平台侧,YAML 里只出现名字;运行时注入环境变量或输入,日志自动打码。再配合 Environments,生产部署可以要求人工审批、加等待计时,还能用环境专属 secrets。

本课建立正确的存放与引用习惯;具体 Docker 登录、SSH 部署在后几课用到这些名字。

先建立图像

仓库 Settings
  └─ Secrets and variables → Actions
        DB_PASSWORD = ••••••(只存平台,不进 Git)
        SSH_PRIVATE_KEY = ••••••

workflow YAML(进 Git,可公开读)
  env:
    DB_PASSWORD: ${{ secrets.DB_PASSWORD }}   ← 只有名字

runner 运行时
  进程环境里有真实值;日志中自动打码为 ***

人话:Secrets 像保险箱;YAML 只写「去保险箱取哪一把钥匙」的标签,不写钥匙齿形。

存放处适合特点
Repository secret单仓库 CI/CD有 admin 权限可管理;运行时可读
Environment secret某环境(如 production)叠加审批、等待计时等保护规则
Variable(非 secret)非敏感配置如区域名、Node 版本明文可见、不打码
写死在 YAML / 提交 .env无泄漏,禁止

跟着做

步骤 1:创建练习用 secret

仓库 → Settings → Secrets and variables → Actions → New repository secret:

  • Name:CI_TEST
  • Value:随意字符串,如 hello-secret

保存后列表里只能看到名字,看不到值(明文不可再查看,只能覆盖更新)。

步骤 2:在 workflow 里引用

name: Secret smoke

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  show:
    runs-on: ubuntu-latest
    steps:
      - name: 引用 secret(观察打码)
        env:
          # 值来自 Settings,不进 Git
          CI_TEST: ${{ secrets.CI_TEST }}
        run: |
          # 日志中通常显示为 ***,不要用技巧拼回明文
          echo "CI_TEST length=${#CI_TEST}"
          echo "CI_TEST value=$CI_TEST"

手动跑一次:value 应被打成 ***,但 length 能对上。 若显示空:检查名字是否完全一致(大小写敏感)、是否处在 fork PR 场景(见后文)。

步骤 3:对照「绝对不要」的写法

# 错误示范:密钥进仓库
env:
  DB_PASSWORD: "SuperSecret123"

即使后来删掉,历史 commit 仍可能残留。正确做法:轮换已泄漏的密钥,并改用 secrets。与「不提交 .env」是同一纪律。

步骤 4:挂 Environment(了解生产门闩)

Settings → Environments → 新建 production → 勾选 Required reviewers(填你自己练习)。

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production   # 使用该环境的保护规则与 secrets
    steps:
      - name: 假装部署
        run: echo "deploying with protected env"

再跑:run 会停在 waiting 状态,直到有人在 run 页面点批准。笔记写:「Environment = 保护规则 + 可选专属 secrets。」

步骤 5:为本站列一张 secrets 清单(先名字后赋值)

Name用途何时需要
GITHUB_TOKEN内置,推包/评论 PR 等自动注入;注意 permissions
SSH_PRIVATE_KEYSSH 到 VPS 部署采用 Push 部署时
DEPLOY_HOST服务器地址同上
DEPLOY_USER服务器账号同上
(云 OIDC)短期云凭证进阶,代替长期 AK/SK

把表写进 README 或内部文档,值只进 Settings。

步骤 6:了解 OIDC(概念即可)

长期 Access Key 放 secret 能用,但泄漏窗口大。云厂商推荐 OIDC:Actions 向云身份 provider 换取短时凭证,云侧按角色授权。本课不配云;知道「生产方向是短时联邦身份,而不是永久拷贝 AK 进 Actions」即可。

讲清楚要点

  1. 因为 Git 会永久记录,所以密钥只能进 Secrets,不能进 YAML 或提交的 .env——泄漏后的正确动作是轮换密钥,不是删文件。
  2. 因为 YAML 常被公开,所以文件里只出现 ${{ secrets.NAME }} 引用——审流水线的人看见的是名字,不是钥匙。
  3. 因为日志可能被转发或截图,所以不要 echo 明文、不要 base64 拼解——打码不是绝对防护,主动规避才是。
  4. 因为生产部署风险更高,所以用 Environment 加审批与独立 secret——与「随便 push 就发生产」隔离。
  5. 因为 fork 来的 PR 默认拿不到 secrets,所以开源协作更安全——这也解释了「为什么 fork PR 里部署 job 没有密钥」。
  6. 因为 GITHUB_TOKEN 权限由 permissions 控制,所以推 GHCR 要显式开 packages: write——不是「有 token 就能为所欲为」。

你可能会懵的地方

  1. 「日志都是 ***,怎么知道注入成功了?」 看长度、看下游步骤是否真的登录成功(如 docker login 返回 0)。不要为调试打印明文——打码基于精确子串匹配,base64 等变换可能绕过,属严重事故。

  2. 「Organization secret 和 repo secret?」 组织级可下发到多仓库,可按环境限定可见仓库;本课主路径用 repo secret 足够。重名时的优先级规则见官方文档。

  3. 「pull_request_target 能读 secret,是不是更好用?」 能读,但也更容易被恶意 PR 组合滥用(在外部 PR 内容上跑你的高权限流程)。默认优先 pull_request;确需使用先完整读完官方安全说明,且严禁 checkout 并执行 PR 分支里的不可信脚本。

  4. 「Variables 和 Secrets 有啥区别?」 Variables 明文不打码,适合非敏感配置(如部署区域、功能开关);密码、密钥、token 一律 Secrets。

  5. 「secret 能用在工作流所有地方吗?」 大多数地方可以,但注意 schedule/fork PR 等场景可用性不同;另外 ${{ secrets.X }} 直接内联进 run: 脚本时有注入风险,推荐经 env: 传入再用。

错法对照

错法问题正解
密码写在 workflow 明文公开即泄漏且进 Git 历史${{ secrets.XXX }}
用 secret 存 Node 版本号这类公开配置管理成本高、无意义打码用 variable 或直接写 YAML
为调试 base64 打印 secret 再解打码失效,日志泄漏用长度与下游成功与否验证
生产部署无 Environment 保护任何人改 YAML 可能直推生产environment + reviewers
secret 名大小写与引用不一致读到空值命名统一大写下划线,逐字符核对

卡点急救

  1. secret 读到空值:三查——名字拼写与大小写是否完全一致;是否在 fork PR 中(默认拿不到 secrets);job 是否挂了不带该 secret 的 environment(environment secret 与 repo secret 作用域不同)。
  2. run 卡在 waiting for approval:这是 Environment 的 Required reviewers 在起作用,去 run 详情页点 Review deployments 批准即可;不想要审批就编辑环境的保护规则。
  3. 怀疑密钥已泄漏:立即在源平台轮换/吊销旧值,再在 Settings 更新 secret;若曾提交进 Git,还需清理历史(filter-repo)——但首要动作永远是轮换,历史清理不能替代。

面试常问

  • GitHub Actions 的 secrets 是怎么存储和保护的? 存储在 GitHub 平台侧,加密保存;workflow 里只出现名字引用,运行时注入到进程环境;日志输出基于存储值的精确子串自动打码为 ***。要点补充:打码可被子串变换绕过,所以最佳实践还包括不打印敏感值、最小 permissions、生产用 environment 审批隔离。

  • 为什么 fork 仓库的 PR 拿不到 secrets?这在设计上解决了什么问题? 外部贡献者的 PR 可能包含恶意 workflow 改动,如果它能读 secrets,攻击者就能把密钥外传。所以 pull_request 事件来自 fork 时只有只读 token、无 secrets。这是「不可信输入与高权限隔离」的安全模型,也回答了为什么开源项目的 CI 对外部 PR 更受限。

  • 如何设计生产部署的凭据管理? 分层答:① 尽量不把人类级凭据给 CI——GitOps 模式下 CI 只需推镜像权限与开 PR 权限;② 必须给时用 environment secrets + required reviewers + 最小作用域(如只写某个 namespace 的受限 kubeconfig);③ 云上优先 OIDC 短时凭证,避免长期 AK/SK;④ 定期轮换并审计 secret 使用记录。

自测三问

  1. 为什么不能把数据库密码直接写在 ci.yml?
  2. ${{ secrets.CI_TEST }} 出现在仓库哪个位置?真实值存在哪?
  3. environment: production 主要解决什么问题?

小结一句话

Secrets 存 Settings;YAML 只引用名字;生产部署再用 Environment 加上门闩。

延伸

  • Using secrets in GitHub Actions
  • Using environments for deployment
  • 下一课:依赖缓存——干净机器的代价,用缓存把重复下载省掉。
加载本节练习…
← 上一课Steps 与 Actions下一课 →依赖缓存