如何在 GitLab CI 中将 SSH 私钥配置为 masked 变量?
完整的多行 OpenSSH 私钥不能直接设为 GitLab 的 masked 变量,因为掩码值必须是无空格的单行字符串。通常应将完整私钥保存在 File 类型变量中,并将 Visibility 设为 Visible。如果必须使用 masked 变量,可以保存完整私钥的单行 Base64 编码,但解码后的私钥不会因此自动受到掩码保护。GitLab 变量掩码说明
为什么删除换行后会报错?
ssh-add 需要读取可解析的私钥文件。如果把内容改成 myprivatekey==,并移除头尾标记和原有结构,就无法得到有效的 OpenSSH 私钥。
私钥末尾缺少 LF 换行也可能导致 error in libcrypto,不能仅凭这条报错确定唯一原因。应先恢复完整私钥内容,包括 -----BEGIN OPENSSH PRIVATE KEY-----、正文和结束标记,并在最后一行之后保留换行。GitLab 私钥错误排查
推荐配置:使用 File 类型变量
在项目的 Settings > CI/CD > Variables 中添加:
- Key:
SENTINEL_DB_RESET_SSH_KEY。 - Type:
File。 - Visibility:
Visible。 - Value:完整私钥,保留所有换行,结束标记后再按一次回车。
File 类型变量在 job 中的值是临时文件路径,因此可以保留原来的 chmod 和 ssh-add 用法。Visible 表示没有启用日志掩码,不能据此认为文件内容可以安全输出。GitLab SSH 私钥配置说明
还要预先核验服务器的 SSH host key,并将对应的 known_hosts 内容保存为另一个 File 类型变量 SSH_KNOWN_HOSTS。不要只依赖 job 运行时执行的 ssh-keyscan 来建立信任。GitLab 主机密钥校验说明
下面的示例假定容器中已有所需 SSH 工具、私钥可以非交互加载,且服务器已配置对应公钥:
reset_database:
stage: .pre
image: kroniak/ssh-client:latest
resource_group: portal_test_vm_lock
before_script:
- |
set +x
eval "$(ssh-agent -s)"
chmod 600 "$SENTINEL_DB_RESET_SSH_KEY"
ssh-add "$SENTINEL_DB_RESET_SSH_KEY"
mkdir -p ~/.ssh
chmod 700 ~/.ssh
cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
script:
- ssh -o BatchMode=yes -o StrictHostKeyChecking=yes "root@$PORTAL_INSTANCE" /path/to/script.sh "$CI_PIPELINE_ID"
示例修正了 YAML 缩进,并删除了原命令中重复的 ssh。实际使用时,还需确认变量的作用域允许当前 job 读取。
必须使用 masked 变量时:编码完整私钥
在可信的本地环境中,对完整私钥文件编码,并将结果保存到另一个文件:
base64 < ./id_ed25519 | tr -d '\r\n' > ./id_ed25519.b64
这里删除的是 Base64 输出中的换行,解码后仍会还原私钥内部原有的换行。不要先删除原始私钥的换行。
将 id_ed25519.b64 的内容保存为普通 Variable 类型变量,例如 SENTINEL_DB_RESET_SSH_KEY_B64,Visibility 选择 Masked,关闭变量引用展开。是否能保存仍以自托管实例的实际校验规则为准。
在前面示例的 before_script 中,用以下代码替换私钥加载部分,保留后面的 known_hosts 配置:
set +x
umask 077
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s' "$SENTINEL_DB_RESET_SSH_KEY_B64" \
| base64 -d > ~/.ssh/ci_private_key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/ci_private_key
rm -f ~/.ssh/ci_private_key
这段代码适用于支持 base64 -d 的 job 环境。Base64 是可逆编码,不是加密;本地生成的 .b64 文件也应按私钥管理,不能提交到仓库。
masked 能否保护解码后的私钥?
不能依赖它做到这一点。GitLab 掩码针对保存的变量值,不会自动解码后再匹配日志中的私钥。对编码值进行变换也可能使掩码无法匹配。GitLab 掩码限制
采用任一方案时,都应避免输出私钥文件,避免在处理私钥时开启 set -x 或调试跟踪,也不要把私钥放入 artifacts 或 cache。set +x 只能关闭 shell 命令跟踪,无法阻止程序主动打印文件内容。
如果启用 Protect variable,还要确认 job 所在分支或标签满足保护条件,否则变量可能不可用。masked 也无法阻止有权限运行的恶意脚本读取并发送私钥,因此仍需审查流水线变更。GitLab CI/CD 变量安全说明
备注:内容仅供参考。
