服务器暴露在公网上,每天被脚本扫描几千次是常态。密码再复杂也扛不住暴力破解、撞库、0day 组合拳。给 SSH 加上 TOTP 二次验证(也就是 Google Authenticator 那套 6 位动态码),相当于给大门上了第二把锁——即使密码泄露,没有手机上的码也进不来。

本文覆盖三种场景,从简单到严格,你自己选:


一、基础方案:密码 + TOTP 二次验证

适合个人服务器,保留密码登录,每次额外输一次 6 位验证码。

1.1 安装 PAM 模块

sudo apt update
sudo apt install -y libpam-google-authenticator

验证安装:

ls /lib/security/pam_google_authenticator.so

有输出即成功。

1.2 为当前用户生成密钥

务必用你要登录的普通用户执行,不要用 root。

google-authenticator

它会依次提问,全部选 y

问题选 y 的含义
Time-based tokens?使用基于时间的验证码(TOTP)
Update ~/.google_authenticator?保存密钥到用户目录
Disallow multiple uses?同一验证码只能用一次
Tokens valid for 30 seconds?窗口期内是否允许前后偏移一次(选 y 更安全)
Enable rate-limiting?限制每 30 秒最多尝试 3 次

执行完成后,终端会显示两样东西:

  • 二维码 — 用手机 Google Authenticator 扫描
  • 紧急备用码(backup codes)一定要抄下来保存好,手机丢了靠这个救命

1.3 配置 SSH PAM

sudo nano /etc/pam.d/sshd

在文件末尾添加一行:

auth required pam_google_authenticator.so

保存退出(Ctrl+O → Enter → Ctrl+X)。

1.4 开启 SSH ChallengeResponse

sudo nano /etc/ssh/sshd_config

确保以下两项为 yes

ChallengeResponseAuthentication yes
UsePAM yes

Ubuntu 24.04 中 ChallengeResponseAuthentication 已重命名为 KbdInteractiveAuthentication,两者作用相同。如果有 KbdInteractiveAuthentication 项也一并确认开启。

保存后重启 sshd:

sudo systemctl restart sshd

1.5 手机端配置

手机安装 Google Authenticator(App Store / 各应用商店可搜到)→ 点 + → 扫描二维码 → 出现 6 位数字,每 30 秒变一次。

1.6 测试登录

新开一个终端窗口:

ssh user@你的服务器IP

会依次提示:

Password:          # 输入密码
Verification code: # 输入手机上的 6 位数字

两步都通过即登录成功。测试完不要关掉当前会话,确保一切正常再断开。


二、进阶方案:仅指定用户启用 2FA

如果服务器上有多个用户,但只想给特定账号(比如你常用的管理员用户)加上二次验证,可以用 pam_succeed_if 做条件判断。

2.1 需要 2FA 的用户各自生成密钥

每个需要开启 2FA 的用户自己登录执行:

google-authenticator
# 全部选 y,保存备用码,手机扫码

2.2 修改 PAM 配置加判断规则

sudo nano /etc/pam.d/sshd

注释掉之前的全局启用行,添加条件判断:

# auth required pam_google_authenticator.so    # 注释掉这一行(全局启用)

# 仅 user1、user2 启用二次验证,其余用户跳过
auth [success=1 default=ignore] pam_succeed_if.so user in user1:user2
auth required pam_google_authenticator.so nullok

user1:user2 换成你要开启 2FA 的用户名,冒号分隔

nullok 参数的作用:如果名单内的用户还没执行 google-authenticator 生成密钥,暂时跳过验证,不会直接锁住。

2.3 确认 SSH 配置并重启

sudo nano /etc/ssh/sshd_config
# 确保有:
# KbdInteractiveAuthentication yes
# UsePAM yes

sudo systemctl restart sshd

2.4 效果

  • 名单内用户登录:密码 + 6 位验证码
  • 名单外用户:只输密码,直接登录

三、最优方案:SSH 密钥 + TOTP 双因子(无密码)

这是生产服务器的推荐配置——彻底禁用密码登录,必须同时持有 SSH 私钥 + 手机 TOTP 验证码才能登录。暴力破解直接无效。

3.1 编辑 SSH PAM

sudo nano /etc/pam.d/sshd

清空原有 2FA 配置,写入:

# 关闭密码登录认证
auth       required     pam_deny.so

# 开启 2FA 动态验证码校验
auth       required     pam_google_authenticator.so

3.2 修改 SSH 主配置

sudo nano /etc/ssh/sshd_config

设置以下参数:

# 启用键盘交互验证(2FA 必备)
KbdInteractiveAuthentication yes

# 启用 PAM 模块
UsePAM yes

# 允许密钥登录
PubkeyAuthentication yes

# 彻底禁用账号密码登录(核心)
PasswordAuthentication no
ChallengeResponseAuthentication no

3.3 限制仅指定用户可登录(推荐)

sshd_config 末尾添加:

AllowUsers 你的用户名1 用户名2

3.4 精准区分:指定用户走密钥+2FA,其他人拒绝

修改 /etc/pam.d/sshd

# 仅指定用户走 2FA 流程
auth [success=1 default=deny] pam_succeed_if.so user in root:ubuntu
auth required pam_google_authenticator.so

root:ubuntu 换成你的用户名,冒号分隔。不在列表里的用户直接被 default=deny 拒绝,连密钥认证的机会都没有。

3.5 重启生效

sudo systemctl restart sshd

3.6 客户端登录流程

ssh 用户名@服务器IP

过程:

1. SSH 私钥自动校验(通过)
2. 弹出提示 → Verification code:
3. 输入手机 6 位动态码
4. 登录成功

没有私钥 → 连接被拒绝 有私钥但没有验证码 → 卡在第二步进不去


四、关键说明:2FA 只拦 SSH,不影响网站

这是很多人配置时的最大顾虑——「我开了 SSH 二次验证,会不会把网站搞挂了?」

完全不会。 2FA 只拦截远程 SSH 登录这一个入口,和网站运行是两个完全独立的层面。

场景会不会受影响原因
游客访问网页不受影响走 nginx 端口(80/443),不经过 sshd
API 接口调用不受影响同样走 nginx,和 SSH 无关
网站后台登录不受影响属于网站自身登录,和 SSH 二次验证彻底分离
www-data / nginx / apache 用户运行进程不受影响系统服务用户不需要 SSH 登录
PHP/Python 程序进程不受影响正常运行,无任何权限拦截
SFTP / rsync 同步不受影响走 SSH 子系统的不需要 2FA(取决于配置)

实际做法是:用 AllowUserspam_succeed_if 把 2FA 限制在运维账号(如 rootwebadmin),系统服务用户(www-datanginx 等)压根没有 SSH 登录的必要,也不需要生成 2FA 密钥。示例:

# 仅允许运维账号 SSH 登录
AllowUsers root webadmin

# PAM 中仅这两个账号需要 2FA
auth [success=1 default=ignore] pam_succeed_if.so user in webadmin:root
auth required pam_google_authenticator.so nullok

简单说:2FA 只拦人,不拦网站。


五、应急方案(手机丢失 / 验证码失效)

手机丢了或验证码一直不对:

  1. 通过服务器本地控制台(VPS 商家的 Web VNC / IPMI)登录账号
  2. 删除 2FA 配置文件:
rm ~/.google_authenticator
  1. 删除后即可直接登录(绕过验证)
  2. 重新执行 google-authenticator 绑定新手机

备份码的作用:手机丢了但还有服务器 SSH 会话没断的话,可以用备份码登录。但最稳妥的方案永远是去控制台删文件。


六、常见问题

Q:只想给部分用户启用 2FA? → 用方案二的 pam_succeed_if 条件判断。

Q:root 也想启用? → root 登录后执行 google-authenticator,其他步骤一样。

Q:备份码和手机都丢了? → 去服务器本地控制台删 ~/.google_authenticator 文件,重新绑定。

Q:重启 sshd 后连不上了? → 通过 VPS 商家提供的 Web VNC / 控制台登录排查,最常见原因是 pam.d/sshd 配置语法错误。

Q:已经有用户在登录中,配置会不会踢掉他们?sshd 重启不影响已建立的连接。但你测试时一定要保留一个已登录的会话,新窗口测试通过再退出。


七、总结

三种方案对比如下:

方案密码SSH 密钥TOTP 验证码适合场景
基础方案个人服务器、低风险
进阶方案(指定用户)✅(仅指定用户)多人服务器、分权限
最优方案(推荐)生产服务器、云服务器

对于面向公网的服务器,最优方案是唯一合理的选择——密码是最大的攻击面,关掉它就关掉了 99% 的脚本扫描。

如果你对 Google Authenticator 的隐私顾虑(它会上传数据),推荐替代品:

  • Aegis(Android,开源,本地存储)
  • Raivo OTP(iOS,开源)
  • Bitwarden(内置 TOTP,全平台)
  • 2FAS(全平台,开源)

以上 App 都兼容 Google Authenticator 的 TOTP 标准,扫描同一个二维码即可。


原文发表于 cn-res.vip