SSH 密钥能登录,不代表已经可以放心关闭服务器的密码认证。旧终端可能仍在复用现有连接,客户端也可能在密钥失败后自动尝试密码;修改了 sshd_config,还可能被 Include 文件或 Match 条件改变实际结果。
下面按“准备恢复入口、验证独立密钥登录、检查有效配置、禁用密码、验证和回滚”的顺序操作。适用于使用 OpenSSH 的 Linux 云服务器。服务端示例采用 systemd,客户端命令按 Linux/macOS 编写。Windows OpenSSH 客户端需把密钥路径换成实际路径;涉及 -F /dev/null 的示例可改为 -F NUL。本文未在你的生产服务器执行命令,发行版差异以本机手册与实际配置为准。

修改前,准备一个能救回服务器的入口
保留当前 SSH 会话,另外确认云控制台的 VNC、串口或救援模式可以使用。控制台网页登录成功,不等于已经具备操作系统的恢复权限;需要确认进入实例的方法、所需凭证,以及修改 SSH 配置的权限。
如果目前只有 root 能登录,先在发行版支持的流程中建立普通管理用户,配置公钥,并验证它能执行 sudo。下面以 ops 为示例用户名,192.0.2.10 为文档示例地址,端口为 22。这些值都要替换,不能直接照抄连接。
关闭密码登录与禁止 root 远程登录分开做。本次先验证认证方式,不同时更改 SSH 端口、安全组、防火墙和 root 策略,否则连接失败时会多出几个判断方向。
在服务器上备份配置,并记录实际备份目录:
backup_dir="/root/ssh-config-backup-$(date +%Y%m%d-%H%M%S)"
sudo mkdir -m 700 "$backup_dir"
sudo cp -a /etc/ssh/sshd_config "$backup_dir/"
if [ -d /etc/ssh/sshd_config.d ]; then
sudo cp -a /etc/ssh/sshd_config.d "$backup_dir/"
fi
printf 'Backup: %s\n' "$backup_dir"
备份的是配置,不要为了写教程把服务器主机私钥或个人私钥上传到聊天、网盘或工单。
用一个全新连接证明公钥认证可用
如果还没有专用密钥,在自己的电脑上生成,不在服务器上生成后再到处复制私钥:
ssh-keygen -t ed25519 -f ~/.ssh/hostol_ops_ed25519 -C "ops-management"
为私钥设置口令;已有同名文件时取消生成,改用新文件名,避免覆盖已有密钥。受合规或算法策略限制的环境,应使用本单位允许的密钥类型。
把 .pub 公钥安装到目标用户的授权文件。可以使用 ssh-copy-id,或采用云平台、配置管理系统已有的授权流程:
ssh-copy-id -i ~/.ssh/hostol_ops_ed25519.pub -p 22 ops@192.0.2.10
只上传公钥。默认授权文件通常是 ~/.ssh/authorized_keys,但服务器可能配置了其他路径或集中式公钥获取机制,以 AuthorizedKeysFile、AuthorizedKeysCommand 的有效配置为准。使用默认路径时,确认文件归目标用户所有,常见权限是 .ssh 为 700、authorized_keys 为 600;用户家目录也不应允许其他用户随意写入。SELinux 环境还需核对文件安全上下文,不能把关闭 SELinux 当作修复办法。[1]
在自己的电脑新开一个终端,明确禁用连接复用和密码回退:
ssh -F /dev/null -p 22 \
-i ~/.ssh/hostol_ops_ed25519 \
-o IdentitiesOnly=yes \
-o ControlMaster=no -o ControlPath=none \
-o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
-o KbdInteractiveAuthentication=no \
ops@192.0.2.10
-F /dev/null 不读取常规客户端配置,因此依赖跳板机、代理或主机别名的环境需显式补上连接参数。不要用关闭主机密钥校验来换取连接成功;首次连接时通过可信渠道核对服务器指纹。[4]
输入“私钥口令”是在本机解锁私钥,与输入服务器账户密码不同。连接成功后执行 whoami,再执行 sudo -v。后者可能仍要求输入账户密码,这不代表 SSH 密码登录没有关闭。不要删除账户密码或关闭 PAM 来消除这个提示。
如果只有旧终端能用、新连接失败,暂停修改,先解决公钥认证问题。每个需要保留的管理账户都应完成一次独立连接测试。
检查 sshd 实际读取了哪些配置
在服务器查看认证相关配置位置:
sudo grep -nE '^[[:space:]]*(Include|Match|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AuthenticationMethods)' \
/etc/ssh/sshd_config
sudo grep -RniE '^[[:space:]]*(Include|Match|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AuthenticationMethods)' \
/etc/ssh/sshd_config.d 2>/dev/null
这只是定位文本;配置还可能包含其他路径、启动参数或自动化生成内容。Ubuntu 文档明确提醒,配置片段与主文件的读取顺序会影响结果。OpenSSH 多数选项使用先读取到的值,把 PasswordAuthentication no 追加到文件末尾,未必覆盖前面的 yes。[1][3]
先看全局结果:
sudo /usr/sbin/sshd -T | grep -E \
'^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|authenticationmethods|authorizedkeysfile|authorizedkeyscommand) '
如果存在 Match User、Match Group、Match Address 等条件,再模拟实际连接。下面的客户端地址、主机名和服务器监听地址均为示例,需按真实连接替换:
sudo /usr/sbin/sshd -T \
-C user=ops,addr=198.51.100.20,host=client.example,laddr=192.0.2.10,lport=22 \
| grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|authenticationmethods) '
-T 输出有效配置;-C 会应用对应连接条件下的 Match 规则。云服务器经过 NAT 时,laddr 应填写 sshd 实际看到的本地监听地址,而不一定是公网地址。[2]
若服务通过 -f 加载了其他配置文件,或使用 -o 指定启动覆盖值,上述检查必须带上对应参数。可以用 systemctl cat ssh.service 或 systemctl cat sshd.service 查看服务定义,并核对运行进程;不要只检查默认文件。
关闭密码认证,同时检查交互式认证
对于没有 SSH OTP、PAM 多因素认证或其他交互式认证需求、准备统一使用公钥登录的服务器,目标设置通常为:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
只关闭 PasswordAuthentication 还不够。keyboard-interactive 也可能通过 PAM 提供密码认证;已有 OTP 或 MFA 的环境不能直接关闭这一入口,应先核对 AuthenticationMethods 与认证策略。[1]
例如配置了 AuthenticationMethods publickey,keyboard-interactive,直接禁用交互式认证会让原有组合认证无法完成。这类服务器应保留组织规定的多因素流程,而不是照抄“纯公钥”配置。
修改前一步确认的、实际生效的位置。配置片段只有在被 Include 引入且顺序符合预期时才有用,不要假定某个新建的 99-*.conf 能覆盖所有设置。若配置由 cloud-init、Ansible 或平台工具管理,还要同步它的源配置,避免下次部署恢复密码登录。
保持现有 UsePAM 和账户策略不变。完成修改后,先检查语法和密钥,再重复全局与 -C 条件检查:
sudo /usr/sbin/sshd -t
命令没有输出且退出状态为 0,才表示该项检查通过;这并不能证明新连接一定成功。[2] 目标用户在实际来源地址下应看到 pubkeyauthentication yes、passwordauthentication no、kbdinteractiveauthentication no,同时确认没有额外认证组合阻止登录。
重载正确的服务,不先重启服务器
确认实际使用的服务单元:
systemctl status ssh.service --no-pager
systemctl status sshd.service --no-pager
Ubuntu/Debian 常见名称是 ssh.service,其他发行版可能使用 sshd.service,以本机结果为准。有的系统使用 socket 激活,还要核对 ssh.socket 的状态;本次不修改监听端口,也不照搬端口变更流程。[3]
语法检查通过后,仅对实际存在、管理当前 sshd 的单元执行重载,例如:
sudo systemctl reload ssh.service
sudo systemctl status ssh.service --no-pager
实际单元是 sshd.service 时替换名称。若单元不支持 reload,不要连着执行多个猜测的重启命令;在确认恢复入口可用后,按本发行版文档安排必要的 restart。保留原 SSH 会话,检查服务状态及日志,再开始新连接测试。
用成功与拒绝两种测试确认结果
在另一终端重新执行前面的“只用公钥”命令。确认新会话能进入目标用户,并且所需的管理权限仍可使用。旧会话保持连接,不能代替新会话测试。
然后从自己的电脑进行一次禁用公钥、仅请求密码或交互式认证的测试:
ssh -F /dev/null -p 22 \
-o ControlMaster=no -o ControlPath=none \
-o PubkeyAuthentication=no \
-o PreferredAuthentications=password,keyboard-interactive \
-o NumberOfPasswordPrompts=1 \
ops@192.0.2.10
预期结果是在认证阶段被拒绝,且不能完成密码登录。如果仍提示服务器账户密码,应回头检查有效配置、条件匹配和服务是否完成重载。连接超时、路由错误或安全组拒绝,不能证明密码认证已经关闭。限制测试次数,避免触发站点自己的封禁策略。[1][4]
有多个管理用户、出口地址或跳板入口时,分别验证受影响的组合。不要在未测试的情况下关闭最后一个可用终端。
新连接失败时,按刚才改动的范围回滚
如果旧会话仍可用,用它检查服务日志与认证配置。常见原因包括公钥装到了错误用户、授权文件权限不合要求、修改位置未生效,以及 AuthenticationMethods 仍要求被关闭的认证方式。
需要回滚时,将备份中的原文件恢复到各自原位置;本次新建的片段应移出被 Include 匹配的目录。若修改了多个文件,全部恢复,不能只恢复 sshd_config 而留下新的片段继续生效。恢复后重新执行 sshd -t,通过后重载实际服务,再验证新连接。
旧会话失效时,通过事先确认的控制台或救援入口恢复配置。具体挂载和修复步骤依云平台而定,不在不明磁盘布局上执行批量删除或替换命令。
日常管理还需要保管带口令的私钥、记录授权用户、及时撤销离职人员或丢失设备的公钥。关闭密码认证完成的是 SSH 入口的一项调整;补丁、最小权限和恢复能力仍需单独维护。
官方参考资料
资料核验日期:2026 年 10 月 8 日。配置含义依据 OpenSSH 官方手册,发行版操作依据 Ubuntu Server 官方文档;部署前请对照本机版本确认。
[1] OpenSSH sshd_config 手册:https://man.openbsd.org/sshd_config
[2] OpenSSH sshd 手册:https://man.openbsd.org/sshd
[3] Ubuntu Server:OpenSSH server:https://documentation.ubuntu.com/server/how-to/security/openssh-server/
[4] OpenSSH ssh 手册:https://man.openbsd.org/ssh




