关注了就能看到更多这么棒的文章哦~
Restricting SSH agent keys
By Jake Edge
January 5, 2022
DeepL assisted translation
https://lwn.net/Articles/880458/
OpenSSH 这一系列工具是用于安全地进行远程登录的,在我们的社区中已经在广泛使用了。它也是远程访问 Git 仓库等功能的基础。最近,即将发布的 OpenSSH 8.9 中有了一个实验性的功能,希望有助于填补一个安全漏洞。当用户在把 authentication 转发给本地的 ssh-agent 时,攻击者控制的 SSH 服务进程(例如 sshd)就有机会利用这个漏洞。原本 SSH agent 里保存的这些安全密钥会被用来跟任何 host 来进行认证,现在 SSH agent restriction 则允许用户指定这些密钥可以用在什么地方,以及如何使用。
ssh-agent 是用来简化对同一 host 进行重复连接的场景。它会存储和管理 SSH 密钥,这样就不需要在每次连接时输入保护密钥的口令(passphrase)了。通常情况下,至少需要用 passphrase 口令来解锁一次密钥,然后就使用 ssh-add 操作时会被 agent 保存起来。此外也可以用 ssh_config 里的 AddKeysToAgent 选项来实现相同目的。ssh-agent 是一个 "专门设计得尽量简单的程序",因为它会持有私钥。Damien Miller 对这个新功能的描述是这样的:
它采用的是一个简单的、由 client 来发起的协议,带有少量的操作,包括添加或删除密钥、从加载的密钥来检索出一系列的公钥,以及最重要的功能是用私钥来进行签名。跟 agent 进行交互大多数是通过 ssh-add 工具来完成的,包括添加、删除和列举密钥,此外还有 ssh 工具会使用 agent 中持有的密钥来进行用户认证,但也可以支持其他工具,只要它们都遵守这个协议就行。
由于 agent 中持有非常重要的密钥信息,因此它是 "一个经常被攻击者利用的、理想的攻击对象"。agent 只能从本地系统来访问到,这就大大地限制了攻击面(attach surface),除非对该 agent 的访问专门被转发(forward)到远端系统。使用 ssh 的 -A 选项(或 ForwardAgent 这个 config 选项)就能让远程 host 能够与本地 agent 通信。然后,该远程 host 就跟本地程序一样可以发起所有 agent 相关操作了。
这种代理转发(agent forwarding)通常是用来进行多跳 SSH 连接的(multi-hop SSH connections),这个过程中不再需要多次重新输入 passphrase 来解锁远端 host 上的密钥。这也意味着私钥不需要存储在远程 host 上。当一个用户远程连接到 HostA,并从 HostA 来使用相同的 SSH 密钥连接到一个或多个其他 host 的时候,就会了解到与 HostA 进行初始 SSH 连接就启用 agent-forwarding 的话就会非常方便。从 HostA 发起的 SSH 连接都可以继续扩大 agent-forwarding 的范围。
当把 agent 的访问转发到一个攻击者控制的系统时,问题就出现了,这个恶意系统可以使用存储在 agent 中的密钥来认证并登入这个密钥所支持的任何其他 host 了。因此,用户可能对 HostA、HostB 和 HostC 使用了 forwarding 功能,但他们的密钥也会授予他们对 HostV 或 HostZ 的访问权,也许这两个就是攻击者希望访问的目标了。目前,SSH 没有办法限制代理所持有的密钥是如何使用的,这就是这里的新功能希望要解决的问题。
解决方案的一部分就是将对于 agent 的本地访问以及远程访问区分开,因此,即使 agent 的访问权被转发出去,那么一些密钥也只能是在从本地访问时才可以获取。其实将这两种类型访问 agent 的操作混为一谈是很久以前所做的一个错误决定,所以现在可以添加密钥时限制其使用方式的话就可以帮助改善这个问题。作者为 ssh-add 新增了一个 -h 选项,用来描述一个密钥的合法用途,正如功能描述中一个例子所说的:
新增的功能使得用户可以为他们所添加到 ssh-agent 中的密钥来增加一个 destination 限制,并让 ssh 能确保施行这些限制。比如这个命令:
$ ssh-add -h "perseus@cetus.example.org" \
-h "scylla.example.org" \
-h "scylla.example.org>medea@charybdis.example.org" \
~/.ssh/id_ed25519所添加的密钥只能在以下情况之下用来进行认证:
从原始 host 发起的到 scylla.example.org 的人和用户的连接。
从原始 host 的用户 perseus 发起的到 cetus.example.org 的连接。
从 scylla.example.org 的用户 medea 发起的到 charybdis.example.org 的连接。
试图利用这个密钥来对其他 host 进行认证的话,就会被 agent 拒绝,因为不在上面明确列出的访问清单里面。比如试图通过 scylla.example.org 来对 cetus.example.org 进行认证就是不允许的。同样,试图登录为其他用户名,然后想访问 cetus.example.org 的 perseus 用户或 charybdis.example.org 的 medea 用户也都会失败,因为不符合指定的用户。
还可以指定更复杂的路径,但其中每一个跳板都需要在其 -h 选项中列出。所以一个多级跳板的路径可能看起来像这样:
$ ssh-add -h "HostA" \
-h "HostA>HostB" \
-h "HostB>HostC" \
key-file
需要注意的是,像上面这样配置的 agent 就不会再允许使用 agent 里的密钥来直接从本地系统访问到 HostB 或 HostC,也就是它们只能通过指定的跳板才能到达了。然而,用户仍然能够绕过 agent,通过在 ssh 提示时输入密钥的 passphrase 的方式来直接进入 HostB 或 HostC。
要使用这个新功能的话就需要更新客户端工具,但也需要更新远程系统上的 SSH 服务。agent 的协议也需要改变,才能在认证请求中加上服务端的 host 密钥,所以旧版本的 SSH 服务就无法参与到这个新的方案里面了。如果 ssh-add 或 SSH 服务端不支持这个 agent restriction 功能的话,该功能也将采用 "fail safe (安全后备)" 方案。如果 ssh-add 不能理解对于访问目标的限制条件,它会直接失败,agent 也就会拒绝这些没有发送方的 host 密钥的认证请求。
这里还有一些注意事项。其中最大的问题是,攻击者仍然可以劫持 agent connection,因此他们可以使用不符合预期要求的 host 来请求对已经授权的 host(和用户)进行认证:
有个不太明显的问题,那就是他们还能将 agent 的访问转发给其他 host,例如通过使用不能配合 ssh-agent 的 SSH 实现版本,或者完全是使用另一种工具比如 socat。请注意,攻击者在这里并没有获得任何新的密钥,他们仍然需要被迫通过被攻击的 host 来做事,他们仍然被限制在只能访问到那些只允许在指定 host 上使用的密钥。
[……]因为有这些微妙之处,所以最好把密钥限制功能看作是允许“经过”某个特定 host 来使用密钥,而不是“从”指定的 host,而且,更广泛地来说的话,任何一条 forward 链路的强度完全取决于链路中最薄弱的那个环节。还有另一种理解密钥限制功能的方法,那就是每一个密钥限制都代表把一个密钥委托给了一个 host,可信程度上只比委托者略高一点。
总的来说,这似乎是 SSH 工具中的一个会受到欢迎的补充。其所提供的限制功能肯定会有用的。很高兴知道 agent forwarding 功能不再会能够访问到这个密钥可能会访问到的所有主机。介绍该功能的文档非常详细,令人钦佩,其中包括了实施细节、未来的计划等等。鼓励感兴趣的读者自己也看看。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~