资讯动态

Rocky 9.6 SSH双因素认证实战:密码/密钥+Google Authenticator动态码

发布时间:2026/10/9 6:21:29 来源:尧图企业网站定制
做运维的朋友应该都有这种体会凌晨三点被手机上的监控通知震醒SSH 登录日志里刷了几百条Failed password来自同一个 IP 段的扫描脚本换个端口又来一轮。以前我也迷信强密码觉得密码够长够乱就行直到有台测试服务器的 root 密码被爆破成功进了系统装了一堆挖矿程序才老老实实把双因素认证提上日程。这篇文章不跟你摆理论直接带你把 Rocky 9.6 上的 SSH 登录改成“密码或密钥 Google Authenticator 动态码”的双重认证。里面会讲清楚每一步背后在调什么、为什么这么调以及把服务器锁在门外之后怎么救回来。不管你是刚入行的小白还是手里捧着一堆服务器想统一加固的老手这套流程都值得过一遍。1. 为什么要在 Rocky 9.6 上用 Google Authenticator1.1 先准备一台能远程登录的 Rocky 9.6先说基础环境。我使用的是 Rocky Linux 9.6 的最小化安装无论是从官网 ISO 还是镜像站下载的安装介质安装时选 MINIMAL 模式就够了。很多朋友一上来就装 GUI服务器真的没必要装完反而多了一堆没用的依赖攻击面也变大。如果你已经在虚拟机里装好了系统那接下来要享受如今最需要的步骤是让这台机器能固定 IP、能远程登录。虚拟机里装 Rocky 最常见的问题是重启之后 IP 变了导致 Xshell 连不上。建议一开始就通过nmcli把网络配置改成静态 IP。比如网卡接口是ens160IP 段规划在192.168.1.0/24sudo nmcli con mod ens160 ipv4.method manual \ ipv4.addresses 192.168.1.100/24 \ ipv4.gateway 192.168.1.1 \ ipv4.dns 192.168.1.1 sudo nmcli con up ens160 ip addr show ens160nmcli是 NetworkManager 的命令行工具Rocky 9.x 默认使用 NetworkManager 管理网络。固定完 IP 之后用 Xshell 新建一个 SSH 会话填上 IP 和用户名密码能连上就说明网络这道坎过去了。我这里特意提到通过 Xshell 连接是因为后面 Google Authenticator 的交互提示会在 SSH 客户端里出现Xshell 对键盘交互认证支持得不错后面你会看到完整的操作过程。1.2 TOTP 动态口令的原理Google Authenticator 并不是什么高深的技术它实现的是标准的 TOTP 算法也就是 Time-based One-Time Password。关键点可以理解成手机和服务端共享一个秘密密钥两边每隔 30 秒取一次当前时间戳用 HMAC-SHA1 算出一个 6 位数字。你手机上显示的数字会跟着时间滚动服务端会拿你提交的数字去验证如果对上了就证明“你”同时拥有秘密密钥和当前时间。你平时用第三方登录时的短信验证码本质上也属于一次性的动态口令只不过走的是短信通道而 TOTP 是纯本地计算不需要信号不产生费用也不存在短信被拦截的问题。唯一要求就是手机时间和服务端时间不能差太多否则算出来的数字对不上后面排查部分我会再讲。我习惯把这个过程类比成进门需要开两把锁第一把锁是钥匙也就是你的密码或 SSH 密钥第二把锁是滚动码手机上那个 30 秒变一次的数字。盗贼就算偷走了你的钥匙没有滚动码也进不了门。反过来就算别人看到了你的滚动码等你生成的下一组数字已经变了他也进不去。2. 安装 google-authenticator 并把账号绑定到手机2.1 启用 EPEL 源并安装软件包Rocky 9.6 的基础源里并没有google-authenticator这个包需要先启用 EPEL 源。EPEL 是 Fedora 社区维护的扩展仓库很多 Rocky 上没有的小工具都在里面。安装两步命令直接贴sudo dnf install -y epel-release sudo dnf install -y google-authenticator第一条命令装的是 EPEL 源的 RPM 包装完以后dnf就能识别更多软件第二条命令安装的主角会同时带来两个关键文件命令行工具/usr/bin/google-authenticator和 PAM 模块pam_google_authenticator.so。安装完成后可以用下面的命令确认 PAM 模块是否真的落地rpm -ql google-authenticator | grep pam正常情况下你会看到类似/usr/lib64/security/pam_google_authenticator.so的输出。PAM 模块是这次配置的核心它负责在 SSH 认证流程里跑一段单独的验证逻辑。如果你输出里没有这个文件多半是 EPEL 源没生效或者 dnf 把依赖解析到了奇怪的地方先检查源的状态再往下走。2.2 运行 google-authenticator 生成密钥安装好软件后需要为每一个要从 SSH 登录的账号都执行一遍google-authenticator因为它生成的是“用户级”的密钥文件。比如要用zhangshan这个用户登录就先切换到该用户再运行sudo -u zhangshan -i google-authenticator首次运行会有一连串交互式提问很多人看到英文就慌。我帮你把每个问题的含义和推荐答案都拆开Do you want authentication tokens to be time-based (y/n) [y]?问你要不要用基于时间的动态码也就是 TOTP而不是基于计数器的一次性密码。时间型的好处是手机上数字会自动刷新不用每次额外点一下这里直接选y。紧接着屏幕上会出现一个很大的二维码下面是底部的密钥字符串类似G3FG3H2K...。这段信息相当重要二维码是用来看的密钥字符串是以后手动绑定手机用的绝对不要随便截图外发。如果你现在在服务器上登录可能不方便扫码可以把密钥手动输入到手机。再往下Do you want me to update your /home/zhangshan/.google_authenticator file (y/n) [y]?问要不要把密钥写进用户家目录下的配置文件必须选y不然后面 PAM 模块不知道该拿什么去校验。Do you want to disallow multiple uses of the same authentication token?问你是否禁止同一个动态码重复使用。选y。这能防止有人拿到你刚用过的动态码再次提交算是一个防重放保护。实际会话中给出的默认答案就是 y我们保持默认。Do you want to increase the window size to 4 minutes?问你要不要放宽时间窗口。默认窗口是 3 分钟也就是前后各允许 1.5 个时间段如果你选 y就扩大到 4 分钟对时间偏差大的用户更友好但同时会增大爆破成功的概率。我的建议是选n保持默认配合 NTP 时间同步完全够用。Do you want to enable rate-limiting for this user?问要不要做速率限制。选y会让 PAM 模块在连续输错几次之后暂停认证能有效拖慢暴力破解的速度。配置完成后用户家目录下会生成~/.google_authenticator文件。文件权限务必要限制好chmod 600 ~/.google_authenticator只有当前用户自己能读其他人一概不许碰。如果这个文件被别的用户读走别人就能拿到你的 TOTP 密钥等于把第二把锁的钥匙也偷走了。2.3 手机端扫码绑定与验证接下来是手机端操作。打开你手机上的 Google Authenticator 应用如果之前没安装去官方应用商店搜 Google Authenticator 下载。它也支持其他兼容应用比如 Microsoft Authenticator、Authy 等本方案用的是 Google Authenticator 的协议和配置格式所以其他支持 TOTP 的工具同样适用。点应用里的“”号选择“扫描二维码”对准刚才终端输出的二维码。如果扫描不方便就选择“手动输入密钥”把那串 Base32 编码的密钥敲进去。我建议手动输入时核对两遍漏掉一个字符你就要从头再来。绑定成功以后手机应用里会出现一个账户名底下是一组每 30 秒刷新一次的 6 位数字。这个数字就是你的动态码。不要急着关掉服务器终端先拿手机上的动态码验证一下能不能匹配。验证方法是后面配置完成后在登录时输入一次或者你可以在系统里直接跑一次google-authenticator还没退出的状态下去看文件但那样意义不大真正验证一定是通过 SSH 登录时测这一步放到下一章配置完成后再做。这里多插一句google-authenticator会在生成密钥的同时显示几个紧急恢复码英文叫 emergency scratch codes它们是以1 2 3 4 5 6 7 8 9 0开头的 8 位数字每个只能用一次用途是当你手机丢了或者 App 数据清空时用这些备用码登录服务器。这几个码务必复制到本地密码管理器中保存或者打印出来锁进抽屉千万别截图丢在服务器上。3. PAM 和 SSH 的两种双因素接法到这里Google Authenticator 只是完成了用户侧的密钥生成服务端还根本没启用它。这就要动 PAM 和 SSH 配置了这也是整个方案里最容易把自己关在门外的一步。先备份文件再操作这是铁律sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak备份的重要性后面你会体会到万一哪一步改错一个命令就能救回来。3.1 方案 A系统密码 动态码先说最少配置的方案保留原来的系统密码登录在密码之外再加上动态码。这种情况下你在登录时会被要求先输入系统密码再输入动态码或者反过来取决于 PAM 行的顺序。编辑/etc/pam.d/sshdsudo vi /etc/pam.d/sshd在文件顶部#%PAM-1.0这行下面加一行auth required pam_google_authenticator.so改动以后文件开头大致长这样#%PAM-1.0 auth required pam_google_authenticator.so auth substack password-auth auth include postlogin ...这个顺序的含义很关键PAM 会从上到下依次执行auth类型的模块。required表示这一关必须过没过就直接拒绝过了以后继续执行后面原有的auth substack password-auth也就是核对系统密码。所以最终效果是动态码先过一遍密码再过一遍两个都对了才放行。然后编辑/etc/ssh/sshd_config确认或添加以下几行UsePAM yes PasswordAuthentication yes KbdInteractiveAuthentication yes ChallengeResponseAuthentication yesUsePAM yes是 PAM 生效的前提必须开着。KbdInteractiveAuthentication yes是 OpenSSH 新版本里对键盘交互认证的正式开关ChallengeResponseAuthentication yes是它旧的别名留着双保险。之后先检查配置再重启服务sudo sshd -t sudo systemctl restart sshdsshd -t会在真正重启前检查语法如果有拼写错误或者参数冲突会直接报出来这是避免自己断自己后路的第一道保险。3.2 方案 BSSH 密钥 动态码推荐如果你管理的是生产环境我强烈建议用方案 B先要求 SSH 密钥再要求动态码。这样做等于彻底把密码登录给关掉密码盗取攻击直接失效。即使密钥文件泄露对方没有动态码依然进不来。前提是你已经在本地客户端生成过 SSH 密钥了。如果没有先在 Windows 或 Linux 客户端上执行ssh-keygen -t ed25519 -C your_comment生成完公钥后要趁密码登录还没关闭、动态码也还没强制时先把公钥推到服务器上ssh-copy-id -i ~/.ssh/id_ed25519.pub zhangshan192.168.1.100或者手动把公钥内容追加到服务器上~/.ssh/authorized_keys文件里。这一步一定放在开启强制校验之前否则 SSH 密钥都还没放上服务器你后面就会被自己拦在门外。接下来改/etc/pam.d/sshd。和方案 A 不一样这次要把原有的密码认证部分注释掉只留下 Google Authenticator 模块#%PAM-1.0 auth required pam_google_authenticator.so #auth substack password-auth #auth include postlogin ...为什么注释掉password-auth因为 SSH 密钥已经在 OpenSSH 层完成了一个因素的认证后面第二步只需要 PAM 完成动态码的认证。如果不注释有的版本会在 keys 验证通过之后再提示你输入一次系统密码体验很割裂而且没必要。account、session下面几段保持不动那些控制的是账户有效性、会话初始化和认证环节无关。然后编辑/etc/ssh/sshd_configUsePAM yes PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication yes ChallengeResponseAuthentication yes AuthenticationMethods publickey,keyboard-interactive关键就是这行AuthenticationMethods。它定义了“必须依次过哪几个认证关卡”。这里写的是publickey,keyboard-interactive意味着用户必须先通过公钥认证再通过一次键盘交互认证而键盘交互认证会走到我们在/etc/pam.d/sshd里放的 Google Authenticator 模块。少了哪一关都不行。同样执行sshd -t和systemctl restart sshd。3.3 配置 AuthenticationMethods 时要注意的顺序问题AuthenticationMethods这行有蛮多坑我单独拿出来强调。如果写的是AuthenticationMethods publickey,keyboard-interactive逗号表示“且”两个条件必须同时满足这是双因素的核心。如果写成了下面的样子AuthenticationMethods publickey keyboard-interactive空格表示“或”用户只要满足其中一个就能登录。虽然实践中keyboard-interactive仍然走到了 PAM且 PAM 里有 Google Authenticator所以最终还是会被拦但逻辑上已经不符合双因素的预期。建议直接用第一个写法一行解决问题。还有一个必须牢记的事在重启sshd之前保持当前已经打开的 SSH 会话不要关闭。我见过不少朋友改完配置立刻重启然后新开一个窗口测试连不上手里唯一的旧连接也被自己关掉了最后只能去虚拟机控制台手动救。正确做法是改完配置开一个新会话去测试确认新会话能通过完整的认证流程后再关闭旧窗口。这是所有 SSH 加固操作里最基本的仪式感。4. 客户端登录实操Xshell、命令行与 root4.1 用 Xshell 走一遍双因素认证先说你最关心的 Xshell 实际体验。在 Xshell 里新建会话主机 IP 就是之前配置的静态 IP端口 22用户名填zhangshan。我按方案 B 演示整个流程。连接之后Xshell 会先提示你选择认证方式。你选择“Public Key”同时选中之前生成好的私钥文件如果你给私钥设了密码短语这里会先要求输入私钥密码短语。这一步是 SSH 密钥认证对应的是publickey环节。密钥验证通过之后Xshell 会再弹出一个“键盘交互式”提示内容通常是Verification code或者只是让输入 OTP。此时打开手机上的 Google Authenticator输入 30 秒一变的 6 位数字回车。整个过程大概就是Authenticated with partial success. Verification code:注意输入验证码时终端通常不回显看着光标不动很正常输完直接按回车就行。如果走到这一步说明 PAM 模块接管了后续认证Google Authenticator 已经生效。如果是走方案 AXshell 会先弹一次系统密码的提示然后紧接着再要求输入验证码。有些版本界面会分成两次对话框第一次是Password第二次是Verification code别把密码输到验证码的框里反过来更不行。4.2 命令行和常见 SSH 客户端Linux 和 macOS 下的命令行操作更直观。方案 B 的完整登录命令是ssh -i ~/.ssh/id_ed25519 zhangshan192.168.1.100连接后系统会先要求输入公钥密码短语如果你设了再提示输入动态码。如果你不确定客户端走哪种认证方式可以用-o显式指定认证顺序ssh -o PreferredAuthenticationspublickey,keyboard-interactive \ -i ~/.ssh/id_ed25519 zhangshan192.168.1.100这样就把顺序固定在“密钥在前键盘交互在后”避免客户端自作主张先尝试别的认证方法也不会出现绕过多因素的情况。PuTTY 也是常见的 Windows 客户端。在 PuTTY 的 Connection - SSH - Auth 里加载私钥然后在 Connection - SSH 的分类里确保允许 keyboard-interactive 认证。PuTTY 对 PAM 提示的兼容性整体稳定如果输入动态码后提示失败多半不是客户端问题而是时间同步或 PAM 配置问题看第 5 章的排查思路。4.3 root 登录和紧急恢复码很多服务器习惯用 root 登录。在 Rocky 9.6 里默认配置下 root 是允许密码登录的但这和双因素认证并不冲突。你想让 root 也使用动态码就要为 root 单独跑一次google-authenticator因为/root/.google_authenticator和普通用户的家目录文件是两个完全独立的文件。但我个人的建议是生产环境不给 root 开密码登录而是改成PermitRootLogin prohibit-password让它只接受密钥 动态码密码认证直接关闭。原因很简单root 本来权限就最高再走密码登录就是把最大的钥匙挂在最容易被猜的门上不是好习惯。至于紧急恢复码我建议每条都单独抄下来甚至干脆导入到密码管理器单独的分组里。真到了手机丢失或者 App 卸载的时候你还能用恢复码登录一次进去后立刻删除旧的.google_authenticator文件再重新生成新的密钥手机重新绑定。恢复码每个只能使用一次用完就作废记得及时补充。5. 常见故障与排查记录5.1 验证码无效先看系统时间你在手机上看到的动态码是“基于当前时间”算出来的。如果你的服务器时间慢了两分钟那么服务端在验证时用的时间点和手机不一样出的数字自然对不上。TOTP 的标准允许时间窗口存在轻微偏差但偏差一大就必然失败。遇到动态码总是提示无效第一件事永远是用date查看服务器时间然后确认 NTP 同步是否正常timedatectl set-ntp true timedatectl status chronyc trackingRocky 9 默认使用 chrony 做时间同步。如果你是在虚拟机里装系统宿主机时间不准也会把虚拟机的时区拽偏因此宿主机和虚拟机最好都开启 NTP。时间同步恢复正常后手机上等 30 秒刷新一组新动态码再试。5.2 密码对了但登不进去PAM 与配置文件权限如果journalctl -u sshd或/var/log/secure里出现类似pam_google_authenticator.so: Unable to open的日志大概率是权限问题。服务器上的~/.google_authenticator文件必须 600 权限、属主必须是当前登录用户。用ls -l ~/.google_authenticator看一眼如果显示-rw-r--r--立刻改成 600。还有一种情况同一个用户之前生成过一次密钥后来为了重新绑定手机又跑了一次google-authenticator会把文件内容覆盖掉。这是正常的旧的手机绑定会失效新的绑定生效。如果明明覆盖了文件但登录仍然使用旧密钥说明你改错了用户或者改错了家目录。另外如果你在/etc/pam.d/sshd里加了 Google Authenticator 一行但登录时它完全没有提示动态码那很可能是 PAM 行没有被实际执行。检查一下你的/etc/ssh/sshd_config里有没有UsePAM no。如果之后又被覆盖成 no全部 PAM 配置都会被跳过那动态码自然就不起作用了。5.3 配置过程中把自己锁在外面怎么办这是所有 SSH 加固里最常出问题的环节。万一你真的在改配置过程中手滑新会话一个都连不上别急还有几个后门第一如果 KVM、VMware、VirtualBox 等控制台还能登录也就是直接在虚拟机桌面或串口登录那么你仍然可以以 root 身份进入系统把/etc/pam.d/sshd.bak和/etc/ssh/sshd_config.bak复制回去然后systemctl restart sshd恢复原状。第二如果连控制台都进不去就要考虑通过系统救援模式启动把两块备份文件恢复。这种极端情况需要你在虚拟机平台上重启进入 single user 模式一般虚拟化平台都能做到。第三也是最推荐的永远在开启强制校验之前先保留一个已经认证成功的 SSH 会话不关闭。服务器systemctl restart sshd不会断开现有连接你始终可以通过这个“幸存连接”去检查和回滚配置。我之前就是靠这个习惯救了无数次。5.4 多用户和批量部署的注意点如果你管理的是一批机器、一堆用户一个个登录去跑google-authenticator显然不现实。先明确一个安全原则不要在 PAM 带nullok参数。nullok的意思是这个模块对没有.google_authenticator文件的用户跳过验证这相当于给所有还没配置的人打开了没有双因素的门不适合强制安全场景。批量部署的思路是走脚本生成密钥文件。google-authenticator本身支持非交互参数常用的是google-authenticator --time-based --force --disallow-reuse --rate-limit \ --secret/path/to/secret --print如果你决定自动化分发请务必把生成的密钥文件通过安全渠道传到对应每个用户的家目录并确保属主、权限正确。更稳妥的做法是让每个用户自己登录执行一次交互式命令虽然多一步但能避免密钥在内网传输中被监听。我自己倾向于“机器自动装模块 用户自助绑定”的混合模式PAM 全部启用但不设置nullok同时定期巡检哪些用户没有密钥文件。6. 用了一段时间后的几点体会6.1 为什么我最终选了密钥 动态码刚接触这套方案时我觉得密码 动态码就可以了毕竟少搞一个 SSH 密钥分发步骤。但真正用在生产环境里我还是把密码登录关了改成密钥 动态码。理由不是密码不够强而是密码这层在 OpenSSH 的认证体系里太容易被尝试和爆破而密钥本身是一段随机性很强的本地文件不存在“猜”的可能。关掉密码登录后SSH 暴力破解日志几乎瞬间安静下来剩下的只是扫描器无功而返的噪音。另外一个值得留意的问题是用户体验。方案 B 增删用户时需要导入导出公钥团队里如果有人换了电脑新私钥要重新上传这比改密码多了一点管理成本。但相比安全收益来看这点成本相当划算尤其是服务器暴露在公网上的场景。6.2 真遇上一个倒霉场景我记忆最深刻的一次翻车是给一台远程服务器启用 Google Authenticator 时手机刚好恢复出厂设置App 数据全没了密钥文件又没提前备份差点把我挡在门外。当时唯一的依靠就是生成密钥时抄下来的紧急恢复码。那次之后我把所有服务器的紧急恢复码都记录到了密码管理器并且要求团队里每个成员都要自己保存一份自己的恢复码不把自己的恢复码交给别人。如果你现在正在看这篇文章看完之后就去把紧急恢复码抄下来不然谁说了都没用。6.3 后续还能怎么扩展Rocky 9.6 这套 Google Authenticator 配置不光能保护 SSH也可以扩展到 sudo、控制台登录、FTP 等应用原理都是一样的在对应的/etc/pam.d/文件里加上pam_google_authenticator.so那一行即可。如果你以后要临时给一些服务开 web 登录还可以把 TOTP 理念用到其他管理系统的两步验证里只要理解了 PAM 的认证顺序和 SSH 的认证方法组合切换场景只是换文件而已。我用了一段时间后最大的体会是安全措施做得越简单直接越容易坚持。Google Authenticator 最大的优势就是它够轻量不用额外搭建基础设施成本基本为零。现在 SSH 登录对我来说已经是习惯动作输完密钥密码再看一眼手机上的 6 位数字两台设备同时在场才有资格进服务器。这套配置步骤虽然有点繁琐但它是真正能让“爆破”两个字成为过去式的方案。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑