前一阵有个朋友找我报故障:刚在VMware里装好的CentOS Stream 9,控制台能正常登root,但Windows上用Xshell连就一直弹Permission denied。他反复确认密码没输错,我也跑去对着VNC控制台看了半天,密码确实是对的。后来综合判断了一下,原因很明确:从RHEL 9这一代开始,sshd默认不再允许root通过密码远程登录。这不是密码错误,也不是防火墙拦人,而是系统故意这么设置的。如果你也刚装完CentOS Stream 9,正卡在“root远程登录被拒”这里,这篇文章就把我实际排查和处理的完整思路讲清楚,顺便把配置完仍进不去的情况也一并捋一遍。
1. 先分清楚故障类型:是“连不上”还是“登不进”
拿到这种问题,别急着改配置。第一步永远是先看SSH客户端给你什么反馈。这决定了整个排查方向。
常见的远程连接失败就三种情况:
| 现象 | 含义 | 优先排查方向 |
|---|---|---|
| Connection refused | 目标端口没有服务监听,或者被防火墙直接拒绝 | sshd是否启动、端口是否监听22、防火墙规则 |
| Connection timed out | 网络通不到,或中间设备丢包丢弃 | IP是否可达、路由、云安全组/物理防火墙 |
| Permission denied | TCP已建立连接,但认证环节失败 | 用户名、密码、密钥、sshd认证策略 |
标题里说的这个场景,大概率属于第三种:你输错密码才可能这样,但问题是即使密码正确,系统也照样弹Permission denied。如果我用root密码登录,日志里很可能会看到 Failed password for root 或者 Permission denied 这类记录,这就说明认证请求已经到了sshd进程,只是sshd根据配置不让你通过。
我建议你把远程客户端的输出调整到verbose模式再看一次:
bash复制ssh -vvv root@目标主机IP
这一堆输出里重点看最后几行。如果能看到类似:
text复制Authentications that can continue: publickey
注意:可能只有“publickey”被列出。说明sshd只要求公钥认证,根本不会接受你输入的密码。这基本就能锁定问题出在sshd配置上,而不是密码或账号本身。
服务端同时可以看日志:
bash复制journalctl -u sshd --since "10 minutes ago" | tail -n 50
CentOS/RHEL系列传统的认证日志在/var/log/secure,也可以一起看:
bash复制tail -n 30 /var/log/secure
看到Failed password for root from 192.168.x.x port xxx ssh2并不可怕,这只能说明客户端在尝试密码认证;但如果看到Authentication refused或类似字样,甚至没有任何密码验证日志而是直接断开,那就要往PermitRootLogin这条策略上靠。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 根因在这里:RHEL 9系列把root密码登录默认关了
CentOS Stream 9继承的是RHEL 9的那套安全基线。OpenSSH在RHEL 9上的默认行为是这样的:root用户允许通过SSH登录,但不允许使用密码认证。换句话说,默认值是PermitRootLogin prohibit-password。
你打开/etc/ssh/sshd_config看,可能看到的是一行注释:
bash复制#PermitRootLogin prohibit-password
很多新手看到#就以为没生效,其实被注释掉的参数取的是sshd编译时的默认值,而RHEL 9打包的默认值正好就是prohibit-password。不看实际生效值,光看配置文件很容易被误导。
要确认当前sshd进程真正生效的策略,执行:
bash复制sshd -T | grep -i permitrootlogin
这个会输出sshd当前实际使用的参数,属于最权威的结果。如果输出是permitrootlogin prohibit-password,那你SSH密码登录root被拒就是必然的。
prohibit-password是什么意思?我单独解释一下,很多人把它理解成“禁止root登录”,其实是两回事:
no:彻底禁止root通过SSH登录。yes:允许root通过密码或密钥登录。prohibit-password:允许root登录,但只接受公钥认证,不接受密码认证。
所以准确地说,CentOS Stream 9不是“不让root远程登录”,而是“不让root远程用密码登录”。这和你之前用CentOS 7/8的习惯完全不同:CentOS 7默认是允许root密码登录的,到RHEL 9之后的默认策略直接把这个口子堵了。
为什么安全基线要这么搞?原因很直白:SSH暴力破解最喜欢撞root账号,不管你把端口改成多少,公网上总有扫描器在扫。一旦root密码被破解,攻击者就拿到了全部权限。而用公钥认证,私钥长度是几百位的随机数,撞库成本比密码高几个数量级。所以系统默认先保安全,再给你保留“密钥登录”这条口子。
如果你需要的是“用root管理服务器”,完全可以通过普通用户登录再sudo或su,不需要直接暴露root密码认证。这也是RHEL官方推荐的思路。
3. 方案一:用密钥认证,root照样能远程登录,还安全
既然默认策略允许root通过密钥登录,那最顺理成章的解决办法就是配一套SSH公钥。这也是我强烈推荐的方式,尤其是可直接访问公网的机器,绝对不要为了省事去开root密码认证。
具体操作过程分三步:
第一步:在本地客户端生成密钥对
我这里用ed25519算法,比传统的RSA短而且性能好,兼容性在CentOS Stream 9自带的OpenSSH上完全没问题。Windows下的PowerShell、Linux/macOS的终端都支持:
bash复制ssh-keygen -t ed25519 -C "root-manage-key" -f ~/.ssh/id_ed25519 -N ""
-N ""表示不设置私钥口令。如果你担心私钥文件被拷走,建议不要用空口令,而是在生成时输入一个passphrase,每次使用私钥时再输入一次,相当于多一道锁。
生成后本地会有两个文件:
~/.ssh/id_ed25519:私钥,绝对不要外传。~/.ssh/id_ed25519.pub:公钥,可以放在服务器上。
第二步:把公钥写入服务器的root用户目录
这一步是很多人的拦路虎。因为root密码登录已经被禁了,你没法用ssh-copy-id root@服务器IP去推公钥——这个命令需要输入一次密码,但密码认证被禁了,推不过去。
所以正确的时序是:先通过VMware控制台、VNC、云厂商的网页版终端等本地手段登录服务器,然后在服务器上执行:
bash复制mkdir -p /root/.ssh
chmod 700 /root/.ssh
vim /root/.ssh/authorized_keys
把刚才生成的id_ed25519.pub内容整个粘贴进去,保存退出,然后:
bash复制chmod 600 /root/.ssh/authorized_keys
restorecon -Rv /root/.ssh
restorecon这步很关键。CentOS Stream 9默认启用SELinux,如果.ssh目录和authorized_keys文件的安全上下文不对,sshd会拒绝读取,导致密钥认证看起来没反应。执行restorecon后,SELinux会把它们恢复成ssh_home_t类型。
第三步:本地远程连接测试
在客户端重新执行:
bash复制ssh -i ~/.ssh/id_ed25519 root@目标主机IP
正常情况下,这里不再需要输入密码,直接就能登录成功。如果提示私钥权限过大,比如Windows下给了Everyone可读权限,SSH客户端会拒绝使用这个私钥,需要在本地把私钥权限收紧到只有当前用户可读写。
用密钥登录的好处是:你不用改任何系统默认策略,根因没有破坏,安全基线还在。后续即使密码再弱、日志里刷一堆Failed password,攻击者也进不来,因为密码认证压根不开放。
4. 方案二:临时开启root密码登录(仅适合内网/测试环境)
如果场景是内网开发环境、虚拟机测试、个人学习,并且你确实想用密码登录root,也可以按下面方法临时放开。但我要先说明白:公网生产环境不建议这么干,如果你非要开,请务必先看下一章的安全加固建议。
放开的思路不是去改主配置文件,而是单独建一个二次配置文件。原因很简单:CentOS Stream 9的sshd默认会读取/etc/ssh/sshd_config.d/*.conf目录下的所有配置文件,这个机制很容易被忽略。如果你以后还要做其他SSH参数调整,单独建文件比直接改主配置更清晰,升级系统时也不容易被覆盖。
新建配置文件:
bash复制vim /etc/ssh/sshd_config.d/99-allow-root-password.conf
内容写两行:
text复制PermitRootLogin yes
PasswordAuthentication yes
然后检查配置语法并重启sshd:
bash复制sshd -t
systemctl restart sshd
注意,sshd -t输出没有报错才是正确状态。重启后再验证一下实际生效的参数:
bash复制sshd -T | grep -iE "permitrootlogin|passwordauthentication"
如果输出是:
text复制permitrootlogin yes
passwordauthentication yes
说明ssh层面的策略已经放开了,再用root密码远程登录就可以通过。
这里有个很多人踩过的坑:只写了PermitRootLogin yes,但PasswordAuthentication如果之前被别处改成no,那密码认证依然不生效。所以上面两行必须同时存在,除非你确认PasswordAuthentication本来就是yes。
另外,如果你的服务器启用了firewalld,需要确认22端口放行:
bash复制firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
firewall-cmd --list-all
如果你用的是云服务器,比如阿里云、腾讯云,还需要去云控制台的安全组里放行TCP 22入方向。这一步经常被忽略:你配置全都改对了,本地防火墙也开了,但公网连不上,多半是云安全组还没放行。
5. 改了配置还是登不进去?按这个顺序逐项排查
经常有人照着教程操作完,跑回来问“为什么还是Permission denied”。我遇到过的情况大概有这么几类,你按顺序自查一遍基本都能定位。
第一步:再确认一次最终生效参数
修改配置文件后,不要只看文件内容,要看sshd进程真正读取到的值。执行:
bash复制sshd -T | grep -iE "permitrootlogin|passwordauthentication|allowusers|allowgroups|denyusers|denygroups"
把这几项全部列出来。如果这里显示的PermitRootLogin仍然是prohibit-password,说明/etc/ssh/sshd_config.d/下面还有其他配置文件把你的设置覆盖了。检查方式:
bash复制grep -r "PermitRootLogin" /etc/ssh/sshd_config.d/
删除或修改冲突文件后再重启sshd。
第二步:检查是否有AllowGroups或AllowUsers限制
有些从CentOS 8迁移过来的用户,或者用了某些安全加固脚本,会往sshd配置里加这样的参数:
text复制AllowGroups wheel
这种情况下,即使PermitRootLogin yes,但只要root不在wheel组,就会被拒绝。而CentOS Stream 9默认情况下root不属于wheel组。如果你确实需要root通过SSH登录,可以先把root加进wheel组:
bash复制usermod -aG wheel root
但这个操作要谨慎,等于让root拥有wheel组权限,安全边界会被拉低。更推荐的做法是:查清这台机器是否真的需要root直接SSH登录,如果不需要,就用普通用户登录再sudo。
第三步:备份SELinux和权限问题
如果你走的是密钥登录方案,最常遇到的就是SELinux没有恢复上下文。重新执行:
bash复制restorecon -Rv /root/.ssh
同时确认目录和文件权限:
bash复制ls -ld /root/.ssh
ls -l /root/.ssh/authorized_keys
正常应该是:.ssh目录700,authorized_keys文件600。如果权限偏宽松,比如644或777,sshd出于安全考虑也会拒绝使用这个密钥。
第四步:看日志到底卡在哪一步
日志永远比你猜得准。生产环境下的操作顺序是:
bash复制journalctl -u sshd -f
另开一个窗口尝试登录,实时观察日志。常见的几种关键日志:
Failed password for root:密码认证被试用但没有通过,说明策略允许,只是密码不对。Connection closed by authenticating user root:认证过程中被关闭,可能是密钥不匹配或策略拒绝。error: maximum authentication attempts exceeded:尝试次数太多,客户端需要降低验证方法,加-o PreferredAuthentications=publickey再试。Authentication refused:多半是PermitRootLogin没放行。
第五步:排除网络链路问题
在服务器本机测试:
bash复制ssh root@127.0.0.1
如果本机用root密码能登录,但外网不能,问题就出在链路。检查IP、网关、防火墙,云主机还要看安全组。如果本机也登不进去,那就是sshd配置或密码本身的问题,继续回看日志。
6. 顺手补几个安全习惯,避免“开着大门迎黑客”
如果你最后确实选择了开启root密码登录,尤其是公网环境,一定要接受一个事实:这台机器从开启那一刻起,就会持续被扫描。我的原则很简单:能开公网的机器,一律不开放root密码登录;如果一定要开,请至少做到以下几点。
第一,用强密码。
不要用Root@123这种自以为很强的密码。建议密码长度至少16位,包含大小写字母、数字、特殊符号,并且不要和别的系统重复。设置完可以用以下命令检查密码时效:
bash复制chage -l root
建议把密码最长有效期限制在90天以内:
bash复制chage -M 90 root
第二,限制root只能从特定IP登录。
比如只允许公司出口IP远程登录root,在/etc/ssh/sshd_config.d/99-allow-root-password.conf里改成:
text复制PermitRootLogin yes
PasswordAuthentication yes
AllowUsers root@192.168.1.0/24
这样即使密码泄露,攻击者换一个IP也进不来。
第三,给SSH加上fail2ban之类的小工具。
CentOS Stream 9可以通过安装fail2ban来监控/var/log/secure里的失败尝试,自动封禁连续多次失败的IP。装好后默认对SSH生效:
bash复制dnf install epel-release -y
dnf install fail2ban -y
systemctl enable --now fail2ban
注意EPEL源的availability取决于当前CentOS Stream版本是否在维护周期内,安装时如果提示找不到包,先检查dnf repolist。
第四,能折中就不要走极端。
如果只是想让root能远程登录,但不想暴露密码认证,完全可以用前面讲的密钥方式。如果嫌密钥麻烦,又必须用密码登录,那就只在内网测试机上用,或者只在临时排障时开启,排障结束后马上把配置文件删除并重启sshd。
第五,注意wheel组的闸门。
实际生产环境我会建议只允许wheel组用户SSH登录,然后让root通过sudo提升权限,而不是直接开启root密码。具体配置:
text复制AllowGroups wheel
再把需要登录的普通用户加入wheel组:
bash复制usermod -aG wheel zhangsan
这样即使有人想爆破root,sshd直接拒绝root认证;想爆破普通用户,普通用户也没有直接改系统配置的权限,必须经过sudo审计。相比“开放root密码登录”这种粗暴方式,安全性高了一个量级。
我在实际运维中已经把这套思路固定下来了:装新系统时,要么用普通用户加sudo的方案,要么给root配密钥;遇到“安装后root不能远程登录”这类问题,先按日志和sshd -T确认策略,再决定是用密钥还是临时开密码。改配置只解决眼前问题,真正的关键还是这台机器暴露在什么网络环境里,以及你到底希望它扛住多强的攻击。把这两件事想清楚,root能不能远程登录就不再是个困惑了。
