如何使用环境变量存储的SSH KEY执行git命令且不写入文件系统
不落地SSH私钥执行Git命令实现方案
核心逻辑是利用SSH的管道传参、临时内存态ssh-agent的能力,全程私钥仅在进程内存中传递,不写入任何磁盘文件,兼容带密码保护的私钥。
推荐方案:临时ssh-agent托管(适配带密码私钥)
这个方案启动一个仅当前代码块生命周期内有效的临时ssh-agent进程,通过管道把环境变量中的私钥、密码喂给agent加载,执行完Git命令后agent自动退出,内存中的密钥直接回收,无残留风险。
def run_git_with_env_ssh_key require 'open3' ssh_key = ENV.fetch('GIT_SSH_PRIVATE_KEY') ssh_key_pass = ENV['GIT_SSH_KEY_PASSPHRASE'] # 无密码可传nil # 启动临时ssh-agent进程 Open3.popen3('ssh-agent -s') do |_, stdout, _, _| # 解析agent输出的连接环境变量 agent_vars = {} stdout.read.scan(/([A-Z_]+)=([^;]+);/).each do |k, v| agent_vars[k] = v.delete_prefix('"').delete_suffix('"') end # 通过管道将私钥、密码传入ssh-add,全程不触盘 add_pid = Process.spawn( 'ssh-add', '-', in: :pipe, out: File::NULL, err: File::NULL, **agent_vars.transform_keys(&:to_sym) ) do |stdin| stdin.write(ssh_key) stdin.write("\n") stdin.write("#{ssh_key_pass}\n") if ssh_key_pass end Process.wait(add_pid) # 注入agent环境变量执行Git命令,按需替换成实际业务需要的git逻辑 system(agent_vars, 'git clone git@your-git-domain:org/repo.git /path/to/target') system(agent_vars, 'git -C /path/to/target pull origin main') end end # Rails中直接调用即可 run_git_with_env_ssh_key
轻量方案:进程替换直传密钥(适配无密码私钥)
如果私钥没有设置密码,可以不用启动ssh-agent,直接通过文件描述符把私钥传给SSH,通过GIT_SSH_COMMAND指定Git调用SSH的参数:
require 'shellwords' git_ssh_config = [ 'ssh', '-o StrictHostKeyChecking=accept-new', '-i /dev/fd/3', "3<<<#{Shellwords.escape(ENV['GIT_SSH_PRIVATE_KEY'])}" ].join(' ') system( { 'GIT_SSH_COMMAND' => git_ssh_config }, 'git clone git@your-git-domain:org/repo.git /path/to/target' )
带密码的私钥用这个方案需要额外配置SSH_ASKPASS脚本自动输入密码,流程更繁琐,优先选择第一个方案。
注意事项
- 不要为了省事把私钥写到临时文件再加载,即使写完立即删除,也存在进程异常退出残留文件、被其他高权限进程读取的风险
- 必须配置
StrictHostKeyChecking=accept-new,避免首次连接Git主机时SSH弹出交互确认导致流程卡住,不要直接关闭主机校验,会存在中间人攻击风险 - 生产环境记得在Rails的日志过滤配置中,把存储私钥、密钥密码的环境变量加入过滤列表,避免敏感信息泄露到日志中
- 不要全局配置
GIT_SSH_COMMAND或者全局启动ssh-agent,仅在执行Git命令的代码块内局部注入环境变量,避免影响应用其他逻辑的SSH请求
内容的提问来源于stack exchange,提问作者Kerby82
相关产品推荐
相关产品推荐

