在 Ubuntu 22.04 系统上为 SSH 开启基于时间的 TOTP 认证
前言
一次性密码(英语:one-time password,简称OTP),又称动态密码或单次有效密码,是指电脑系统或其他数字设备上只能使用一次的密码,有效期为只有一次登录会话或一段短时间内。基于时间的一次性密码算法(英语:Time-based One-Time Password,简称:TOTP)是一种根据预共享的密钥与当前时间计算一次性密码的算法。国内互联网服务的各种短信验证码的原理也与此相关。本次实践聚焦 SSH 远程连接场景下的 TOTP 认证,为已有的密钥认证机制再加强一下。
1. 安装 Google 的 PAM
PAM 意为可插入身份验证模块,是 Linux 系统上用于对用户进行身份验证的身份验证基础结构。因为 Google 制作了 OATH-TOTP 应用程序 Google Authenticator,所以他们还制作了一个可生成 TOTP 的 PAM,并且与任何 OATH-TOTP 应用程序完全兼容,例如 Google Authenticator 或 Microsoft Authenticator。
使用 apt 进行安装:
sudo apt install libpam-google-authenticator
安装 PAM 后,使用 PAM 附带的帮助程序应用程序为需要 OTP 认证的 SSH 用户生成 TOTP 密钥。该密钥是按用户生成的,因此多个用户需要操作多次。
运行帮助程序:
google-authenticator
运行命令后,应用程序会询问一些问题。第一个询问身份验证令牌是否应该基于时间:
Do you want authentication tokens to be time-based (y/n) y
此 PAM 允许基于时间或基于顺序的令牌。使用基于时间的令牌意味着代码会在特定时间范围后发生更改。选择基于时间的算法是因为这是 Google Authenticator 等应用程序所期望的,所以回答 y 。
运行完此命令后将输出一个验证码图像。使用任何 OATH-TOTP 应用程序进行扫描后将能够保存此验证码序列。
程序将要求输入在OATH-TOTP 应用程序中保存的验证码序列,也可以输入-1跳过:
Enter code from app (-1 to skip):
验证通过后程序将提示保存恢复码,以免丢失 OATH-TOTP 应用程序后无法登录:
Code confirmed
Your emergency scratch codes are:
01234567
01234567
01234567
01234567
01234567
之后程序会提示选择密钥保存文件:
Do you want me to update your "/root/.google_authenticator" file? (y/n) y
接下来还会有三个问题:
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
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
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
第一个问题是询问是否禁止多次使用同一身份码进行验证,选择是 y;
第二个问题是允许在移动的四分钟窗口内最多输入 17 个有效代码。如果回答“否”,则只允许 1:30 分钟的滚动窗口中限制输入 3 个有效代码,为了更好的安全性,选择否 n;
第三个问题是是否启用速率限制,这将限制攻击者每 30 秒尝试登录的次数不得超过 3 次,选择是 y。
至此安装完毕。
2. 配置 SSH 以使用 PAM 中的 OTP 模块
备份一下 PAM 配置文件:
sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak
修改配置:
sudo vim /etc/pam.d/sshd
将以下行添加到文件底部:
. . .
# Standard Un*x password updating.
@include common-password
# enable OTP auth
auth required pam_google_authenticator.so nullok
auth required pam_permit.so
第一行的 nullok 表示身份验证方法是可选的。这允许没有 OATH-TOTP 令牌的用户仍然仅使用 SSH 密钥登录。
如果用户不使用令牌登录,则需要使用第二行的 pam_permit.so 来允许其他方法的身份验证。
配置 SSH 以支持这种身份验证。先备份文件:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
修改配置:
sudo vim /etc/ssh/sshd_config
修改 ChallengeResponseAuthentication
或 KbdInteractiveAuthentication
选项的值将其设为 yes
:
. . .
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
. . .
重启 SSH 服务验证是否修改正确:
sudo systemctl restart sshd.service
3. 让 SSH 启用 OTP 验证
通常情况下,使用密钥进行身份验证会完全跳过基于 PAM 的身份验证,这就会使得使用密钥登录的用户在登录时会跳过 OTP 验证模块。
因此需要使用 AuthenticationMethods
指令,以允许启用多种身份验证方法:
sudo vim /etc/pam.d/sshd
. . .
AuthenticationMethods publickey,keyboard-interactive
保存文件并退出。
之后再次打开 PAM 配置文件
sudo vim /etc/pam.d/sshd
找到 @include common-auth 行并通过添加 # 字符作为该行的第一个字符将其注释掉。
这将使得通过 PAM 验证时不提示输入密码,只提示 OTP 验证码:
. . .
# Standard Un*x authentication.
#@include common-auth
. . .
保存并关闭文件,然后重新启动 SSH:
sudo systemctl restart sshd.service
4. 验证 OTP 验证机制
使用 Xshell 登录原有设备,通过公钥认证后将提示一个验证码弹窗:
输入从验证器应用中得到的验证码,即可正常登录。
附录
如果开启 OTP 验证的账户为 root
账户,则需要在 SSH 的配置文件 /etc/ssh/sshd_config
中允许 root
登录:
PermitRootLogin yes