agent 转发并不等于 agent。转发是请远端主机上的 sshd 自己开一个套接字,并把那边的 SSH_AUTH_SOCK 指过去,于是那台机器上的程序可以回头找你家里的 agent 签名。远程自始至终没有拿到密钥,它也根本不需要拿到:它拿到的是这份能力的完整使用权。

这是穿过堡垒机抵达内网机器的常规做法,同时也意味着路径上每一跳都分到了这份能力。多数伸手去拿 -A 的人,真正想要的是 ProxyJump——它只转发 TCP,把 agent 留在本机。

两者容易被混为一谈,是因为它们用的是同一套协议、同一个 SSH_AUTH_SOCK 变量,区别只在于那只套接字落在哪台机器上。agent 留在本机,它就是你的私钥保险箱;套接字出现在远端,它就是一张那台机器上任何程序都能使用的通行证。

远程拿到的是套接字,不是密钥

ssh_config(5) 对这笔交易毫不含糊。ForwardAgent 接受 yes、no(默认值)、一个明确的套接字路径,或某个环境变量的名字;手册警告说,能够在远端绕过该 agent 套接字文件权限的用户,可以经由这条转发连接访问你本地的 agent。更要紧的是紧接着的那句:攻击者无法从 agent 取得密钥材料,但可以对密钥执行操作,从而用已装载的身份完成认证。

本文的实测正好呈现了这个形状。一个只拿到 SSH_AUTH_SOCK、既没有密钥文件也没有口令的第三方进程,列出了身份并拿到了一枚 88 字节的签名。这就是转发交给某一跳的全部能力。

转发是逐跳选择的

能签名的机器集合,恰好就是那些以启用转发的方式运行 ssh 的跳数。ssh -A bastion 把 agent 暴露给 bastion;从那里再 ssh -A build-box,build-box 也一并拿到;而 ssh build-box 不带 -A,链条就止步于 bastion。没有任何一跳会意外地级联下去,但也没有任何东西会提醒你。

这一点值得反复强调:转发不是连接本身的属性,而是一个按次传入的开关。你在哪一条命令上写了 -A,就在哪一段链路上开了口子;没写的那一段,agent 不会自己跟过去。因此「这条链路谁能用我的身份」这个问题,答案就是那串命令里出现过几次 -A。

ProxyJump 是更安全的形状。ssh_config(5) 对它的描述是:先与指定的跳板主机建立一条 ssh 连接,再从那里向最终目标建立 TCP 转发,以此连上目标主机。跳板机转发的是数据包而不是签名,所以 agent 从不离开工作站;ssh -G 也确认,-o ProxyJump=bastion 之后 forwardagent 仍然是 no。

服务端能管和管不了的事

在服务端,配置里什么都不写时 sshd -T 会报 allowagentforwarding yes,写上 AllowAgentForwarding no 才会变成 no。手册在这里补了一句值得读两遍的警告:除非同时禁止用户获得 shell 访问,否则关掉 agent 转发并不能提升安全性,因为他们总可以自己装一个转发器。把它当成卫生习惯,而不是一道边界。

真正有效的是把身份收窄。ssh-add -c 要求每次使用都确认,而一次没有 tty 可以回答提示的签名请求被拒绝了。ssh-add -h 'bastion>prod-db' 把密钥约束到特定的来源与目的主机对上,并把整条路径(含转发)逐跳拿去比对。ssh-add -t 则限定了这一切还能持续多久。

坏掉的样子

最经典的一种是堡垒机被攻陷。谁占住那台机器,谁就继承了你的身份,时限等于 agent 还装着这把钥匙的时间:他们可以用它登录这把钥匙能开的每一台主机,还可以借这条转发继续摸到你从未打算开放的下游机器。在磁盘上轮换密钥打断不了它,因为 agent 内存里那份还是旧的,而且照样在签。要判断自己是否处在这种风险里,只需问一句:路径上有没有哪台机器的管理员权限不在我手里?只要有,这条链路上的 agent 转发就应当被当作已经泄露来对待。

更安静的一种是把转发全局打开。一条 Host * 配 ForwardAgent yes,会把每一次连接都变成一次授权,包括连到同事测试机的那一次;而出现在远端的那只套接字归你在那边的账户所有,同时那台机器上的 root 也能访问。转发应当是按主机做的决定,而且不该比需要它的那项任务活得更久。

快速验证

先弄清楚你到底授权出去了什么:

ssh -G prod-db | grep -iE '^(forwardagent|proxyjump)'
grep -rin forwardagent ~/.ssh/config
ssh-add -l
ssh -o ForwardAgent=no -J bastion,build-box prod-db
sshd -T | grep -i allowagentforwarding

第一行打印的是生效后的取值,所以在一台你并不掌控的主机上看到 forwardagent yes,就是本次要查的问题;最后一条在服务端执行,用来看服务端到底允不允许转发。ssh-add -l 里应当只列出这项任务需要的身份,因为装载进去的每一个都会随转发一起被授权出去。

来源

  • OpenBSD:ssh_config,ForwardAgent 及其警告,以及 ProxyJump 从跳板机做 TCP 转发的语义,已核验 2026-09-16。
  • OpenBSD:sshd_config,AllowAgentForwarding 的默认值,以及为何关掉它并不构成边界,已核验 2026-09-16。
  • OpenBSD:ssh-add,-c 逐次确认、-t 存活期,以及针对转发密钥的 -h 目的主机约束,已核验 2026-09-16。