使用双因素认证增强 SSH 安全性
我热情地尽可能使用双因素认证,因为静态密码并不是最好的护城河机制。传统密码容易受到社会工程学攻击、键盘记录器、黄色便利贴的影响,而且——尤其是在计算机变得越来越快的情况下——容易被破解。抛弃它们而支持双因素认证是一个好主意,并且可以让我晚上睡得更香。
不幸的是,基于网络的商业双因素系统通常过于昂贵和复杂,无法在家中或小型网络中使用。但是,猜猜怎么着?您的 Linux 计算机上已经拥有构建双因素认证系统所需的必要组件。无处不在的安全通信工具 OpenSSH 提供了创建基于主机的双因素认证系统所需的所有工具,该系统适用于家庭、小型办公室甚至更大的网络。
本文介绍了如何将可移动介质与 OpenSSH 公钥/私钥以及出色的 ssh-agent 程序结合使用,以实现普通用户和特权用户的双因素认证。
让我们首先为普通(非 root)用户创建双因素认证。在本例中,我们使用众所周知的 SSH 公钥认证工具,并进行了一些小的调整。我们不会像默认那样将私钥存储在您主目录的 .ssh 子目录中,而是将其放在 USB 闪存盘上。
在本示例中,您将以非特权用户 bob 身份登录到 Fedora Core 计算机 machine1。您将以 bob 身份连接到远程 Linux 计算机 machine2。
让我们首先创建我们将用于登录到 machine2 的公钥/私钥对
ssh-keygen -t rsa -f key-rsa-bob@machine2 -C key-rsa-bob@machine2
在提示时输入密码(密码越长越随机越好)。默认情况下,ssh-keygen 程序会在您主目录的 .ssh 子目录中创建密钥对——在本例中为 /home/bob/.ssh。对于本示例,我选择了一个任意但描述性的文件名,以便一目了然地识别目标用户和主机名;这在后续示例中很重要,这些示例使用多个密钥。(我假设 USB 驱动器已格式化为 Linux 文件系统,如 ext3;vfat 可以工作,但您需要在每次挂载后将密钥的文件权限更改为 400。)
挂载您的 USB 闪存盘,您应该看到它为 /media/usbdisk、/media/usbdisk1、/media/disk 或 /media/disk-1。将您新创建的私钥移动到相应的目录,并限制对所有者的访问
mv key-rsa-bob@machine2 /media/usbdisk chmod 400 /media/usbdisk/key-rsa-bob@machine2
接下来,将公钥 (key-rsa-bob@machine2.pub) 复制到 machine2 上的 /home/bob/.ssh/authorized_keys 文件中。使 authorized_keys 文件仅对所有者可读
chmod 400 authorized_keys
现在,您可以从 machine1 以 bob 身份登录到远程计算机 machine2,使用公钥/私钥对(-i 选项告诉 ssh 客户端要使用哪个密钥)
ssh -i /media/usbdisk/key-rsa-bob@machine2 bob@machine2
在提示时输入私钥密码,machine2 上的 OpenSSH 服务器将您登录。卸载并移除 machine1 上的 USB 设备(或可移动光盘),您的私钥受到保护。您已实现双因素认证:一个因素是存储在 USB 设备上的密钥,您可以将其与计算机分开存放,第二个因素是您存储在脑海中的密码。
使用 SSH 公钥认证对于许多人来说是一个常见且熟悉的过程。将私钥放在可移动介质上是将一个因素与另一个因素物理分离的简单方法。
示例 1 展示了如何使用 USB 设备将一个认证因素与另一个因素分离,从而安全地登录到远程计算机。这在以非特权用户身份登录时效果很好,但在以 root 身份登录时则不然。我们必须找到一种以超级用户身份远程登录的方法。
一种解决方案是简单地扩展前面示例的方法,并配置远程 OpenSSH 服务器以允许直接从网络进行 root 登录。不会有密码或密钥遍历网络,但我们会违反古老的系统管理禁令,即禁止直接以 root 身份登录。不应允许任何捷径,因此我们必须弄清楚如何首先以普通用户身份登录,然后再以 root 身份登录。
OpenSSH 再次来拯救我们。在本例中,我们继续使用公钥/私钥,但引入了一个配置上的变化。首先,配置远程 SSH 服务以允许通过内部环回接口进行 root 登录,但不允许通过外部网络进行 root 登录。其次,配置 ssh-agent 实用程序以允许远程计算机通过查询本地计算机上存储的密钥来认证 root。
以下是该过程的工作方式
在本地计算机上为 root 创建私钥/公钥对。
将公钥复制到远程计算机上 root 的 authorized_users 文件中。
在本地运行 ssh-add 实用程序以缓存私钥。
ssh到远程计算机并以普通用户身份登录,如示例 1 中所述;但是,这次使用代理转发选项。
在远程计算机上,ssh到 localhost 接口作为 root 用户。远程 OpenSSH 守护程序查询本地代理,认证 root,您可以以超级用户身份登录。
ssh-agent 实用程序提供了我们正在寻找的功能。它允许远程 SSH 守护程序通过查询本地存储的解密私钥缓存来认证用户。密钥永远不会在机器之间传输——私钥仍然存储在您本地工作站上的可移动介质上。
ssh-agent 功能强大,但设置起来可能很棘手。首先,您需要使用 ssh-add 实用程序来解密您的私钥并将其交给 ssh-agent。其次,您需要告诉 ssh-add 如何与 ssh-agent 通信。ssh-add 通过套接字与 ssh-agent 通信,套接字的位置存储在 SSH_AUTH_SOCK 环境变量中。默认情况下,ssh-agent 创建具有任意名称的套接字,并且正确设置 SSH_AUTH_SOCK 可能需要一些工作。
幸运的是,许多 Linux 发行版(包括 Fedora Core)在您以图形方式登录时(例如在 GNOME 或 KDE 上)会自动设置必要的 ssh-agent/ssh-add 连接。在控制台登录,打开终端控制台并键入以下内容
ssh-add -l
只要 ssh-add 可以与 ssh-agent 通信,您应该看到您的公钥列表或类似“The agent has no identities”的消息。
如果由于任何原因,ssh-agent 未运行或您的 SSH_AUTH_SOCK 变量未设置,或未正确设置,您将收到消息“Could not open a connection to your authentication agent”。在这种情况下,运行以下命令
eval `ssh-agent`
这将启动一个 ssh-agent 实例,并自动在您当前的 shell 中设置环境变量。
接下来,像第一个示例中那样为 root 创建密钥对
ssh-keygen -t rsa -f key-rsa-root@machine2 -C "key-rsa-root@machine2"
将私钥移动到可移动介质,并仅向所有者授予读取访问权限
mv key-rsa-root@machine2 /media/usbdisk chmod 400 /media/usbdisk/key-rsa-root@machine2
将公钥复制到远程计算机 machine2 上的 /root/.ssh/authorized_keys 文件中。
通过运行以下命令将 machine2 上 root 的私钥添加到 ssh-agent
ssh-add -t 300 /media/usbdisk/key-rsa-root@machine2
在提示时输入密码,当 ssh-agent 添加密钥时,它会返回消息“Identity added: key-rsa-root@machine2 (key-rsa-root@machine2)”。(-t 300 选项将缓存的生命周期限制为 300 秒或五分钟。如果您不指定生命周期,您的密钥将永远有效。)
以普通用户身份登录到远程计算机
ssh -A -i /media/usbdisk/key-rsa-bob@machine2
在提示时输入密码,您将登录到 machine2。(此命令与示例 1 中的命令相同,只是我们使用了 -A 选项,该选项启用代理转发。)
键入ssh-add -l在 machine2 上,您应该看到您刚刚添加到 ssh-agent 的 root 密钥。例如
2048 fa:5c:4b:73:88:26:..:... /media/usbdisk/key-rsa-root@machine2 (rsa)
下一步,su到 root(在 machine2 上),并配置 SSH 守护程序以允许在内部环回接口上进行 root 登录。编辑 /etc/ssh/sshd_config 文件并添加/修改以下选项
PermitRootLogin yes AllowUsers bob@* AllowUsers root@localhost.*
(某些 OpenSSH 配置要求您显式设置数字环回地址AllowUsers root@127.0.0.1.)
保存您的更改,然后重新启动 SSH 守护程序
service sshd restart
注销 root 帐户,并使用 OpenSSH 重新以 root 身份登录
ssh root@localhost
现在 machine2 上的 OpenSSH 守护程序接受在环回接口上进行的 root 登录,但不接受来自外部网络的 root 登录。它与 machine1 上的 ssh-agent 协商以认证您为 root 用户。root 的私钥永远不会离开 machine1!以这种方式使用 OpenSSH 可以有效地让您替换 su(切换用户)和 sudo 实用程序。
但是,我们还没有完全完成。您可以通过将 su 命令限制为本地连接的设备来进一步提高安全性。如下所示修改 /etc/pam.d/su,以防止任何人通过网络使用 su
auth required pam_securetty.so
su 命令仅在控制台和虚拟终端上有效。
卸载并移除您的 USB 设备。个人实际上必须在此刻偷走您的 USB 驱动器才能获得您的密钥。即使那样,他们也必须发现您的密码或花费大量的计算能力和时间来破解密钥。
在使用此系统在野外部署之前,我们需要消除一个潜在的漏洞。
使用 ssh-agent 和代理转发允许远程 SSH 服务器查询存储在您本地计算机上的私钥。但是,如果您使用此系统登录到多台计算机,则一台机器上的入侵者可能会劫持这些密钥以闯入另一台机器。在这种情况下,此系统可能比使用静态密码的系统更危险。
为了说明这个问题,让我们通过将 machine3 添加到组合中,将我们的示例网络从两个节点扩展到三个节点。如示例 1 和 2 中所述,在 machine3 上为 bob 和 root 创建密钥对,并将 root 的私钥添加到 machine1 上的 ssh-agent。
现在,ssh使用代理转发选项 -A 以 bob 身份登录到 machine3。运行ssh-add -l,您可以看到 machine2 和 machine3 的公钥
2048 fa:5c:4b:73:88:...: ... /media/usbdisk/key-rsa-root@machine2 (RSA) 2048 26:b6:e3:99:c1:...: ... /media/usbdisk/key-rsa-root@machine3 (RSA)
在本示例中,machine1 上的 ssh-agent 缓存了 machine2 和 machine3 的私钥。这单个代理允许我们以 root 身份登录到任一计算机。但是,使用单个代理也可能允许 machine2 上的入侵者以 root 身份登录到 machine3,反之亦然。这不好。
幸运的是,我们可以通过使用 ssh-add -c 选项来解决此问题;我们可以通过使用单独的 ssh-agent 实例来存储每台远程计算机的一个 root 密钥来增加额外的安全性。-c 选项告诉 ssh-agent 让用户确认每次使用缓存的密钥。为每个主机分配一个 ssh-agent 实例可防止任何尚未知的 ssh-agent 漏洞将一台机器的密钥暴露给另一台机器。
使用 ssh-add 确认选项很容易;只需在每次将密钥添加到 ssh-agent 时设置 -c 选项。让我们试一试。在 machine1 上启动两个代理,指定预定义的套接字
ssh-add -c /media/usbdisk/key-rsa-root@machine2 ssh-add -c /media/usbdisk/key-rsa-root@machine3
当您ssh到 machine2 和 machine3 时,系统会要求您确认密钥的使用。
您也可以使用单独的 ssh-agent 来存储每个密钥。让我们试一试;在 machine1 上启动两个代理,指定预定义的套接字
ssh-agent -a /tmp/ssh-agent-root@machine2 ssh-agent -a /tmp/ssh-agent-root@machine3
再次,我使用了一个任意但描述性的命名约定。设置环境变量,并添加 machine2 的密钥
export SSH_AUTH_SOCK=/tmp/ssh-agent-root@machine2 ssh-add -c /media/usbdisk/key-rsa-root@machine2
为 machine3 重复此过程,进行适当的替换
export SSH_AUTH_SOCK=/tmp/ssh-agent-root@machine3 ssh-add -c /media/usbdisk/key-rsa-root@machine3
现在,登录到 machine3(我们将在此时转到 machine3,因为我们刚刚将 SSH_AUTH_SOCK 变量设置为指向 machine3 的代理)
ssh -A -i /media/usbdisk/key-rsa-bob@machine2 bob@machine3
运行以下命令以查看您可以在 machine1 上查询哪些密钥
ssh-add -l
您只看到 machine3 上 root 的密钥。
退出 machine3,将环境变量更改为 machine2 ssh-agent 套接字,然后登录到 machine2
export SSH_AUTH_SOCK=/tmp/ssh-agent-root@machine2 ssh -A -i /media/usbdisk/key-rsa-bob@machine2 bob@machine2
再次检查您的密钥
ssh-add -l
在 machine2 和 machine3 上检查您的密钥仅显示该机器的 root 密钥。在前面的示例中,通过使用单个 ssh-agent,您将看到 machine2 和 machine3 的密钥。
为要登录的每台计算机使用单独的 ssh-agent 实例需要更多的工作。
每次要登录到另一台计算机时都重置 SSH_AUTH_SOCK 变量是不切实际的。为了简化此过程,我编写了一个简单的脚本 tfssh(双因素 ssh)来简化此过程。其语法为
tfssh [username@]host [keydir]
该脚本 [列表 1 位于 LJ FTP 站点,网址为 ftp.linuxjournal.com/pub/lj/listings/issue152/8957.tgz] 在必要时启动 ssh-agent,设置环境变量,将 root 密钥添加到 ssh-agent,并以用户身份登录到远程计算机。您还可以告诉 tfssh 在任意目录 ([keydir]) 中查找其密钥,并为密钥缓存设置密钥超时。
ssh-add
ssh-add 允许您锁定和/或确认使用私钥。使用 -x 和 -X 选项来锁定和解锁密钥。您将创建一个密码来锁定密钥,并使用该密码来解锁它。使用 -c 选项指示 ssh-add 在每次 ssh-agent 被要求使用密钥时提示您。提示显示在运行 ssh-agent 的计算机上,并有效防止未经授权的用户使用您的密钥。
静态密码正迅速变得弊大于利。我们需要打破静态习惯,开始使用双因素认证。OpenSSH 是一个强大的系统,它提供了实现这一步骤所需的工具。通过使用公钥/私钥、代理转发和可移动介质,我们可以将 OpenSSH 用作密钥“保险箱”。反过来,这使我们能够创建一个简单、廉价且有效的基于主机的双因素认证系统。
这个双因素系统需要花费适量的工作来配置和使用,但它非常值得额外的安全性。但是,使用 tfssh 脚本可以使该过程易于使用。使用该脚本意味着您可以获得双因素认证的所有好处,但几乎没有麻烦。
双因素与 2.X 因素
有些人将本地存储的 SSH 密钥及其密码视为两个因素。这种观点是合理的,但我感觉将密钥存储设备与计算机物理分离更舒适。将密钥保存在可移动介质上可以减少入侵者捕获和破解它们的可能性。
现在,重要的是要意识到将密钥保存在 USB 闪存盘等设备上并不能消除入侵者窥探它们的能力。您的密钥在挂载时很容易受到攻击,您应该采取预防措施来加强您从中连接到其他计算机的工作站的安全性。为本地(控制台)登录使用强密码,保持您的工作站已打补丁等等。
因此,只要您充分保护您的工作站,使用公钥认证比使用静态密码更好。您想有多安全取决于您的偏执程度。
Paul Sery 担任 UNIX 和 Linux 系统管理员超过 20 年。他撰写了几本 Linux 书籍,包括 Network Linux Toolkit 和 Knoppix for Dummies。他还与 Jon “maddog” Hall 合著了几本 Red Hat Linux for Dummies 和 Fedora Core for Dummies 书籍。Paul 居住在新墨西哥州阿尔伯克基,可以通过 pgsery@swcp.com 与他联系。