agent 转发并不等于 agent 本身。本地 agent 可以替客户端完成认证操作,而不把私钥交给服务器;转发则进一步在远端开放一个套接字,让那边的程序也能请求这项服务。因此,「私钥材料留在本地」和「身份认证能力只留在本地」是两个不同的承诺。
选择堡垒机访问方式时,这个区别尤其重要。如果只是要登录内网机器,通常用 ProxyJump 并关闭 agent 转发就够了。只有程序确实需要在远端发起认证,例如一条经过明确授权的后续 SSH 连接,才需要考虑把 agent 的使用能力交过去。不要把网络可达性和身份委托混成一个开关。
远端拿到的是能力
转发出来的套接字通常通过远端的 SSH_AUTH_SOCK 暴露。能访问它的进程,包括拥有足够权限的本机攻击者,都可以向原始 agent 请求操作。这不意味着服务器上的任何普通账户都有权限,更不意味着私钥文件已经被复制过去;风险来自可以调用身份,而不是必须先偷到密钥。
一把没有额外约束的已加载密钥,在转发通道仍然可用时,可能被拿去完成你没有打算批准的登录。密钥的存活期、逐次确认、目标限制,以及目标服务器是否接受这个身份,都会改变请求能否成功。把它说成「完整拥有私钥」不够准确,把它说成「只是一只无害套接字」同样会误导操作判断。
旧版本曾把一枚没有解释来源的八十八字节签名当作证据。单看长度,既不能证明实验实现了 OpenSSH agent 协议,也不能证明一次经过转发的认证成功。下面改用真正的 SSH 客户端,验证一个范围明确、读者可以复现的性质:最终生效的转发选项。这个检查不冒充真实网络集成测试。
转发由生效配置决定
命令里没有 -A,并不代表关闭了转发。匹配的 Host 段、被包含的配置文件,或者系统配置,都可能已经把它打开。反过来,即使文件写着 ForwardAgent no,命令行上的 -A 仍会显式启用转发。需要关闭时,应当使用 -a,或者没有相互矛盾选项的 -o ForwardAgent=no。
ProxyJump 提供到目标的 TCP 路径,并不会顺便重置目标连接的转发选项。目标 SSH 连接及其认证仍从本地发起,所以单纯借道跳板机不需要把 agent 暴露过去。目标和跳板机的配置也不一定一样,应分别检查相应别名,不要把某一条 ssh -G 的输出理解成整条路径的所有设置。
如果已经进入远端 shell,再运行一次 SSH,那台机器上的客户端会读取它自己的配置。判断哪一跳继续转发,要看每次调用的有效设置,不能数历史命令里出现过几次 -A。排查已有连接时还要留意连接复用:打印当前配置描述的是这次拟议调用,不能反推所有既有主连接的运行状态。
缩小委托范围
ssh-add -c 通过 askpass 程序请求使用确认,不能只因为请求方没有终端,就断言确认必然失败。ssh-add -t 可以限制身份留在 agent 中的时间。自 OpenSSH 8.9 起提供的目标约束依据已知主机密钥识别主机,经过转发路径使用时还需要客户端和服务端配合支持。协作路径上的每一跳都须获准;恶意节点仍可用其他工具再次中继套接字,但签名仍受获准目的地限制,不能把路径约束当作绝对防中继机制。
服务端设置 AllowAgentForwarding no,可以关闭 OpenSSH 自带的转发设施,但不能阻止拥有不受限制 shell 的用户另行搭建转发器。这个限制有配置卫生价值,却不能单独隔离一个恶意 shell 用户。仍然应该把服务器信任关系和可执行程序的权限纳入判断。
坏掉的样子
堡垒机被攻陷后,攻击者可能在转发通道和相应身份仍可用的窗口里滥用 agent。在磁盘上换掉私钥文件,并不会自动卸载 agent 内存里的旧身份。处置时应终止转发连接、移除相关的已加载身份;如果风险需要,再撤销服务器接受该身份的授权。已经建立的远程会话不会因此自动消失,需要另外处理。
另一个常见问题是把 ForwardAgent yes 放进 Host * 后忘记它。最好只为确实需要的主机设置例外,并让用于转发的 agent 只包含必要身份。给某次登录指定 IdentityFile,并不会把整个转发 agent 过滤成只有那一把钥匙。登录时选什么身份,和远端能向 agent 请求什么,是两个不同层次的问题。
快速验证
下面的一次性配置打印四项设置,供人工比较,不连接网络或读取私钥。代码没有值断言,也没有失败与清理陷阱;示例域名仅用于配置匹配:
f=$(mktemp)
printf 'Host *\n ForwardAgent no\n' > "$f"
ssh -G -F "$f" target.invalid | grep '^forwardagent '
ssh -G -F "$f" -A target.invalid | grep '^forwardagent '
printf 'Host *\n ForwardAgent yes\n' > "$f"
ssh -G -F "$f" -J bastion.invalid target.invalid | grep '^forwardagent '
ssh -G -F "$f" -a -J bastion.invalid target.invalid | grep '^forwardagent '
rm "$f"
保留的 OpenSSH 10.0p2 记录报告顺序为 no、yes、yes、no;本轮没有重新执行。这验证了选项处理,不验证套接字权限、签名操作、目标约束,也没有建立真实的多跳连接。检查可信配置时,可以查看 ssh -G destination 和各跳板别名;但 Match exec 求值可执行本地命令,主机名规范化也可能触发 DNS,不能把任意配置检查都当作无副作用。每一个 yes 都应该能回答两个问题:为什么需要转发,以及接收方为什么值得信任。
来源
- OpenBSD:ssh,命令行转发开关、跳板连接与配置检查,已核验 2026-10-15。
- OpenBSD:ssh_config,ForwardAgent、ProxyJump 与配置优先级,已核验 2026-10-15。
- OpenBSD:ssh-agent,agent 套接字与无需传输私钥的认证方式,已核验 2026-10-15。
- OpenBSD:ssh-add,askpass 确认、存活期与目标约束的支持条件,已核验 2026-10-15。
- OpenBSD:sshd_config,AllowAgentForwarding 与 shell 访问下的限制,已核验 2026-10-15。