1. 项目概述
最近在梳理团队服务器的安全基线,发现一个普遍但危险的现象:很多同事还在用“用户名+密码”这种单一方式登录生产环境的SSH服务器。一旦密码泄露或者被暴力破解,整个服务器就门户大开。为了堵上这个安全漏洞,我决定给所有关键服务器部署SSH双因素认证(2FA),核心方案就是Google Authenticator(GA)的TOTP动态验证码。这个方案的好处是,登录时除了要知道密码,还得有手机上那个30秒刷新一次的6位动态码,两者缺一不可,安全性直接提升一个量级。
我花了几天时间,在CentOS、Rocky Linux和Ubuntu几个主流系统上都折腾了一遍,也把Xshell、PuTTY、MobaXterm这几个最常用的SSH客户端都测通了。过程中踩了不少坑,比如PAM模块顺序搞反导致登录循环失败,或者客户端配置不对永远只弹一个密码框。这篇文章就是我这趟“填坑之旅”的完整记录,目标很明确: 让你看完就能一次配通,不走弯路 。无论你是运维工程师、开发者还是个人站长,只要你的服务器需要更安全的SSH访问,这套方案都值得你花半小时部署上。
2. 核心原理与登录流程拆解
在动手之前,我们必须搞清楚SSH双因素认证到底是怎么工作的。很多教程只给命令,不说原理,结果一出问题就抓瞎。我把整个流程拆解成几个核心组件,你理解了它们之间的关系,后面排错就会非常轻松。
2.1 核心组件分工与协作
整个2FA体系不是靠一个软件完成的,而是多个系统组件协同工作的结果。我们可以把它想象成一道需要两道关卡的门卫系统。
-
Google Authenticator PAM模块 (
libpam-google-authenticator) : 这是安装在Linux服务器上的“第一道门卫”。它的职责单一且明确:校验用户输入的6位动态验证码是否正确。它本身不生成验证码,只负责验证。验证码的“种子”(Secret)保存在每个用户家目录下的一个隐藏文件(~/.google_authenticator)里。这个模块通过PAM(可插拔认证模块)框架与系统集成。 -
PAM配置文件 (
/etc/pam.d/sshd) : 这是“门卫的工作手册”。它定义了SSH登录时,需要依次调用哪些PAM模块进行认证,以及它们的顺序。 顺序是这里的命门 。我们必须确保先调用Google Authenticator模块(检查验证码),再调用系统密码认证模块。如果顺序反了,整个流程就乱套了。 -
SSH服务端配置 (
/etc/ssh/sshd_config) : 这是“门的通信协议”。它告诉SSH守护进程(sshd):“我们这里登录要走‘键盘交互’模式,别走简单的密码模式了。”关键参数是KbdInteractiveAuthentication yes和PasswordAuthentication no。前者开启复杂的多轮问答式认证,后者关闭简单的单次密码认证通道,强制所有密码登录都走PAM流程。 - SSH客户端 (Xshell/PuTTY等) : 这是“访客”。它必须支持并正确配置“键盘交互”认证模式。如果客户端错误地只使用“密码”认证,它会直接尝试把密码发送给服务器,而服务器此时期待的是验证码,对话根本对不上,必然导致“Access denied”。
- 手机验证器App (Google Authenticator/Microsoft Authenticator等) : 这是“动态口令生成器”。它根据和服务器共享的“种子”(Secret),结合当前时间,每30秒生成一个新的6位码。它和服务器上的PAM模块通过基于时间的TOTP算法保持同步。
2.2 一次成功的登录交互时序
理解了组件,我们来看一次成功的登录过程中,客户端和服务器到底在“聊”什么。这是排查问题的关键参照。
- 客户端发起SSH连接,服务器说:“我支持键盘交互认证。”
- 客户端回应:“好的,就用键盘交互。”
-
服务器通过PAM发起第一轮询问(根据PAM配置,这是Google Authenticator模块在提问):
Verification code:。有些系统可能提示语仍是Password:,但 顺序是第一位的 。 - 用户必须在第一个输入框里,输入手机App上当前的6位动态码。
-
服务器验证动态码正确后,PAM流程继续,发起第二轮询问(系统密码模块在提问):
Password:。 - 用户在第二个输入框里,输入Linux系统的用户密码(如root密码)。
- 密码验证通过,登录成功。
这里有一个99%的新手都会踩的坑 :当第一个框弹出时,如果你习惯性地输入了系统密码,服务器会用验证码的逻辑去校验你的密码,当然对不上,于是立刻返回“Access denied”,并且 不会 给你第二次输入的机会。这就是为什么很多人配置完后,一直登录失败,却搞不清原因。记住这个顺序: 先验证码,后系统密码 。
2.3 架构示意图与数据流
为了更直观,我们可以看下数据是如何流动的:
[手机 App] [SSH 客户端] [Linux 服务器]
TOTP 6位码 ──键盘交互──► Xshell/PuTTY ──SSH:22──► sshd
(每30秒刷新) (先验证码) (仅启用 (UsePAM yes)
(后密码) Keyboard-Interactive) └──► PAM栈
1. pam_google_authenticator.so
2. pam_unix.so (密码验证)
这个流程图清晰地展示了:动态码从手机产生,通过客户端以“键盘交互”模式传输给服务器端的SSH,SSH再交给PAM处理,PAM则严格按照配置的顺序,先过GA验证码关,再过系统密码关。
3. 服务器端配置:五步一次成功指南
理论说完,开始实战。服务器端的配置是基础,务必按顺序操作。 强烈警告:在修改PAM和SSH配置前,务必通过VNC、控制台或另一个已建立的SSH会话保持一个有效的root连接。 这是你的“救命通道”,万一配置出错导致所有SSH登录被锁,你还能通过这个通道修复。
3.1 步骤一:预处理SELinux(针对RHEL/CentOS/Rocky/AlmaLinux)
SELinux是增强安全模块,但有时会阻止PAM模块写入用户家目录的配置文件。在内网测试或学习环境,为了快速通过,可以临时禁用。生产环境建议配置正确的SELinux上下文。
# 临时将SELinux设置为宽松模式(Permissive),当前生效
setenforce 0
# 永久禁用SELinux(重启后生效),编辑配置文件
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
# 查看当前SELinux状态
getenforce # 应该显示 Permissive 或 Disabled
注意 :对于Ubuntu/Debian系统,它们使用AppArmor,通常不需要此步骤,除非遇到特定权限错误。
3.2 步骤二:安装必要的软件包
我们需要安装两个包:
google-authenticator
(PAM模块)和
qrencode
(用于生成二维码,方便手机扫描)。
-
RHEL/CentOS/Rocky/AlmaLinux 系列 :
# 确保EPEL仓库已启用(提供额外软件包) yum install -y epel-release # 安装核心软件 yum install -y google-authenticator qrencode -
Ubuntu/Debian 系列 :
apt update apt install -y libpam-google-authenticator qrencode
3.3 步骤三:为每个SSH用户生成密钥并绑定手机
这是关键一步,需要
在每个需要启用2FA的用户下执行
。如果你用root用户登录,那就是给root配置;如果用普通用户
webadmin
登录,就要切换到
webadmin
用户下执行。
切忌用root用户替普通用户生成密钥文件
,因为密钥文件会保存在执行命令的用户家目录下。
# 假设我们为root用户配置
# 首先,可选择性删除旧的配置文件(如果是重新配置)
rm -f ~/.google_authenticator
# 执行配置向导
google-authenticator
执行命令后,会进入一个交互式配置向导。以下是我推荐的配置选项,兼顾安全与便利:
| 提示问题 | 推荐选择 | 说明与理由 |
|---|---|---|
Do you want authentication tokens to be time-based (y/n)
| y | 选择基于时间的TOTP模式,这是主流标准,与手机App兼容。 |
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?
| y | 重要 :禁止同一令牌重复使用。防止攻击者截获一个验证码后重复使用。 |
By default, tokens are good for 30 seconds... Do you want to enable so-called “rate-limiting” (y/n)
| y | 启用速率限制。30秒内最多允许3次登录尝试,有效抵御暴力破解。 |
Do you want to enable rate-limiting (y/n)
| y | 同上,确认启用。 |
配置完成后,终端会显示一个大大的 二维码 ,以及一行“Your new secret key is:”开头的文本(一串Base32编码的密钥)。同时会给出几个紧急备用码(Emergency Scratch Codes), 务必妥善保存这些备用码 ,万一手机丢失,可以用它们登录。
现在,立刻打开你手机上的验证器App(Google Authenticator、Microsoft Authenticator、Authy等都可以),扫描屏幕上的二维码,或者手动输入那串密钥。 你的手机App里就会新增一个条目,开始每30秒刷新一个6位码。
最后,检查生成的密钥文件:
ls -la ~/.google_authenticator
文件权限应该是
400
或
600
,属主是当前用户。这是PAM模块能正常读取的关键。
3.4 步骤四:配置PAM认证栈(核心中的核心)
这是整个配置中最容易出错的一步。文件路径是
/etc/pam.d/sshd
。我们需要修改它,告诉系统在SSH登录时,如何调用认证模块。
核心原则:顺序必须是
pam_google_authenticator.so
在前,系统密码认证在后。
-
针对 RHEL/CentOS/Rocky/AlmaLinux 8+ 系统 : 这些系统通常使用
password-auth子栈来管理密码认证。我们需要在子栈调用之前插入GA模块。# 备份原始文件是个好习惯 cp /etc/pam.d/sshd /etc/pam.d/sshd.bak # 使用vim或nano编辑 /etc/pam.d/sshd # 在文件靠前的位置(通常在 `auth substack password-auth` 这一行之前),添加下面这行: auth required pam_google_authenticator.so添加后,相关部分看起来应该像这样:
#%PAM-1.0 auth required pam_sepermit.so auth required pam_google_authenticator.so # <-- 我们添加的行 auth substack password-auth auth include postlogin ... 其他内容 ... -
一个至关重要的排查点 : 很多RHEL系系统的默认PAM配置中,包含下面这样一行:
auth requisite pam_succeed_if.so uid >= 1000 quiet这行的意思是“只有UID大于等于1000的用户才允许通过此认证”。而root用户的UID是0, 这行规则会直接阻止root用户通过SSH登录! 如果你需要root也能SSH登录,必须 注释掉或删除这行 。
#auth requisite pam_succeed_if.so uid >= 1000 quiet # 注释掉它 -
针对 Ubuntu/Debian 系统 : 这些系统可能使用
@include common-auth。你需要确保pam_google_authenticator.so在@include common-auth之前 。auth required pam_google_authenticator.so @include common-auth -
可选参数
nullok: 如果你团队中有些用户还没配置2FA,但又希望他们暂时能登录,可以在模块后加nullok参数。这样,对于没有~/.google_authenticator文件的用户,这个模块会被跳过。auth required pam_google_authenticator.so nullok警告 :
nullok参数会降低安全性,因为它为未配置2FA的用户留下了后门。仅在过渡期使用,并确保所有用户最终都完成配置。
3.5 步骤五:配置SSH服务端并重启
现在来配置SSH服务本身,让它使用我们刚设好的PAM流程。编辑
/etc/ssh/sshd_config
文件。
# 找到并修改以下参数,如果不存在则添加
UsePAM yes
ChallengeResponseAuthentication yes # 旧版参数,与兼容性相关
PasswordAuthentication no # 关键!关闭纯密码认证通道
KbdInteractiveAuthentication yes # 关键!启用键盘交互认证
# 可选:如果你希望同时要求公钥和2FA,可以设置(但本文以密码+2FA为主)
# AuthenticationMethods publickey,keyboard-interactive
参数详解 :
-
PasswordAuthentication no:这是 最重要 的一步。它关闭了SSH协议自带的、简单的密码认证方法。如果不关闭,客户端可能直接走这个“后门”,完全绕过我们精心设置的PAM双因素流程。 -
KbdInteractiveAuthentication yes:告诉SSH服务端,“我支持键盘交互认证”,这是触发PAM多轮问答的前提。 -
ChallengeResponseAuthentication yes:一个历史遗留参数,在一些旧版本(如CentOS 7)的文档中仍被强调。在新版OpenSSH中,它可能被标记为已弃用,其功能已由KbdInteractiveAuthentication涵盖。但为了最大兼容性,两者都设为yes也无妨。
配置检查与重启 : 修改完配置后,千万不要直接重启。先进行语法测试,避免配置错误导致SSH服务无法启动。
# 测试sshd配置文件语法,无输出表示正确
sshd -t
# 语法检查通过后,重启sshd服务
systemctl restart sshd
# 或 systemctl reload sshd
时间同步检查 : TOTP基于时间,服务器和手机的时间必须同步(误差通常要在30秒内)。如果总是提示验证码错误,首先检查时间。
# 启用并启动NTP时间同步服务
timedatectl set-ntp true
# 查看时间状态
timedatectl status
确保你的手机也开启了“自动设置日期和时间”。
4. 客户端配置详解:Xshell、PuTTY、MobaXterm
服务器端配好了,如果客户端配置不对,照样登录不上。核心就一点: 让客户端使用“键盘交互”认证模式,而不是“密码”模式。
4.1 Xshell 8 配置(推荐,交互最清晰)
Xshell的配置界面比较直观,是验证服务器配置是否正确的理想工具。
- 新建或复制会话 :打开Xshell,点击“文件”->“新建”,或右键一个已有会话选择“属性”。
- 连接设置 :在“连接”页面,填写主机IP地址和端口(默认22)。
-
关键步骤 - 用户身份验证
:
- 点击左侧的“用户身份验证”。
- 在右侧的“方法”下拉列表中, 只选择“Keyboard Interactive” 。
- 务必取消勾选“Password”和“Public Key”等其他所有方法 。如果这里勾选了“Password”,Xshell可能会尝试直接发送密码,导致认证失败。
- 连接 :保存会话并连接。Xshell会弹出一个“用户身份验证”窗口。
-
输入顺序
:
- 第一个输入框(可能提示“Verification code”或“Password”), 输入手机上的6位动态验证码 。
- 点击“确定”或“下一步”后,出现第二个输入框(明确提示“Password”), 输入Linux系统的用户密码 。
- 如果两次输入都正确,即可成功登录。
4.2 PuTTY 配置(经典轻量客户端)
PuTTY配置稍隐晦,但一旦理解,也非常稳定。
- 会话设置 :打开PuTTY,在“Session”页面,输入主机名(或IP)和端口。
- 连接类型 :确保“Connection type”是“SSH”。
- 自动登录用户(可选) :在“Connection” -> “Data” -> “Auto-login username”中,可以预先填写用户名(如root),这样连接时就不用手动输入了。
-
关键步骤 - 认证方法
:
- 不要 在“Connection” -> “SSH” -> “Auth”页面加载任何私钥文件(.ppk),除非你明确要使用“公钥+2FA”的组合认证。对于“密码+2FA”,这里应该留空。
-
PuTTY的认证顺序是自动协商的,我们主要通过服务器端的
PasswordAuthentication no来强制它走键盘交互路径。
- 保存会话 :回到“Session”页面,输入一个会话名称,点击“Save”,方便下次使用。
-
连接与登录
:
- 点击“Open”连接。首次连接会弹出主机密钥确认窗口,点击“Accept”接受。
-
在终端窗口里,你会看到登录提示。
记住顺序:先出现
Verification code:(或第一个Password:),输入动态码;回车后出现第二个Password:,输入系统密码。
PuTTY常见问题排查 :
-
现象
:连接后只出现一个
Password:,输入密码后立刻Access denied,没有第二次输入机会。 -
原因
:客户端可能错误地尝试了“密码”认证,或者服务器端
PasswordAuthentication仍为yes。 -
解决
:确认服务器端
sshd_config中PasswordAuthentication已设为no并重启了sshd。PuTTY端可以尝试在“Connection” -> “SSH” -> “Auth”下,勾选“Attempt authentication using Pageant”和“Attempt TIS or CryptoCard auth”,但这通常不是主因,核心在服务器配置。
4.3 MobaXterm 配置(功能强大的全能终端)
MobaXterm的配置逻辑与PuTTY类似。
- 新建会话 :点击“Session” -> “SSH”,输入远程主机和用户名。
- 高级设置 :点击“Advanced SSH settings”选项卡。
- 私钥设置 : 除非使用公钥认证,否则请确保“Use private key”选项未勾选,或路径为空 。如果这里错误地指向了一个.ppk文件,MobaXterm会尝试公钥认证,可能干扰键盘交互流程。
- 连接登录 :保存并连接。登录提示顺序与PuTTY一致:先验证码,后系统密码。
重要提示 :在“键盘交互”认证模式下,Xshell、PuTTY、MobaXterm通常 无法像普通密码模式那样保存密码 。这是出于安全设计,因为动态验证码是变化的。这意味着每次登录都需要手动输入密码和当时的验证码。
5. 自动化与脚本集成:Python实现自动登录
对于需要自动化执行的运维脚本、CI/CD流水线,每次手动输入验证码显然不现实。我们可以通过Python脚本,在受控的安全环境中自动生成TOTP码并完成登录。 请注意,此方法将第二因素(TOTP种子密钥)放在了脚本或环境变量中,如果脚本泄露,则双因素认证形同虚设。请仅在可控的、安全的环境中使用。
5.1 环境准备与脚本编写
首先,在能运行脚本的机器上安装必要的Python库。
pip install paramiko pyotp
然后,编写自动化登录脚本
ssh_2fa_auto.py
:
#!/usr/bin/env python3
import os
import sys
import paramiko
import pyotp
# 从环境变量读取敏感信息,避免硬编码在脚本中
HOST = os.environ.get("SSH_HOST")
USER = os.environ.get("SSH_USER")
PASSWORD = os.environ.get("SSH_PASSWORD")
SECRET = os.environ.get("TOTP_SECRET") # 这是你在 `google-authenticator` 命令中得到的Base32密钥
if not all([HOST, USER, PASSWORD, SECRET]):
print("错误:请设置 SSH_HOST, SSH_USER, SSH_PASSWORD, TOTP_SECRET 环境变量。")
sys.exit(1)
# 清理Secret中的空格(有时显示会带空格)
SECRET = SECRET.replace(" ", "")
def keyboard_interactive_handler(title, instructions, prompt_list):
"""
处理键盘交互认证的回调函数。
paramiko会调用此函数来回答服务器提出的问题。
"""
replies = []
# 初始化TOTP生成器
totp = pyotp.TOTP(SECRET)
for prompt, _echo in prompt_list:
prompt_lower = prompt.lower()
# 判断问题类型:如果是验证码请求
if "verification" in prompt_lower or "code" in prompt_lower:
replies.append(totp.now()) # 填入当前动态码
# 如果提示是密码(通常是第二个问题)
elif "password" in prompt_lower:
replies.append(PASSWORD) # 填入系统密码
else:
# 对于无法识别的问题,返回空字符串(通常不会发生)
replies.append('')
return replies
try:
# 创建SSH传输层
transport = paramiko.Transport((HOST, 22))
transport.connect(username=USER)
# 发起键盘交互式认证,并传入我们的处理器
transport.auth_interactive(USER, keyboard_interactive_handler)
if transport.is_authenticated():
print("认证成功!")
# 在此处可以打开SFTP通道或执行命令
# ssh = paramiko.SSHClient()
# ssh._transport = transport
# stdin, stdout, stderr = ssh.exec_command('ls -la')
# print(stdout.read().decode())
transport.close()
else:
print("认证失败。")
transport.close()
sys.exit(1)
except Exception as e:
print(f"连接或认证过程中发生错误: {e}")
sys.exit(1)
5.2 运行脚本
在运行脚本前,需要设置环境变量。在Linux/Mac的终端或Windows的PowerShell中:
# Linux/Mac
export SSH_HOST='your.server.ip'
export SSH_USER='root'
export SSH_PASSWORD='your_linux_password'
export TOTP_SECRET='JBSWY3DPEHPK3PXP' # 替换为你的真实Base32密钥
# 然后运行脚本
python3 ssh_2fa_auto.py
# Windows PowerShell
$env:SSH_HOST = 'your.server.ip'
$env:SSH_USER = 'root'
$env:SSH_PASSWORD = 'your_linux_password'
$env:TOTP_SECRET = 'JBSWY3DPEHPK3PXP' # 替换为你的真实Base32密钥
# 然后运行脚本
python ssh_2fa_auto.py
安全警告 :
-
密钥保管
:
TOTP_SECRET是最高机密,等同于密码。切勿提交到代码仓库。 - 环境隔离 :最好在专用的、访问受控的“跳板机”或“CI Runner”上运行此类脚本。
-
使用密钥对
:对于自动化场景,更安全的做法是结合SSH公钥认证。在
sshd_config中设置AuthenticationMethods publickey,keyboard-interactive,这样脚本需要同时拥有私钥和TOTP种子才能登录,安全性更高。
6. 故障排查与常见问题实录
配置过程中遇到问题很正常。我把自己和同事们踩过的坑整理成了下面这个排查表,你可以像查字典一样快速定位问题。
| 故障现象 | 最可能的原因 | 排查与解决步骤 |
|---|---|---|
只有一个
Password:
提示,输入后立刻
Access denied
|
1. 客户端错误使用了“Password”认证方式。
2. 服务器端
PasswordAuthentication
仍为
yes
,客户端直接走了密码通道,绕过了2FA。
3. 用户操作错误 :在第一个提示(本应是验证码)输入了系统密码。 |
1.
检查客户端
:确保只启用了“Keyboard Interactive”(Xshell)或未指定私钥(PuTTY/MobaXterm)。
2. 检查服务器 :确认
/etc/ssh/sshd_config
中
PasswordAuthentication
已设为
no
,并执行
systemctl restart sshd
。
3. 牢记顺序 :第一个提示框 必须 输入手机上的6位动态码。 |
连接后根本不出现
Verification code
提示
| SSH服务端未正确启用键盘交互认证。 |
1. 检查
/etc/ssh/sshd_config
:确保
KbdInteractiveAuthentication yes
和
UsePAM yes
。
2. 检查PAM配置
/etc/pam.d/sshd
,确认
pam_google_authenticator.so
行已添加且位置正确。
3. 重启sshd服务。 |
| root用户无法登录,但普通用户可以 | PAM配置中包含了限制root的规则。 |
检查
/etc/pam.d/sshd
,找到类似
auth requisite pam_succeed_if.so uid >= 1000 quiet
的行,将其注释掉(前面加
#
)。
|
执行
google-authenticator
命令时报权限错误
| SELinux安全上下文阻止写入用户家目录。 |
1. 临时解决:执行
setenforce 0
。
2. 永久解决(测试环境):编辑
/etc/selinux/config
设置
SELINUX=disabled
后重启。
3. 生产环境:应使用
chcon
命令设置正确的文件上下文。
|
| 验证码总是错误,即使手机App显示新码 | 服务器与手机时间不同步。TOTP算法对时间极其敏感。 |
1.
服务器
:运行
timedatectl status
查看时间。执行
timedatectl set-ntp true
启用同步。
2. 手机 :进入系统设置,确保“自动设置日期和时间”已开启。 3. 允许时间漂移:在
google-authenticator
配置向导中,对“是否放宽时间窗口”选择
y
。
|
| 用户A登录,却提示用户B的验证码 |
在错误的用户下执行了
google-authenticator
命令。
|
为谁配置,就用谁登录执行
。如果需要为用户
alice
配置,请切换到
alice
用户 (
su - alice
) 后再运行
google-authenticator
。密钥文件
~/.google_authenticator
是用户独立的。
|
| 多次输错验证码/密码后,账户被锁定 |
系统触发了PAM失败锁定机制(如
pam_faillock
)。
|
1. 通过VNC或控制台登录服务器。
2. 重置指定用户的失败计数: - 对于
faillock
:
faillock --user <用户名> --reset
- 对于
pam_tally2
(较旧系统):
pam_tally2 --user <用户名> --reset
|
| MobaXterm/PuTTY提示私钥文件(.ppk)错误 | 在认证配置中指定了错误的或不需要的私钥文件路径。 | 在客户端设置中, 移除 “私钥认证”相关的配置(如MobaXterm的“Use private key”,PuTTY的Auth中的私钥路径),因为我们使用的是“密码+验证码”模式。 |
7. 生产环境部署清单与进阶考量
如果你需要在新服务器上批量部署,或者作为上线前的检查清单,下面这个“10秒速查清单”可以帮你快速回顾所有关键步骤。
服务器端清单 :
-
[ ]
SELinux
:根据策略决定是禁用 (
setenforce 0) 还是配置正确上下文。 -
[ ]
安装软件
:
yum/apt install google-authenticator qrencode。 -
[ ]
生成用户密钥
:切换到目标用户,执行
google-authenticator,扫码绑定。 -
[ ]
配置PAM
:编辑
/etc/pam.d/sshd,确保auth required pam_google_authenticator.so位于密码认证栈之前,并移除uid >= 1000等限制root的规则。 -
[ ]
配置SSHD
:编辑
/etc/ssh/sshd_config,设置UsePAM yes,ChallengeResponseAuthentication yes,PasswordAuthentication no,KbdInteractiveAuthentication yes。 -
[ ]
重启服务
:执行
sshd -t && systemctl restart sshd。 -
[ ]
时间同步
:确认
timedatectl status,启用NTP。 - [ ] 备用通道 :始终保留一个通过VNC/控制台或已有会话的登录方式,直到测试完全成功。
客户端清单 :
- [ ] 认证方法 :仅启用 Keyboard Interactive (Xshell) 或保持默认不指定私钥 (PuTTY/MobaXterm)。
- [ ] 登录顺序 :牢记 先输入6位动态验证码 , 后输入系统密码 。
- [ ] 测试连接 :使用新配置的客户端会话进行测试, 不要关闭已有的保障性会话 。
进阶安全考量 :
-
备用码管理
:将
google-authenticator命令生成的5个8位备用码(Emergency Scratch Codes)安全地存储起来(如加密的密码管理器)。这是手机丢失时的救命稻草。 -
分步实施
:对于团队,可以先在PAM配置中使用
nullok参数,让已配置的用户走2FA,未配置的仍用密码。给团队成员一个缓冲期去完成绑定,最后再移除nullok。 -
结合公钥认证
:最高安全级别是“公钥+动态码”。在
sshd_config中设置AuthenticationMethods publickey,keyboard-interactive。这样,登录需要:① 正确的私钥;② 正确的动态码。即使私钥泄露,没有动态码也无法登录。 -
日志监控
:配置集中的日志系统(如rsyslog, ELK),监控
/var/log/secure或/var/log/auth.log,关注认证失败事件,及时发现暴力破解尝试。
整个配置的核心逻辑可以浓缩为一句话: 服务器端关闭纯密码通道,迫使SSH走PAM流程;PAM流程里先校验Google Authenticator的动态码,再校验系统密码;客户端配合使用键盘交互模式,并按照先码后密的顺序输入。 只要理解了这三个环节的衔接,任何相关问题都能迎刃而解。

1314

被折叠的 条评论
为什么被折叠?



