你对着一台Linux服务器,FinalShell里填好root用户名,密码输得滚瓜烂熟,点连接,结果对面扔回来一句“登录密码错误”。重试三遍,心态开始出问题。更气人的是,同一个密码,换成普通用户居然秒连。这个场景我在日常运维里遇到过太多次,也帮别人排查过很多次,十次里八次都不是密码的问题,而是SSH服务端策略在挡你。
这个标题对应的其实是一个非常典型的SSH登录链路故障:root用户被拒绝,普通用户正常。它涉及的坑很多,覆盖了sshd_config配置、PAM认证、账户密码策略、甚至FinalShell客户端本身。如果你或者身边的同事也卡在这个问题上,这篇文章把我实际排查过的路径完整写下来,你可以照着一步步走,基本能解决掉九成以上的情况。
1. 现象分析:普通用户能连、root不能连,问题大概率出在哪
1.1 SSH登录链路上哪些环节会分别放行普通用户却挡住root
先说结论:普通用户能连,说明SSH服务是活的,端口是通的,网络安全组或防火墙也没拦你。sshd进程能正常接受连接、完成密钥交换、进入认证阶段,那整个链路从网络到SSH主服务都是好的。
接下来真正决定root能不能登录的,就是认证阶段里的几个策略点:
PermitRootLogin:直接决定SSH服务允不允许root用户登录。很多发行版默认就是prohibit-password,也就是“只允许密钥登录,密码方式直接拒”。PasswordAuthentication:控制是否允许密码认证。如果全局关掉了密码认证,普通用户可能也是用密钥连的,而你给root填密码当然怎么填都不对。- PAM认证层:比如
pam_faillock、pam_tally2会把连续输错密码的账户锁掉,root被锁后就会一直提示密码错误。 - 账户状态:root密码过期、账户被锁定、
/etc/shadow里root那一行的密码哈希被加了!前缀,这些都会造成“密码正确但被拒”。 - 其他叠加项:SELinux的ssh相关布尔值被改过、
sshd_config.d目录下有覆盖配置、最终生效值和主文件不一致等。
简单类比一下:门卫(sshd)让普通员工(普通用户)刷卡进门,却把老板(root)拦在门外。这不是门禁系统坏了,而是门禁规则里对老板单独设了限制。我们要做的就是找到那条针对老板的规则,把它改掉。
1.2 用本机命令行先复现一把,把FinalShell从怀疑清单里摘出去
遇到这种问题,我的第一个动作不是去改配置,而是先做一次本机命令行连接测试。在本地终端里执行:
bash复制ssh root@服务器IP
如果本机也提示Permission denied,那就基本坐实是服务端配置问题。如果本机root能连、只有FinalShell不能连,那才需要怀疑FinalShell自己保存的密码、密钥或会话配置出了问题。
这一步别偷懒。我见过有人折腾一下午FinalShell,最后发现是FinalShell里保存的root密码是三个月前的旧密码,本机ssh用新密码一下就进去了。FinalShell虽然是个好用且被广泛使用的SSH工具,但它本质上是客户端,服务端认证失败的消息它原样抛给你,并不代表它就是罪魁祸首。
另外还要注意一点:SSH默认MaxAuthTries是6,如果你在FinalShell里反复重试,把错误次数耗完了,服务端会直接断开这个连接,表现为“连接被关闭”。这种情况下即使密码后来输对了也没用,一般要等几秒再试,或者换一个新会话来连。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心修复:sshd_config里PermitRootLogin的完整配置逻辑
2.1 四个可选值分别代表什么,选哪个最合理
打开SSH服务端配置,正常路径是/etc/ssh/sshd_config,如果你用的是CentOS/RHEL系,也可能是/etc/ssh/sshd_config.d/目录下有独立的conf文件。先找到PermitRootLogin这一项,它的值有这么几种:
yes:允许root使用密码和密钥登录,最省事。prohibit-password:禁止root密码登录,但允许密钥登录。这个值在较新版本里也叫without-password,写法不同,含义一样。no:完全禁止root直接登录SSH,root只能通过普通用户su或sudo切过去。forced-commands-only:允许root用密钥登录,但只允许执行被强制指定的命令,比如备份脚本、远程同步命令,不能拿到交互式shell。
如果你的需求就是希望FinalShell能用root密码连上去,那你需要把这一项改成yes:
bash复制PermitRootLogin yes
改完以后要重启SSH服务才生效:
bash复制systemctl restart sshd
不同发行版命令略有差别,老一点的系统可能要用service sshd restart,Ubuntu系的SSH服务名可能是ssh,可以用systemctl restart ssh。实际服务器用的是什么命令,最好先systemctl status sshd看一眼服务名再动手。
2.2 修改配置后如何确认真正生效(include目录与sshd -T)
很多人在这一步栽过跟头:明明改好了PermitRootLogin yes,重启服务了,再连还是被拒。原因就是现在的sshd_config普遍支持include机制,主配置里写着的默认值,会被/etc/ssh/sshd_config.d/目录下的某个conf文件覆盖掉。
比如云服务器厂商预置的镜像里,可能有一个叫/etc/ssh/sshd_config.d/50-cloud-init.conf之类的文件,里面明确写了PermitRootLogin no。这个文件的优先级高于主配置文件,你光改主文件是没有用的。
正确做法是看实际生效的配置,而不是看某个文件里写了什么:
bash复制sshd -T | grep -iE 'permitrootlogin|passwordauthentication|pubkeyauthentication'
sshd -T会把最终生效的配置全部打印出来。如果看到permitrootlogin no,说明你改的地方被覆盖了,要么把include目录下的配置文件也改掉,要么直接把不需要的那个conf文件重命名或删除。
这里有一个很实用的习惯:修改sshd_config之前先备份,修改完以后用sshd -t做一次语法检查。养成这种好习惯能避免因为手误写错参数,导致ssh服务重启失败,最后把自己锁在服务器外面,这个教训我印象很深。
2.3 顺手检查PasswordAuthentication,别让两处配置互相打架
还有一个高频问题是PasswordAuthentication。它的作用是全局控制“允不允许使用密码认证”。如果你服务器把这一项设成了no,那就算PermitRootLogin是yes,root填密码也会被拒。普通用户如果配置了密钥登录,则不受影响,这正好符合“普通用户能连、root不能连”的现象。
所以要检查的不止一个配置项,而是这两个要联动看:
| 配置项 | 期望值(允许root密码登录时) |
|---|---|
| PermitRootLogin | yes |
| PasswordAuthentication | yes |
| PubkeyAuthentication | 按需,通常保持yes |
很多云厂商镜像为了安全,会把PasswordAuthentication设成no,只支持密钥。如果你非要用密码登录,那就得把它改成yes。但这里要提醒一句:在公网环境开放密码登录是有爆破风险的。理想做法是用密钥加白名单IP登录,密码登录只在内网或开发测试环境开。
3. 密码明明正确却被拒:PAM层与账号状态的隐藏坑
3.1 连续失败触发锁定:faillock与pam_tally2的处理
服务端经常配置了失败登录锁定策略。CentOS系常见的是pam_faillock,有的系统还在用pam_tally2。它的机制是:连续N次密码错误后,锁定这个用户一段时间,甚至永久锁定直到管理员手动解锁。
root用户一旦被锁,表现就是“密码正确但一直提示认证失败”。普通用户因为没触发锁定,自然不受影响。
检查root是否被锁:
bash复制faillock --user root
或者老一点的方法:
bash复制pam_tally2 --user root
如果提示失败次数已经超过阈值,解锁命令是:
bash复制faillock --user root --reset
老系统对应版本:
bash复制pam_tally2 --user root --reset
这里有个实际经验:FinalShell的自动重连功能有时会在密码变更后反复用旧密码尝试,自己把自己锁了。你一边怀疑密码不对,一边手动重试,只会让锁定期越来越长。处理这类问题前先看一眼锁定状态,比盲目试密码靠谱得多。
3.2 root密码过期:chage命令看状态,别让过期策略卡住SSH
Linux账户有密码有效期策略。长期不用的root账户,或者镜像初始化后一直没改过密码的root,可能已经处于“密码过期”状态。
查看root的密码过期信息:
bash复制chage -l root
输出里能看到Last password change和Password expires这两个关键行。如果Password expires显示一个过去的时间,说明密码已经过期了。
密码过期后,在本地终端或者物理控制台登录时,系统会让你先改密码再进入系统。但通过SSH,很多配置下root会因为密码过期而被直接拒绝登录,连“改密码”的机会都不给。普通用户如果也在同一套策略下,应该也会被拒,但如果你把所有策略都打在root一个人身上,就会出现标题里的情况。
把密码有效期拉长或改成永久有效:
bash复制chage -M 99999 root
或者干脆临时给root重置一个新密码:
bash复制passwd root
重置后再用FinalShell连一次。这类问题在刚买的云服务器、长期不维护的老机器上尤其常见,建议第一步就用chage -l root扫一眼。
3.3 UsePAM链路中的pam_access与hosts.deny,偶尔出现的冷门限制
有些环境里,sshd_config里的UsePAM默认是yes,意思是SSH认证会走一遍/etc/pam.d/sshd里定义的PAM规则。如果这个PAM配置里加了账户限制模块,root就可能被额外挡住。
比较典型的是pam_access模块配合/etc/security/access.conf来控制允许登录的用户来源。比如access.conf里写了-:root:ALL,那就是拒绝所有来源的root登录。普通用户不在限制名单里,所以照常能连。这种配置在安全合规要求比较高的服务器上能看到。
查看/etc/pam.d/sshd,如果发现这样一行:
text复制account required pam_access.so
再去查/etc/security/access.conf里是否对root做了限制。临时验证方法是把PAM配置里那行注释掉,重启SSH再试。当然,这属于比较冷门的情况,优先级排在后边。
3.4 SELinux对SSH权限的干扰,一个常被忽略的因素
CentOS系默认开着SELinux,如果之前对/etc/ssh/目录下的文件做过不合规的改动,或者文件的安全上下文乱了,也可能导致root密钥登录异常、密码登录异常。检查SELinux状态:
bash复制getenforce
如果是Enforcing,可以先临时切换到宽容模式做对比测试:
bash复制setenforce 0
如果临时关闭SELinux后root能连了,那问题就出在SELinux策略上。可以查一下SSH相关布尔值:
bash复制getsebool -a | grep ssh
再尝试恢复/etc/ssh/目录下文件的安全上下文:
bash复制restorecon -R -v /etc/ssh /root
恢复后重新开启SELinux,再测连接。不能一关了之,生产环境SELinux还是要保留的,但这类问题确实存在,遇到的时候别死磕PermitRootLogin,记得排查一下这一层。
4. FinalShell这一侧不背锅的时候,也有三个真实存在的坑
4.1 保存的旧密码和重新认证机制
服务端全部配置都对了,但FinalShell还是提示密码错误,这种情况也不是没有。最常见的坑就是FinalShell里保存的密码和当前服务器的root密码不一致。
这个看起来像是废话,但实际场景中很容易发生。比如服务器巡检时密码被同事改过、运维平台做了一次密码轮换、或者你曾经用其他工具登录时重置过root密码,之后把这事忘了。FinalShell的会话里还是旧密码,点连接时它也真的会把旧密码发过去,怎么会不报错。
处理方式就是新建一个会话,或者编辑原会话,把密码改成现在正确的root密码重新测一次。如果服务器允许密钥登录,也可以直接在服务端检查/root/.ssh/authorized_keys里是否存了一份你认为已经删掉的旧公钥,客户端却还在用对应的私钥,现象同样是反复认证失败。
4.2 root密钥登录的权限要求比普通用户更严格
如果走的是密钥认证而不是密码认证,服务端对文件权限非常敏感。普通用户的家目录权限稍微宽松一点可能没事,但root不同,它的家目录是/root,密钥文件路径是/root/.ssh/authorized_keys。
服务端要求的是:
/root目录权限不能是777,一般700或750;/root/.ssh目录权限是700;/root/.ssh/authorized_keys权限是600;/root和/root/.ssh的属主都必须是root。
用FinalShell连接时,如果它配置的是密钥登录,但服务端文件权限不符合要求,OpenSSH为了保证安全会直接忽略这个密钥文件,然后退回密码认证。如果密码认证又被PermitRootLogin prohibit-password挡住,就会变成“普通用户正常、root怎么都登不上”的经典现象。
修复命令:
bash复制chmod 700 /root
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
chown -R root:root /root/.ssh
改完再连一次,大概率能好。这一条值得单独记着,因为它真的很隐蔽,日志里还会显示Authentication refused: bad ownership or modes,但你看日志不仔细容易漏掉。
4.3 别用普通用户加su绕开问题,后果是啥
有些人会在FinalShell里先连普通用户,然后执行su - root切到root。这样确实能绕开PermitRootLogin的限制,因为认证发生在已经登录的session内部,不涉及SSH的root登录。
短期用一用可以,但我不建议长期这么干。原因有两个:
- 部分运维操作依赖真正的root SSH会话,比如远程同步、SFTP上传文件到root家目录、某些脚本检测登录用户等,用su切换后的环境容易出莫名其妙的问题;
- 如果普通用户本身没有sudo权限,又要做系统级操作,切root变得很麻烦,也没法通过FinalShell的SFTP功能直接管理root目录下的文件。
所以从根本上解决PermitRootLogin配置,让root能正常直连,才是符合大家使用习惯的做法。
5. 完整排查链路复现:三分钟信息收集,再按概率逐项试
5.1 先不动配置,用几条命令把现场信息捞全
遇到这类问题,我先做一轮信息收集,大概三到五分钟,不要急着改配置。信息越全,后面越能一步到位。
第一组命令,看SSH服务实际生效配置:
bash复制sshd -T | grep -iE 'permitrootlogin|passwordauthentication|pubkeyauthentication|usepam'
第二组命令,看最近登录失败日志。CentOS系:
bash复制tail -n 50 /var/log/secure
Ubuntu / Debian系:
bash复制tail -n 50 /var/log/auth.log
第三组命令,看root账户状态:
bash复制passwd -S root
chage -l root
faillock --user root
这三组命令拿到的信息,基本能覆盖九成以上问题。把日志里关于ssh的那一段仔细看一遍,里面会有类似Failed password for root, Permission denied, User root not allowed because account is locked之类的关键字,每一句都对应一个方向。
5.2 按概率排序的验证清单与判断表
不同现象对应不同原因,我整理过一张判断表,遇到同类问题时直接对着看:
| 现象特征 | 最可能原因 | 快速验证手段 |
|---|---|---|
| 普通用户密码能连,root密码不能连 | PermitRootLogin设为了no或prohibit-password | sshd -T看实际生效值 |
| 密码正确但日志里一直报failed | pam_faillock触发了锁定 | faillock --user root确认 |
| 日志提示account expired / password expired | root密码过期 | chage -l root确认 |
| 日志提示bad ownership or modes | authorized_keys权限不对 | 检查/root/.ssh下权限 |
| 日志提示not allowed because account is locked | /etc/shadow里root有!标记 | passwd -S root确认 |
| 只有FinalShell连不上,本机ssh能连 | FinalShell里保存的密码或认证方式不对 | 在FinalShell里新建会话重测 |
这张表是我实际排查时反复用到的框架。先看日志说什么,按日志的提示去找对应配置,不要靠猜。
5.3 修复后的验证动作,以及防止翻车的保底操作
修完配置后,验证步骤要成体系,不能改了就算完事。
- 先用
sshd -t做语法检查,确认配置没写错; systemctl restart sshd重启服务;- 在服务器本机执行
ssh root@127.0.0.1,模拟SSH登录一次,确认服务端没问题; - 再在FinalShell里新建一个会话连接试试,不要用旧的故障会话,以免被旧状态干扰。
另外我长期养成的习惯是:在改SSH配置前,先开一个新的SSH会话保持连接不动。万一改坏了导致服务重启失败、sshd起不来,还能靠这个已经建立的会话远程把配置改回来。这属于保底操作,看起来多余,但关键时刻能救命。
我亲身经历过分不清sshd_config和ssh_config的区别,把PermitRootLogin写进了ssh_config,然后重启SSH服务,看起来改了一堆,但服务端根本没读取那个文件。这里再强调一次:服务端监听和认证配置在sshd_config,客户端行为配置才用ssh_config,别改错文件。
6. 把root登录这件事管起来:经验总结与策略固化
6.1 为什么云厂商普遍默认禁用root密码登录
很多云主机镜像默认就禁止root密码登录,甚至密码认证也一并关掉。这么做不是为了恶心用户,而是因为root密码暴露在公网上的风险确实高。服务器挂到公网后,扫描器和爆破脚本会优先猜root,几乎24小时不停。
普通用户能连,是因为这类攻击集中在root上,而且普通用户名你不说出去不容易被猜中。所以很多默认安全策略其实是“允许普通用户密码登录,禁root密码登录”。这不是Bug,是设计。
理解了这一层,你就能明白,修复root登录只是第一步,真正要思考的是用什么方式来保证安全。如果只是一个人用临时服务器,那把PermitRootLogin yes加上,用完就销毁,问题不大。如果是长期项目,就要考虑更稳妥的姿势。
6.2 开发测试与生产环境的不同取舍
我自己的服务器会按环境区分策略。
开发测试环境,追求效率和方便,允许root密码登录是可接受的,因为我清楚地知道这台机器随时能重建,风险可控。
生产环境,我基本不开root密码登录。要么用密钥登录root,要么把普通用户加进sudo组,日常操作全走普通用户,需要提权时再sudo。FinalShell里也可以配置密钥登录,保存私钥文件,用的时候一样方便,安全性却高了一个量级。
配置普通用户加入sudo,命令很简单:
bash复制usermod -aG wheel ops
这样做的收益很明显:日志里能看到是哪个普通用户执行了哪条sudo命令,有审计依据。而所有人共用root操作,出了问题根本分不清是谁干的。
6.3 我长期这么干:备份配置、保留逃生通道、配合fail2ban
关于SSH的可维护性,我总结了一套固定的日常操作:
- 每次修改
sshd_config前,先复制一份备份,文件名带日期,比如sshd_config.bak.20250101; - 修改后用
sshd -t做语法检查再重启,绝不做“改完直接重启”这种危险操作; - 本机window/终端工具里同时保留普通用户密码登录和root密钥登录两套会话,万一root登录策略调整把密码方式切了,至少还有普通用户能进来改;
- 公网服务器上装一个
fail2ban,监控SSH失败日志,超过阈值就临时封禁来源IP,能大幅减少暴力破解的噪音。
日志里如果看到大量Failed password for root,别光想着一台一台排查,优先确认网络层面有没有只允许自己IP段访问SSH端口的白名单。把安全组或防火墙配置成白名单模式,比任何软件策略都更直接。
最后再分享一个跟FinalShell有关的细节:用FinalShell连接服务器时,如果服务器上已经恢复了root密码登录,但旧会话还挂着,无私密地保存了旧密码,你可以在会话配置里找到“重新输入密码”的入口,手动更新。很多人卡在这一步,是因为服务端早就修好了,但FinalShell里那个会话的密码还是错的。把这些做完,再测试连接,基本就是一路畅通了。
