1. Ubuntu为什么默认锁死root远程登录
拿到一台全新的Ubuntu服务器,很多人第一件事就是拿旧环境的root密码去SSH登录,结果迎面一句Permission denied。这真不是密码输错了,Ubuntu在root账号和SSH服务两个层面各自设了一道锁,两道锁叠在一起,才形成了你看到的“死局”。想打开root登录,得先把这两道锁的来龙去脉弄清楚。
1.1 root账号本身没有密码
Ubuntu安装过程中并不会让你设置root密码,系统装完之后,root账号实际上处于“锁定”状态,日常操作一律通过sudo完成。这是从其他发行版转过来的用户最容易忽略的一环:别的系统装完可以直接root登录,Ubuntu这里连在本地终端里输入root密码都是进不去的。
你可以用下面这条命令查看root账号的真实状态:
sudo passwd -S root正常情况下输出结果类似这样:
root L 01/01/2024 -1 -1 -1 -1 0000 -1看到大写字母L,说明root账号是锁定的。想解锁,最直接的方式就是给它设置一个密码:
sudo passwd root这一步是整个流程的前提条件。如果root没有可用密码,就算后面把sshd配置改成PermitRootLogin yes,登录时也会提示密码认证失败,因为系统里压根不存在可以通过认证的root密码。
1.2 sshd层面还有第二道闸门
即使root有了密码,SSH服务端默认也不会放行。OpenSSH从7.0版本开始,把PermitRootLogin的编译默认值改成了prohibit-password,含义是:允许root使用公钥登录,但禁止root使用密码登录。Ubuntu自带的/etc/ssh/sshd_config里也保留了对应注释行:
#PermitRootLogin prohibit-password注意这一行是被注释掉的,也就是说系统没有做显式配置,但OpenSSH内部默认值就是prohibit-password,实际效果等同于启用了这一行。网上不少教程说“取消注释改成yes就行”,这只说对了一半——配置文件改了,但root没有密码,照样进不去。
所以完整的逻辑是:先在系统层面给root准备一个可用的认证方式,也就是密码或公钥,再在sshd层面放开root登录权限。两个条件缺一不可。下面我会先从密码登录这条线路把整套流程跑通,再说怎么收尾加固。
2. 动手之前:版本确认、密码准备和配置备份
改SSH配置属于高危操作,出问题直接连不上机器。每次动手之前,我会老老实实花上几分钟做环境确认和备份,这些习惯能帮我省掉大量排错时间。
2.1 先花两分钟确认系统和SSH版本
确认Ubuntu版本:
cat /etc/os-release确认OpenSSH版本:
sshd -V目前主流的Ubuntu 20.04、22.04、24.04都适用我下面要讲的配置方式,更早的16.04、18.04也能用,只是在新版本新增的Include目录和云镜像预置配置上略有差异,排查章节我会单独展开。版本信息记下来,后续看日志、查文档时心里有数。
2.2 给root设置密码,这一步很多人会漏
前面说过,Ubuntu默认root是锁定状态。执行:
sudo passwd root系统会要求先输入当前sudo用户的密码,然后连续两次输入root的新密码。看到passwd: password updated successfully就说明设置成功,账号也随之解锁。这里有个容易误解的点:sudo passwd root和sudo passwd -l root是相反操作,前者是设密码、解锁,后者是给密码字段加锁标记、锁定账号。如果你之前做过安全加固锁过root,后面无论怎么改sshd配置都不可能用密码登录,排查时别忘了这一层。
2.3 备份配置,并注意Include目录的覆盖规则
改配置前备份是基本操作,别嫌多余。我在每台服务器上都这么干:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)备份文件名带日期,方便日后回滚时快速识别。
另一个重要细节:Ubuntu 22.04及之后版本的sshd_config开头通常会有一行:
Include /etc/ssh/sshd_config.d/*.conf这句话会让/etc/ssh/sshd_config.d/目录下所有.conf文件参与配置合并。由于Include出现在文件最顶部,按OpenSSH“先到先得的参数生效规则”,这个目录里的配置优先级反而高于主配置文件后面写的任何内容。云厂商的Ubuntu镜像经常会在该目录下放一个50-cloud-init.conf,里面可能写着PasswordAuthentication yes或PermitRootLogin no之类的覆盖项。如果后面怎么改主配置都不生效,先去这个目录翻一翻,问题多半出在这里。
2.4 留一条绝对安全的回退通道
改SSH配置时最怕的是:自己在服务器上改了配置并重启服务,结果新配置有语法错误导致服务起不来,自己又被踢出会话,只能眼巴巴地去控制台抢救。我的习惯是动手之前确保至少还有一个已登录的SSH会话窗口不关闭,或者确认自己能打开云厂商的网页控制台。配合下面要讲的语法检查,基本就不会把自己锁在门外。
3. PermitRootLogin四种取值,选哪个直接决定能否登录
这是整篇文章最核心的参数。PermitRootLogin共有四种取值,每一种的行为差异很大,网上很多教程把它们混在一起讲,我整理了一张对照表。
3.1 四种取值的真实行为对比
| 取值 | 密码登录 | 公钥登录 | 实际含义 |
|---|---|---|---|
| yes | 允许 | 允许 | 完全放开,所有认证方式都接收 |
| prohibit-password | 禁止 | 允许 | 密码方式对root关闭,公钥照常,OpenSSH 7.0起的内置默认值 |
| without-password | 禁止 | 允许 | prohibit-password的旧写法,语义相同,现在仍兼容 |
| no | 禁止 | 禁止 | 彻底关闭root远程登录,完全锁死 |
如果你只想跑通“root能用密码直接SSH”,那就要改成yes。但我要提醒一句:让root密码直接暴露在公网SSH端口上,等于给全世界的扫描器留了一个极其显眼的目标。我更推荐的做法是先用yes把流程验证通过,确认没问题后马上改回prohibit-password,只保留公钥认证这一条路。后面我专门用一节讲加固,这里先把能跑通的配置做出来。
3.2 prohibit-password和without-password的纠葛
很多老教程写的是without-password,新文档里写的是prohibit-password,这俩其实是同一个意思,只是OpenSSH在不同版本换了更明确的叫法。without-password字面上容易让人误解成“不需要密码”,实际是“不允许root用密码登录”,只允许密钥。看到老配置里有这个值时别慌,直接改成新写法prohibit-password或者保持原样都可以,行为不会变。
还有一个冷知识:在OpenSSH 7.0之前,PermitRootLogin的默认值其实是yes,也就是老系统默认允许root密码登录。如果你维护的是一台很老的服务器,需要先确认它实际用的OpenSSH版本再判断默认行为,不要想当然。
3.3 密码登录的总开关PasswordAuthentication
很多人只盯着PermitRootLogin,忽略了另一个总开关PasswordAuthentication。真正的密码认证流程要求两个参数同时放行:PermitRootLogin yes决定root能不能登录,PasswordAuthentication yes决定系统允不允许使用密码这种认证方式。
看两个组合就清楚了:
PermitRootLogin yes+PasswordAuthentication yes:root可以用密码登录。PermitRootLogin prohibit-password+PasswordAuthentication yes:普通用户可以密码登录,但root不行,root只能用密钥。
Ubuntu默认PasswordAuthentication是开启的,但如果你之前为了安全手动关过它,就算root权限全放开也没用。排查问题时一定要两个参数一起检查。
4. 从改配置到验证登录的完整流程
下面给出一套可以直接照抄的命令流程,以Ubuntu 22.04为例。整套流程的核心思路是“小步走、随时能回退”,每一步都有对应的验证手段。
4.1 修改sshd_config
打开主配置文件:
sudo nano /etc/ssh/sshd_config找到这一行:
#PermitRootLogin prohibit-password如果只是想让root能密码登录,把它改成:
PermitRootLogin yes如果目标是用密钥登录root,改成:
PermitRootLogin prohibit-password保存退出。注意不要在文件里写多个PermitRootLogin,按先到先得的规则,只有第一个出现的值会生效,多个值只会让后续维护的人一头雾水。改动之前最好再用注释注明改动日期和原因,生产环境的配置文件最怕留无头账。
4.2 重启前必做的语法检查
这一步绝对不能省。执行:
sudo sshd -t没有任何输出,说明配置语法正确。如果返回类似这样的报错:
/etc/ssh/sshd_config line 32: Bad yes value说明第32行有拼写或格式问题,回去改好再试。sshd -t只做语法检查,不会影响正在运行的服务,可以反复执行,放心用。它是我在任何一次SSH配置变更前都必须执行的动作,养成习惯之后,因配置写坏导致服务起不来的概率会无限趋近于零。
4.3 重启服务,注意Ubuntu的服务单元名
Ubuntu的SSH服务单元名是ssh.service,不是sshd.service。执行:
sudo systemctl restart ssh重启后确认状态:
sudo systemctl status ssh看到active (running)就说明服务正常起来了。如果你在配置里引入了语法错误,这里会直接看到failed状态,这时候用之前的备份文件恢复配置,再重启一次就能救回来。
这里还有个细节值得提一下:systemctl restart ssh是重启,systemctl reload ssh是让服务重新加载配置而不中断现有连接。如果你在服务器上还有别的重要会话,用reload更温和。但为了确保配置彻底生效,我一般还是用restart,因为有些参数在reload时并不会重新读取。
4.4 用新终端的实际登录作为最终验证
在你自己的电脑上打开一个新终端,执行:
ssh root@<服务器IP>输入刚设置的root密码,能进入root shell就说明整条链路已经打通。如果提示Permission denied,先别慌,下一节就是完整的排查路径。
验证成功、确认不再需要临时调整后,记得回到配置里把PermitRootLogin改回prohibit-password,再重启一次服务,后续登录就改用密钥。“临时打开-验证-收紧”这套流程,我在很多台服务器上反复实践过,既保证了任务能完成,又把暴露时间压到了最短。
5. 配置改完还是登不进去的排查链路
这一节可能是本文最有实用价值的部分。我见过太多人卡在“明明改对了,也重启了,为什么还是进不去”。这类问题有一个典型的排查顺序,按下面的链路走,通常几分钟就能定位。
5.1 用sshd -T查看实际生效值
不要只盯着配置文件,要确认sshd真正加载了什么。执行:
sudo sshd -T | grep -i permitrootloginsshd -T会把最终生效的配置逐项输出,需要root权限才能看全。如果输出结果是prohibit-password,而你明明在文件里写了yes,说明一定有个地方把它覆盖了。优先检查sshd_config顶部的Include行,以及/etc/ssh/sshd_config.d/目录下的所有.conf文件,云镜像里的50-cloud-init.conf是头号嫌疑。这种“配置文件写了但实际没生效”的情况,靠肉眼看文件几乎永远找不出来,用sshd -T一秒钟就现原形。
5.2 检查root密码状态
继续用这一条命令:
sudo passwd -S root前面说过,看到大写L说明账号锁定。需要重点排查两种场景:一是你从未设置过root密码,二是之前为了安全执行过passwd -l root,后来忘记了。这两种情况下,无论PermitRootLogin设成什么,密码登录都不可能成功。重新执行sudo passwd root设一遍密码即可。
5.3 公钥登录失败的权限排查
如果把PermitRootLogin设成了prohibit-password却依然登录失败,并且错误提示只有Permission denied (publickey),重点检查以下几项:
/root目录权限不能是775或777,标准做法是chmod 700 /root;/root/.ssh目录权限必须是700;/root/.ssh/authorized_keys文件权限必须是600;- 文件属主必须是root:root。
还有一个常见低级错误:在Windows上生成的公钥粘贴进authorized_keys时带上了多余空格或换行,导致密钥格式损坏。遇到这种情况,可以在服务器端执行:
ssh-keygen -l -f /root/.ssh/authorized_keys这条命令会列出文件里所有公钥的类型和指纹。如果正常输出,说明密钥格式没问题;如果报错,说明文件内容需要重新整理。
5.4 用auth.log定位问题环节
Ubuntu的SSH登录日志写在/var/log/auth.log:
sudo tail -n 100 /var/log/auth.log也可以实时跟进:
sudo journalctl -u ssh -f日志里的几类关键词能帮你快速缩小范围:
Failed password for root:密码认证流程已经走通,只是密码不对。user root not allowed because account is locked:认证阶段过了,但系统层账号是锁的。Connection reset by peer:多半是网络或防火墙层面直接掐断了连接,跟SSH配置无关。
日志永远是最直接的排错入口,别一上来就盲目改配置,先看日志能省下大量时间。
6. root登录打开之后的加固方案
配置做完只是第一步,更重要的课题是如何降低风险。root远程登录一旦带着密码开放,任何直接暴露在公网上的服务器都等于挂了个醒目的靶子。我写这篇不是鼓励大家无脑开root,而是要把这个操作的正确姿势讲明白。
6.1 生产环境推荐配置:只开密钥,关掉密码
落地时我会把SSH配置收敛成这个组合:
PermitRootLogin prohibit-password PasswordAuthentication no PubkeyAuthentication yes这样root只能通过密钥登录,普通用户也不能再使用密码登录,暴力破解的入口基本上被堵死了。如果有多人需要登录服务器,各自把公钥放进~/.ssh/authorized_keys,配合sudo来管理权限,比共享root密码干净太多,出了问题也更好追责。
6.2 用Match限定来源IP
如果某些场景确实需要root能密码登录,也别对所有IP开放。可以限定只有可信来源才能这样做:
PermitRootLogin prohibit-password Match Address 192.0.2.25 PermitRootLogin yes这段配置的意思是:默认情况下root只能用密钥登录,只有来自192.0.2.25这个IP的会话允许root使用密码。需要特别注意的是,Match块必须放在配置文件末尾,Match之后的所有参数都会作用于匹配到的会话,位置放错了会导致后面的全局配置全部失效。用这个方式,就算密码泄露,攻击者也无法从其他IP利用它登录root。
6.3 用fail2ban把暴力破解挡在门外
给SSH加一道自动封禁的保险丝,我每台公网服务器都会装fail2ban,成本极低:
sudo apt update sudo apt install fail2ban默认配置就会监控sshd,连续失败次数超过阈值的IP会被临时封禁。想调整参数,编辑/etc/fail2ban/jail.local:
[sshd] enabled = true maxretry = 3 bantime = 3600重启服务生效。另外,云厂商的安全组或防火墙策略里也建议加一条:SSH端口只对你自己常用的IP段开放。软件层和网络层双保险的效果,远好于只依赖其中任何一个。
6.4 几个我一直在用的日常习惯
root登录打开后,我自己一直坚持这几件事:定期查看/var/log/auth.log里有没有大量Failed password记录;设置root密码时使用足够复杂的强密码,绝不用生日、机器名这类弱口令;长期用不到的root密码登录方式,确认不再需要后立刻重新锁死:
sudo passwd -l root锁定之后root无法再通过密码远程登录,但sshd服务不受影响,普通用户和已配置好的密钥登录都能继续正常工作。这套习惯让我在维护了多台服务器之后,依然能有效控制root账号的暴露面。
最后说一点我持续在执行的心得。处理过的服务器里,真正长期开着PermitRootLogin yes的基本没有,绝大多数场景下prohibit-password加密钥登录已经足够。遇到必须root直连的初始化任务,我都是临时改为yes、重启、干完活、立刻改回prohibit-password、再重启,整个过程不超过十分钟。这套办法看起来有点繁琐,但帮我躲过了不止一次因配置遗留而引发的暴力破解风险。照着本文的流程走一遍,你也能把“打开root登录”这件事做得既通又稳。