使用Terraform生成SSH密钥并自动添加至ssh-agent失败问题
问题:Terraform执行
ssh-add后密钥未加入ssh-agent 我用Terraform的tls_private_key资源生成ED25519格式的SSH密钥:
resource "tls_private_key" "cluster_key_private" { algorithm = "ED25519" }
接着通过null_resource的local-exec执行本地命令,尝试将密钥自动添加到ssh-agent密钥链:
resource "null_resource" "sshkey_add" { triggers = { always_run = "${timestamp()}" } provisioner "local-exec" { command = <<-EOT eval "$(ssh-agent -s)" echo '${tls_private_key.cluster_key_private.private_key_openssh}' | tr -d '\r' | ssh-add - EOT } }
但执行完成后,我在终端用ssh-add -L检查密钥列表,输出显示:
❯ ssh-add -L The agent has no identities.
为什么密钥没有成功添加到ssh-agent中?
原因分析与解决方案
核心原因
- 进程隔离导致作用域失效:
local-exec会启动一个独立的子shell进程,你在这个子shell里启动的ssh-agent和执行的ssh-add操作,仅在该子shell的生命周期内有效。当local-exec执行完毕,这个子shell就会退出,对应的ssh-agent进程要么被终止,要么当前终端的shell无法获取它的通信环境变量,自然看不到添加的密钥。 - 环境变量无法跨进程传递:ssh-agent启动后会输出
SSH_AUTH_SOCK和SSH_AGENT_PID两个关键环境变量,只有后续进程继承这些变量才能和该agent通信。但local-exec子shell的环境变量不会回传至你的终端shell,所以即使agent未立即终止,终端也无法连接到它。
可行解决方案
方案1:复用终端已有的ssh-agent
如果你的终端已经启动了ssh-agent(可以通过echo $SSH_AUTH_SOCK验证,有输出则说明已启动),直接去掉启动agent的命令,让ssh-add自动复用终端当前的agent:
resource "null_resource" "sshkey_add" { triggers = { always_run = "${timestamp()}" } provisioner "local-exec" { command = <<-EOT echo '${tls_private_key.cluster_key_private.private_key_openssh}' | tr -d '\r' | ssh-add - EOT } }
这样密钥会被添加到你终端正在使用的agent中,后续执行ssh-add -L就能看到。
方案2:写入本地文件后添加(更稳定)
将生成的私钥写入本地文件(注意设置正确的权限,SSH要求私钥权限必须为0600),再通过文件路径添加密钥,同时保留密钥文件方便后续使用:
resource "local_file" "ssh_private_key" { content = tls_private_key.cluster_key_private.private_key_openssh filename = "${path.module}/cluster_key" file_permission = "0600" } resource "null_resource" "sshkey_add" { triggers = { key_content = local_file.ssh_private_key.content } provisioner "local-exec" { command = "ssh-add ${local_file.ssh_private_key.filename}" } depends_on = [local_file.ssh_private_key] }
这种方式不依赖环境变量传递,直接指定文件路径添加,可靠性更高,同时私钥文件会被保存在当前Terraform模块目录下。
内容的提问来源于stack exchange,提问作者Joost Dübken
相关产品推荐
相关产品推荐

