如何用 GitHub Actions + 动态密码 实现全自动安全部署
如何用 GitHub Actions + 动态密码实现全自动安全部署
部署,是自动化的最后一公里,也是最容易出事的一步
自动化构建 CI 已经成了现代开发流程的标配,但在部署环节,很多人仍然在用:
- 明文密码
- SSH 密钥硬编码
- 凭证长期有效、难以轮换
这一切都让自动化部署变成一把双刃剑:一边提升效率,一边埋下隐患。
在上一篇文章中,我们介绍了一种基于 HMAC 和 UTC 时间戳构建的 一次性动态密码机制,它能在本地和服务器之间生成一致的临时口令,并定期失效,最大限度减少泄露风险。
这一篇,我们就来讲讲:如何把这套机制整合进 GitHub Actions 中,构建一条真正安全、零明文、自动化的部署链路。
总体思路:一套最小改动、最大安全收益的 CI 流程
我们期望实现的效果是:
- 每次 push 到主分支,自动构建项目
- 如果是正式发布(通过 commit message 判断),触发发布流程
- 使用 GitHub Actions 自动部署构建产物
- ✅ 整个过程不暴露任何明文密码
- ✅ 密码在上传结束后立即失效,构建机器也无法长期使用
GitHub Actions 的整体结构
- 动态生成密码(使用 HMAC + 时间戳命令)
- 写入临时文件
rsync.password
- 使用
rsync --password-file
上传构建文件 - 上传完成后清理临时密码文件
✅ 为了防止上传开始时正好跨过时间窗口(导致服务器验证失败),我们使用带有自动重试的 retry 逻辑,在失败后重新生成密码并重试上传。
CI 中动态生成密码的做法
我们不在 Secrets 中保存密码,而是保存生成密码的命令:
1 | echo "RSYNC_PASSWORD $(echo -n 'deploy_user' | \ |
CI 中这样提取:
1 | - name: Generate Rsync Password |
GitHub Secrets 本身是加密存储,但如果被误输出到日志或调试信息中,仍可能造成泄露。
使用动态密码机制,即使命令被泄露,生成的密码也只在几分钟内有效,且无法复用,
极大降低风险。
安全细节:自动清理、IP 隐私、防重放
我们在部署过程中引入了三项安全强化措施:
- 使用
sed
清洗日志中的 IP 输出,避免泄露服务器 IP:
1 | rsync ... 2>&1 | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/[REDACTED-IP]/g' |
- 时间窗口重试机制(Retry)
由于密码基于当前 UTC 时间窗口生成(如每 5 分钟一次),如果部署正好跨过窗口边界,可能导致密码失效。
为此我们使用 nick-fields/retry
自动重试,并在每次 retry 时重新生成一次性密码:
1 | - name: Deploy to Server |
在 retry 操作内我们重新生成了密码文件,并设置好文件权限,使用 set +x
关闭 Shell 输出,避免敏感命令打印到日志。
✅ 即使 CI 日志被误打印,密码也只在几分钟内有效,且每次部署独立,重放失败
- 成功上传后删除临时密码文件,避免密码文件泄露
1 | - name: Cleanup Password File |
完整示例结构
你可以设计如下的 Secrets:
RSYNC_ADDRESS
=user@host::module
RSYNC_PORT
=873
RSYNC_PASSWORD_COMMAND
= 上面那一整行生成命令
配合 .github/workflows/deploy.yml
中的判断逻辑,自动触发部署。
总结
这一篇我们实现了一个几乎零泄露风险的自动部署流程:
- 明文密码消失了 ✅
- 凭证只在当前构建生效 ✅
- 服务器也不需要任何认证服务端口额外配置 ✅
下一篇我们将介绍:如何用 commit message 自动触发版本发布、打 tag、发 Release、部署一整套流程,实现”一次提交,全部完成”的智能发布策略。