如何在 CentOS 8 上为 SSH 设置多重身份验证

作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。

介绍

SSH 默认使用密码进行身份验证,大多数 SSH 强化说明建议改用 SSH 密钥然而,SSH 密钥仍然只是一个因素,虽然是一个更安全的因素。通道是您计算机上的终端,通过加密隧道将数据发送到远程机器。但是就像黑客可以猜出密码一样,他们可以窃取 SSH 密钥,然后在任何一种情况下,攻击者都可以使用该单条数据访问您的远程系统。

在本教程中,我们将设置多因素身份验证来解决这个问题。多因素身份验证(MFA) 或双因素身份验证(2FA) 需要不止一个因素来进行身份验证或登录。这意味着不良行为者必须妥协多项内容,例如您的计算机和手机,才能进入。那里身份验证中使用的几种类型的因素:

  1. 知道的东西,比如密码或安全问题
  2. 拥有的东西,例如身份验证器应用程序或安全令牌
  3. 什么,比如你的指纹或声音

一个常见因素是 OATH-TOTP 应用程序,例如 Google 身份验证器。OATH-TOTP(Open Authentication Time-Based One-Time Password)是一种开放式协议,可生成一次性使用密码,通常是每 30 秒循环一次的六位数字。

除了 SSH 密钥之外,本文还将介绍如何使用 OATH-TOTP 应用程序启用 SSH 身份验证。通过 SSH 登录您的服务器将需要跨两个通道的两个因素,从而使其比单独的密码或 SSH 密钥更安全。此外,我们还将介绍 MFA 的一些其他用例以及一些有用的提示和技巧。

如果您正在寻求有关保护 SSH 连接的进一步指导,请查看有关强化 OpenSSH强化 OpenSSH 客户端的这些教程

先决条件

要学习本教程,您需要:

  • 一台带有 sudo 非 root 用户和 SSH 密钥的 CentOS 8 服务器,您可以按照此初始服务器设置教程进行设置
  • 安装了 OATH-TOTP 应用程序的智能手机或平板电脑,例如 Google 身份验证器(iOSAndroid)。
  • 或者,您也可以使用名为“oathtool”的 Linux 命令行应用程序来生成 OATH-TOTP 代码。它在各种发行版存储库中可用
  • 如果您真的想要保护您的 SSH 连接,这篇SSH Essentials文章中概述了几个很好的步骤,例如将用户列入白名单、禁用 root 登录以及更改 SSH 使用的端口。

第 1 步 — 安装 Google 的 PAM

在这一步中,我们将安装和配置 Google 的 PAM。

PAM 代表Pluggable Authentication Module,是一种在 Linux 系统上用于对用户进行身份验证的身份验证基础结构。因为谷歌制作了一个 OATH-TOTP 应用程序,所以他们也制作了一个生成 TOTP 的 PAM,并与任何 OATH-TOTP 应用程序完全兼容,如 Google Authenticator 或Authy

首先,添加 EPEL(企业 Linux 的额外包)存储库:

  • sudo yum search epel

如果您的存储库具有 EPEL 安装包,您将看到以下输出:

Output
===== Name Matched: epel ===== epel-release.noarch : Extra Packages for Enterprise Linux repository configuration

现在安装epel-release软件包以启用 EPEL 存储库:

  • sudo yum install epel-release

但是,如果您没有 package epel-release,那么您可以手动安装存储库信息:

  • sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm

接下来,安装 PAM。如果这是第一次使用 repo,可能会提示您接受 EPEL 密钥。接受后,将不会再次提示您接受密钥:

  • sudo yum install google-authenticator qrencode-libs

安装 PAM 后,我们将使用 PAM 附带的帮助应用程序为要添加第二个因素的用户生成 TOTP 密钥。此密钥是在逐个用户的基础上生成的,而不是在系统范围内生成的。这意味着每个想要使用 TOTP 身份验证应用程序的用户都需要登录并运行助手应用程序以获取他们自己的密钥;您不能只运行一次就为每个人启用它(但本教程末尾有一些提示,可以为许多用户设置或要求 MFA)。

运行这两个命令来初始化应用程序:

  • google-authenticator -s ~/.ssh/google_authenticator

通常,您需要做的就是google-authenticator不带参数运行命令,但 SELinux 不允许 ssh 守护程序写入.ssh主文件夹中目录之外的文件。这会阻止身份验证。

SELinux 是保护您的系统免受潜在攻击的强大工具,值得在强制模式下运行。因此,关闭 SELinux 不被视为最佳实践。相反,我们会将 google_authenticator 文件的默认位置移动到您的~/.ssh目录中。

运行命令后,系统会询问您几个问题。第一个询问身份验证令牌是否应该基于时间:

Output
Do you want authentication tokens to be time-based (y/n) y

此 PAM 允许基于时间或基于顺序的令牌。使用基于顺序的令牌意味着代码从某个点开始,然后在每次使用后递增代码。使用基于时间的令牌意味着代码在特定时间范围后发生变化。我们将坚持基于时间,因为这是像 Google Authenticator 这样的应用程序所期望的,所以回答y是。

回答完这个问题后,很多输出会滚动过去,包括一个大的二维码。使用手机上的身份验证器应用程序扫描二维码或手动输入密钥。如果二维码太大无法扫描,您可以使用二维码上方的 URL 获取较小的版本。添加后,您会在应用中看到每 30 秒更改一次的六位数代码。

注意:确保将密钥、验证码和紧急刮刮码记录在安全的地方,例如密码管理器。例如,如果您无法访问 TOTP 应用程序,紧急刮刮码是重新获得访问权限的唯一方法。

其余问题告知 PAM 如何运作。我们将一一介绍:

Output
Do you want me to update your "~/.google_authenticator" file (y/n) y

这会将密钥和选项写入google_authenticator文件。如果您说不,程序将退出并且没有写入任何内容,这意味着身份验证器将无法工作:

Output
Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) y

通过在此处回答“是”,您可以通过使每个代码在使用后立即过期来防止重放攻击。这可以防止攻击者捕获您刚刚使用的代码并使用它登录:

Output
By default, a new token is generated every 30 seconds by the mobile app. In order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. This allows for a time skew of up to 30 seconds between authentication server and client. If you experience problems with poor time synchronization, you can increase the window from its default size of 3 permitted codes (one previous code, the current code, the next code) to 17 permitted codes (the 8 previous codes, the current code, and the 8 next codes). This will permit for a time skew of up to 4 minutes between client and server. Do you want to do so? (y/n) n

在这里回答是允许在移动的四分钟窗口中最多有 17 个有效代码。通过回答“否”,您可以在 1:30 分钟的滚动窗口内将其限制为 3 个有效代码。除非您发现 1:30 分钟窗口有问题,否则回答“否”是更安全的选择。如果您回答“否”并且后来意识到您需要更多时间,则可以在.google_authenticator存储在您的主目录根目录文件中调整此设置

Output
If the computer that you are logging into isn't hardened against brute-force login attempts, you can enable rate-limiting for the authentication module. By default, this limits attackers to no more than 3 login attempts every 30s. Do you want to enable rate-limiting (y/n) y

速率限制意味着远程攻击者只能尝试一定数量的猜测,然后被迫等待一段时间才能再次尝试。如果您之前没有直接在 SSH 中配置速率限制,现在这样做是一种很好的强化技术。

完成此设置后,如果您想备份您的密钥,您可以将~/.ssh/google-authenticator文件复制到受信任的位置。从那里,您可以将其部署到其他系统上或在全新安装后重新部署。请注意,通过在多台计算机上使用相同的密钥,如果攻击者能够嗅探到一台服务器的令牌,然后在有效窗口内针对另一台服务器使用它,则您将引入重放攻击的可能性。您必须评估该风险的可能性,而不是让每个系统使用不同的令牌并管理这些不同的令牌。

注意:如果您使用加密的主文件夹,这超出了本文的范围,您可能需要将~./google-authenticator文件存储在主文件夹之外的目录中。项目README详细说明了如何做到这一点。

由于我们将配置文件存储在非标准位置,因此我们需要根据其新位置恢复 SELinux 上下文。

使用以下命令执行此操作:

  • restorecon -Rv ~/.ssh/

完成这两项更改后,我们现在已经安装并配置了 Google Authenticator PAM,下一步是配置 SSH 以使用您的 TOTP 密钥。我们需要告诉 SSH 有关 PAM 的信息,然后配置 SSH 以使用它。

步骤 2 — 配置 OpenSSH 以使用 MFA/2FA

由于我们将通过 SSH 进行 SSH 更改,因此永远不要关闭初始 SSH 连接非常重要。相反,打开第二个 SSH 会话进行测试。这是为了避免在 SSH 配置中出现错误时将自己锁定在服务器之外。一旦一切正常,您就可以安全地关闭任何会话。另一个安全预防措施是创建您将编辑的系统文件的备份,以便万一出现问题,您可以简单地恢复到原始文件并使用干净的配置重新开始。

首先,备份sshd配置文件,然后对其进行编辑。在这里,我们使用的nano是默认情况下未安装在 CentOS 上的 。您可以使用 安装它sudo yum install nano,或者使用您最喜欢的替代文本编辑器。

备份文件,然后打开它:

  • sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak
  • sudo nano /etc/pam.d/sshd

将以下行添加到文件末尾:

/etc/pam.d/sshd
auth       required     pam_google_authenticator.so secret=/home/${USER}/.ssh/google_authenticator nullok
auth       required     pam_permit.so

由于我们必须将 google_authenticator 配置文件放在非标准位置,因此我们必须向 PAM 提供配置文件的路径。secret选项告诉 PAM 配置文件的非默认位置存储在哪里。

nullok行末尾单词告诉 PAM 此身份验证方法是可选的。这允许没有 OATH-TOTP 令牌的用户仍然只使用他们的 SSH 密钥登录。一旦所有用户都拥有 OATH-TOTP 令牌,您可以nullok从此行中删除以强制执行 MFA。

pam_permit.so如果我的用户不使用 MFA 令牌登录则需要第二行以允许身份验证。登录时,每种方法都需要 SUCCESS 才能进行身份验证。如果用户不使用 MFA 身份验证工具,则使用nullok为交互式键盘身份验证返回 IGNORE选项。因此,如果该行被忽略,则下一行将触发,pam_permit.so返回 SUCCESS 并允许进行身份验证。

保存并关闭文件。

接下来,我们将配置 SSH 以支持这种身份验证。

备份 SSH 配置文件,然后打开它进行编辑:

  • sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
  • sudo nano /etc/ssh/sshd_config

寻找开头的行ChallengeResponseAuthentication注释掉该no行并取消注释该yes行。

您的文件将如下所示:

/etc/ssh/sshd_config
. . .
# Change to no to disable s/key passwords
ChallengeResponseAuthentication yes
#ChallengeResponseAuthentication no
. . .

保存并关闭文件,然后重新启动 SSH 以重新加载配置文件。重新启动sshd服务不会关闭我们当前打开的连接,因此您不会冒险使用以下命令锁定自己:

  • sudo systemctl restart sshd.service

要测试到目前为止一切正常,请打开另一个终端窗口并尝试通过 SSH 登录。保持当前 SSH 会话处于打开状态并使用其他会话进行测试非常重要,否则您将在某些时候将自己锁定,并且需要使用 Web 控制台让自己重新进入。

注意:如果您之前创建了一个 SSH 密钥并正在使用它,您会注意到您不必输入用户密码或 MFA 验证码。这是因为默认情况下 SSH 密钥会覆盖所有其他身份验证选项。否则,您应该会收到密码和验证码提示。

接下来,要启用 SSH 密钥作为一个因素并启用验证码作为第二个因素,我们需要告诉 SSH 使用哪些因素并防止 SSH 密钥覆盖所有其他类型。

第 3 步 – 让 SSH 了解 MFA

重新打开sshd配置文件:

  • sudo nano /etc/ssh/sshd_config

在文件底部添加以下行。这会告诉 SSH 需要哪些身份验证方法。这一行告诉 SSH 我们需要一个 SSH 密钥和一个密码或一个验证码(或所有三个):

/etc/ssh/sshd_config
. . .
AuthenticationMethods publickey,password publickey,keyboard-interactive

保存并关闭文件。

接下来,sshd再次打开 PAM配置文件:

  • sudo nano /etc/pam.d/sshd

找到auth substack password-auth文件顶部的行通过添加一个#字符作为该行的第一个字符来注释掉它这告诉 PAM 不要提示输入密码:

/etc/pam.d/sshd
. . .
#auth       substack     password-auth
. . .

保存并关闭文件,然后重新启动 SSH:

  • sudo systemctl restart sshd.service

现在尝试使用不同的终端会话/窗口再次登录服务器。与上次不同,SSH 应该要求您提供验证码。输入后,您将登录。即使您没有看到任何表明您的 SSH 密钥已被使用的迹象,您的登录尝试也使用了两个因素。如果要验证,可以-v在SSH命令后添加(详细):

Example SSH Output
. . . debug1: Authentications that can continue: publickey debug1: Next authentication method: publickey debug1: Offering RSA public key: /Users/sammy/.ssh/id_rsa debug1: Server accepts key: pkalg ssh-rsa blen 279 Authenticated with partial success. debug1: Authentications that can continue: keyboard-interactive debug1: Next authentication method: keyboard-interactive Verification code:

在输出结束时,您将看到 SSH 在哪里使用您的 SSH 密钥,然后要求输入验证码。您现在可以使用 SSH 密钥和一次性密码通过 SSH 登录。如果要强制执行所有三种身份验证类型,可以按照下一步操作。

当您通过 SSH 远程登录到您的服务器时,您现在已经成功添加了第二个因素。如果这是您想要的——使用您的 SSH 密钥和 TOTP 令牌为 SSH 启用 MFA(对于大多数人来说,这是最佳配置)——那么您就完成了。

下面是一些恢复、自动使用等方面的提示和技巧。

第 4 步 – 添加第三个因素(可选)

在第 3 步中,我们在sshd_config文件中列出了批准的身份验证类型

  1. publickey (SSH 密钥)
  2. password publickey (密码)
  3. keyboard-interactive (验证码)

尽管我们列出了三个不同的因素,但到目前为止我们选择的选项只允许使用 SSH 密钥和验证码。如果您希望拥有所有三个因素(SSH 密钥、密码和验证码),只需进行一次快速更改即可启用所有三个因素。

打开 PAMsshd配置文件:

  • sudo nano /etc/pam.d/sshd

找到您之前#auth substack password-auth注释掉的行,然后通过删除该#字符来取消注释该行保存并关闭文件。现在再次重启 SSH:

  • sudo systemctl restart sshd.service

通过启用该选项auth substack password-auth,PAM 现在除了检查 SSH 密钥和要求验证码之外,还会提示输入密码,我们之前已经使用过。现在我们可以通过两个不同的渠道(您的计算机用于 SSH 密钥,您的手机用于 TOTP 令牌)使用我们知道的东西(密码)和我们拥有的两种不同类型的东西(SSH 密钥和验证码)。

步骤 5 — 恢复对 Google MFA 的访问权限(可选)

与您加固和保护的任何系统一样,您有责任管理该安全性。在这种情况下,这意味着不会丢失您的 SSH 密钥或 TOTP 密钥,并确保您可以访问您的 TOTP 应用程序。但是,有时会发生一些事情,您可能会失去对需要进入的密钥或应用程序的控制。

无法访问密钥

如果您丢失了 TOTP 密钥,恢复可以分为几个步骤。第一个是在不知道验证码的情况下返回,第二个是找到密钥或为正常 MFA 登录重新生成它。如果您获得一部新手机并且不将您的秘密转移到新的身份验证器应用程序,则通常会发生这种情况。

要在 DigitalOcean Droplet 上丢失 TOTP 密钥后进入,您只需使用仪表板中的虚拟控制台,使用您的用户名和密码登录。这是有效的,因为我们仅使用 MFA 保护您的用户帐户以进行 ssh 连接。非 ssh 连接(例如控制台登录)不使用 Google Authenticator PAM 模块。

如果您使用的是非 Droplet 系统,那么您有两种方法可以重新获得访问权限:

  1. 对系统的控制台(本地/非 ssh)访问(通常是物理访问或通过 iDrac 之类的东西)
  2. 有一个没有启用 MFA 的不同用户

第二个选项是不太安全的选项,因为使用 MFA 的目的是加强所有 ssh 连接,但如果您无法访问 MFA 身份验证器应用程序,这是一种故障安全。

登录后,有两种方法可以获取 TOTP 密钥:

  1. 恢复现有密钥
  2. 生成新密钥

在每个用户的主目录中,密钥和 Google 身份验证器设置保存在文件 ~/.ssh/google-authenticator. 该文件的第一行是一个密钥。获取密钥的一种快速方法是执行以下命令,该命令会显示google-authenticator文件的第一行(即密钥)。然后,获取该密钥并手动将其输入到 TOTP 应用程序中:

  • head -n 1 /home/sammy/.ssh/google_authenticator

恢复现有密钥后,您可以手动将其输入到您的身份验证器应用程序中,然后您应该恢复营业,或者您可以在下面的 URL 中填写相关详细信息并让 Google 生成一个二维码供您扫描. 您需要添加您的用户名、主机名、google-authenticator文件中的密钥,然后添加您为“entry-name-in-auth-app”选择的任何名称,以便轻松识别此密钥与不同的 TOTP 令牌:

https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/username@hostname%3Fsecret%3D16-char-secret%26issuer%3Dentry-name-in-auth-app

如果有理由不使用现有密钥(例如,无法轻松安全地与受影响的用户共享密钥),您可以~/.ssh/google-authenticator彻底删除该文件。这将允许用户仅使用单个因素再次登录,假设您没有通过删除nullok“/etc/pam.d/sshd”文件中选项来强制执行 MFA 然后它们可以运行google-authenticator以生成新密钥。

无法访问 TOTP 应用程序

如果您需要登录服务器但无权访问 TOTP 应用程序以获取验证码,您仍然可以使用首次创建密钥时显示的恢复代码登录。请注意,这些恢复代码是一次性使用的。使用一次登录后,不能再次作为验证码使用。希望您将它们保存在一个可以访问的地方,如果您没有 TOTP 应用程序但仍然安全。

步骤 6 — 更改身份验证设置(可选)

如果您想在初始配置后更改 MFA 设置,而不是使用更新的设置生成新配置,您只需编辑~/.ssh/google-authenticator文件即可。此文件按以下方式布局:

.google-authenticator 布局
<secret key>
<options>
<recovery codes>

在这个文件中设置的选项在选项部分有一行;如果您在初始设置期间对特定选项回答“否”,则相应行将从文件中排除。换句话说,该文件仅包含启用的选项。如果某个选项不存在,则默认情况下它是禁用的。

以下是您可以对此文件进行的更改:

  • 要启用顺序代码而不是基于时间的代码,请将行更改" TOTP_AUTH" HOTP_COUNTER 1
  • 要允许多次使用单个代码,请删除该行" DISALLOW_REUSE
  • 要将代码过期窗口延长至 4 分钟,请添加行" WINDOW_SIZE 17
  • 要禁用多次失败登录(速率限制),请删除该行" RATE_LIMIT 3 30
  • 要更改速率限制的阈值,请找到该行" RATE_LIMIT 3 30并调整数字。3原始指示尝试在一段时间的数,和30表示时间(秒)的时间段。
  • 要禁用恢复代码,请删除文件底部的五个 8 位代码。

第 7 步 – 避免对某些帐户进行 MFA(可选)

可能存在单个用户或几个服务帐户(即应用程序使用的帐户,而不是人类使用的帐户)需要 SSH 访问而不启用 MFA 的情况。例如,某些使用 SSH 的应用程序(如某些 FTP 客户端)可能不支持 MFA。如果应用程序无法请求验证码,则请求可能会卡住,直到 SSH 连接超时。

只要/etc/pam.d/sshd正确设置了几个选项,您就可以控制在逐个用户的基础上使用哪些因素。

要允许某些帐户使用 MFA 而仅允许其他帐户使用 SSH 密钥,请确保以下设置处于/etc/pam.d/sshd活动状态:

/etc/pam.d/sshd
#%PAM-1.0
auth       required     pam_sepermit.so
#auth       substack     password-auth

. . .

# Used with polkit to reauthorize users in remote sessions
-session   optional     pam_reauthorize.so prepare
auth       required      pam_google_authenticator.so nullok

此处auth substack password-auth注释掉,因为需要禁用密码。如果某些帐户打算禁用 MFA,则不能强制 MFA,因此将nullok选项留在最后一行。

设置此配置后,只需google-authenticator以需要 MFA 的任何用户身份运行,不要为仅使用 SSH 密钥的用户运行它。

步骤 8 — 使用配置管理自动设置(可选)

许多系统管理员使用配置管理工具(如 Puppet、Chef 或 Ansible)来管理他们的系统。如果您想使用这样的系统在创建新用户帐户时安装密钥,有一种方法可以做到这一点。

google-authenticator支持命令行开关以在单个非交互式命令中设置所有选项。要查看所有选项,您可以键入google-authenticator --help以下是按照步骤 1 中概述的方式设置所有内容的命令:

  • google-authenticator -t -d -f -r 3 -R 30 -w 3 -s ~/.ssh/google_authenticator

上面引用的选项如下:

  • -t => 基于时间的计数器
  • -d => 禁止令牌重用
  • -f => 强制将设置写入文件而不提示用户
  • -r => 尝试输入正确代码的次数
  • -R => 用户可以尝试输入正确代码的时间(以秒为单位)
  • -w => 一次可以有多少有效代码(这指的是有效代码的 1:30 分钟 – 4 分钟窗口)
  • -s => 应存储身份验证文件的路径

这些回答了我们手动回答的所有问题,将其保存到文件中,然后输出密钥、二维码和恢复码。(如果您添加 flag -q,则不会有任何输出。)如果您确实以自动方式使用此命令,请确保捕获密钥和/或恢复代码并使它们可供用户使用。还要记住让您的自动化重置“.ssh”目录 ( restorecon -Rv ~/.ssh/)的 SELinux 上下文

步骤 9 — 为所有用户强制 MFA(可选)

如果您想在第一次登录时对所有用户强制执行 MFA,或者如果您不想依赖您的用户生成他们自己的密钥,那么有一种简单的方法来处理这个问题。您可以简单地google-authenticator为每个用户使用相同的文件,因为文件中没有存储特定于用户的数据。

为此,在最初创建配置文件后,特权用户需要将该文件复制到.ssh每个主目录的目录中,并将其权限更改为适当的用户。您还可以将文件复制到/etc/skel/ 以便在创建时自动复制到新用户的主目录。

警告:这可能存在安全风险,因为每个人都共享相同的第二个因素。这意味着如果它被泄露,就好像每个用户只有一个因素。如果您想使用这种方法,请考虑这一点。

另一种强制创建用户密钥的方法是使用 bash 脚本:

  1. 创建一个 TOTP 令牌,
  2. 提示他们下载 Google Authenticator 应用程序并扫描将显示的二维码,以及
  3. google-authenticator在检查google-authenticator文件是否已存在后为他们运行应用程序

为确保脚本在用户登录时运行,您可以为其命名并将其.bash_login放置在其主目录的根目录中。

结论

在本教程中,您跨两个通道(您的计算机 + 您的手机)向您的服务器添加了两个因素(一个 SSH 密钥 + MFA 令牌)。您使外部代理很难通过 SSH 强行进入您的机器,并大大提高了您机器的安全性。

请记住,如果您正在寻求有关保护 SSH 连接的进一步指导,请查看有关强化 OpenSSH强化 OpenSSH 客户端的这些教程

觉得文章有用?

点个广告表达一下你的爱意吧 !😁