资讯动态

Ubuntu root SSH 登录失败排查:PermitRootLogin 与配置覆盖

发布时间:2026/10/25 10:21:52 来源:尧图企业网站定制
改完sshd_config里的PermitRootLogin yes重启服务ssh rootyourhost依然跳出Permission denied (publickey)然后开始怀疑是不是系统有 Bug——这个场景我见过太多次了。绝大多数情况下不是 Bug而是 Ubuntu 的 SSH 配置采用了“第一个出现的值生效”这条规则加上sshd_config.d/目录里那些以数字开头的覆盖文件把你主文件里的设置悄悄盖掉了。Ubuntu 下用 SSH 登录 root 用户这件事表面上只是改一行配置实际上牵涉到账户锁定状态、配置加载顺序、密钥权限链、PAM 校验、云镜像预设策略五六个环节任何一环没打通症状都是同一个Permission denied但根因完全不同。下面这份内容写给已经能在 Ubuntu 上正常 SSH 登录普通账号、但需要把 root 直连打通的人。如果你连普通用户都登不进去那问题不在 root 权限上先解决网络连通和 sshd 服务本身。文中所有操作我在几台不同版本的机器上都反复验证过包括 20.04、22.04 和 24.04版本差异我会逐处标注。1. 先把“能不能登”和“该不该登”分开看这两个问题经常被混在一起讨论导致要么过度保守明明需要 root 直连非说不行要么过度放开生产机上直接开密码 root 登录。分开看各自的答案会清晰很多。1.1 默认拒绝 root 登录这条规则是怎么来的Ubuntu 从 16.04 开始/etc/ssh/sshd_config里PermitRootLogin的默认值就从yes改成了prohibit-password。这个值的行为很具体root 用户不能用密码登录但可以用密钥登录。很多人只记住“Ubuntu 不让 root 登录”这个模糊印象实际上密钥登录是放行的所以如果你的密钥没配好会误以为是配置被禁用。更前一层的原因在账户本身。Ubuntu 安装时如果没让你设置 root 密码root 账户的密码字段是锁定的/etc/shadow里那一行显示为!开头。锁定状态下无论PermitRootLogin设成什么都登不上因为密码验证这一环根本走不到 SSHDSSH 服务守护进程的配置判断账户层面就已经拒绝了。用一条命令就能确认当前状态sudo passwd -S root输出格式是root L 01/01/2024 0 99999 7 -1这种第二列是关键第二列取值含义SSH 密码登录可行性LLocked密码字段被锁定必须先用passwd root设置密码才能解开PPassword set已设置可用密码满足密码登录的账户前提NPNo Password空密码极危险且 sshd 通常会拒绝空密码我见过一次排查了两小时的案例配置文件改对了、服务也重启了、防火墙也通了最后发现是那台机器的 root 处于L状态——之前有人为了“安全”执行过sudo passwd -l root把原本能用的 root 又锁回去了。所以配置改完第一步不是测 SSH而是先确认passwd -S root。1.2 什么场景下确实需要 root 直连我不赞成在公网生产机上开 root 直连但这不代表这个需求本身不合理。常见的合理场景有这么几类一类是自动化脚本和下发工具。很多批量下发脚本默认以 root 身份连接改造成“普通用户 sudo 免密”不是不行但改造成本和后续维护风险可能比开 root 更大尤其是脚本要接管几十台异构机器的时候。这种情况下我会倾向于“限定来源 IP 密钥登录 只开 root”而不是把整个执行链路重构一遍。另一类是本地虚拟机和实验环境。研究性质的一次性环境机器生命周期可能就几个小时配置加固的收益远低于折腾成本。这类环境我通常直接开PermitRootLogin yes用完销毁。还有一类是恢复场景。某次误操作把 sudoers 改坏了、或者把所有普通用户删掉了唯一能救的入口就是 root 直连。这种时候如果 root 登录是禁用的就只能进单用户模式或者挂载救援镜像了操作成本高好几个量级。判断标准其实很简单这台机器能不能承受“root 密码被猜中”的后果。一次性实验环境能承受长期对外暴露的业务机不能承受。如果属于后者但又有 root 直连的硬需求那就必须叠加密钥登录、来源限制、失败锁定这几项措施具体做法放在第 7 章讲。2. 三条登录路径选哪条取决于你的运维习惯同样是“用 root 登录”做法可以有三种安全性、便捷性、可维护性各不相同。选错了后面全是麻烦选对了就是改两行配置的事。2.1 密码直登最快落地也最容易被扫做法只有两步解开 root 密码锁把PermitRootLogin设成yes。五分钟内能跑通新手首选。代价是任何一台公网机器一旦开了 22 端口和 root 密码登录通常在几小时内就会开始收到暴力破解尝试慢的也就一两天。我做过一个小统计在一台对外开放的测试机上开密码 root 登录24 小时内的失败登录尝试是四位数全是从各种来源轮换 IP 进来的自动化扫描。密码强度再好也架不住长期磨尤其是如果密码是人手敲的、带规律的那种。所以密码直登的定位应该是内网环境、临时环境、或者作为密钥登录打通之前的过渡手段。过渡完就应关掉别留着。2.2 密钥直登多花二十分钟省掉后续所有担心密钥登录的核心逻辑是服务端保存公钥客户端持有私钥握手时做一次挑战应答验证。整个过程没有密码在网络上传被嗅探也无意义。这是我最推荐的做法也是prohibit-password这个默认值真正想引导的方向。配置上比密码方式多两件事生成密钥对、把公钥写到 root 的authorized_keys里。麻烦点在于——如果你现在根本登不进 root怎么把公钥写进去答案是分两步走先用普通用户登录通过sudo把公钥投递到/root/.ssh/然后再关掉密码登录。具体的投递命令和权限处理在第 5 章展开。2.3 普通用户登录后提权最稳妥也最容易被嫌麻烦完全不碰 root 的 SSH 登录普通用户登进去之后sudo -i或su -拿到 root shell。这样做的好处是审计链路完整——谁登录的、什么时候登录的日志里记的是真实用户名而不是笼统的root。多个人共用一台机器的时候这个区别非常关键。但它解决不了 1.2 里提到的几个场景自动化脚本、恢复模式、单用户环境。而且每次都敲一遍提权命令长期的摩擦成本是实实在在的。我的实际选择是环境有审计要求就用这条没有就用密钥直登。密码直登只在完全可控的内网里用且设一个过期时间提醒自己关掉。3. 改配置之前的现场勘查确认 sshd 实际生效的配置这是整篇文章里我觉得最值钱的一节。因为 80% 的“改了不生效”问题都出在没搞清实际生效的配置到底是什么。Ubuntu 的 SSH 配置不是一个文件而是一组文件按特定规则拼出来的结果。3.1 sshd -T 与 sshd -t 的区别很多人会混sshd -t只做语法检查通过就静默退出不通过打印错误行号。它不会告诉你某个值的最终结果。真正有用的是大写 T 的那个sudo sshd -T | grep -i permitrootlogin sudo sshd -T | grep -i passwordauthentication sudo sshd -T | grep -i includesshd -T会把所有生效配置项完整打印出来相当于“跑完所有 include、合并完所有覆盖之后的最终结果”。这个输出才是判断依据cat /etc/ssh/sshd_config看到的只是其中一层。我习惯在改配置前后各跑一次把结果重定向到文件比对sudo sshd -T /tmp/sshd_before.txt # 改配置 sudo sshd -T /tmp/sshd_after.txt diff /tmp/sshd_before.txt /tmp/sshd_after.txt这个 diff 会精确告诉你哪些项真的变了。如果改完 diff 是空的说明你改的那行根本没被加载——问题在加载顺序上不在值本身。3.2 那些藏在 sshd_config.d 里的覆盖文件从 Ubuntu 20.04 开始/etc/ssh/sshd_config顶部多了一行Include /etc/ssh/sshd_config.d/*.conf这一行的威力在于OpenSSH 的配置解析规则是“同一个关键字第一个出现的值生效”而不是很多人的直觉“后面的覆盖前面”。因为 Include 在最顶部所以/etc/ssh/sshd_config.d/目录下的文件里的设置优先级高于主文件后面的设置。列一下目录里有什么ls -l /etc/ssh/sshd_config.d/云厂商的镜像基本都会在这里放一个以数字开头的文件内容可能是PasswordAuthentication no PermitRootLogin prohibit-password你在主文件的第 40 行把PermitRootLogin改成了yessshd -T一看还是prohibit-password——因为 d 目录里的那个文件先被加载了。这不是玄学是规则。处理方式有两种我倾向第二种第一种是直接改 d 目录里的文件。缺点是如果那个文件是某个自动化工具生成的比如云初始化程序下次开机可能被重新写回去。第二种是在 d 目录里新建一个数字更大的文件比如99-local.conf把需要的设置写进去。因为文件按字典序加载99-排在后面所以它反而抢不到“第一个出现”的位置……等等这里要纠正一个常见误解数字大的文件不一定优先。因为规则是第一个生效而 Include 使用通配符时会按字典序展开所以50-cloud-init.conf会先于99-local.conf被读取50-里的设置赢。那正确做法是什么要么用数字更小的文件名比如00-override.conf抢占先机要么直接改掉那个50-文件要么把Include这一行从主文件顶部挪到文件末尾。三种做法都可以我通常选第一种因为它不动别人的文件、也不改主文件结构回滚时删掉自己那个文件就干净了。提示不管用哪种做法改完一定用sshd -T | grep验证别凭文件名大小猜优先级。3.3 Match 块和 AllowUsers 也会拦人还有两个容易被忽略的拦截点。第一个是Match块。Match User root或Match Address这种条件块里如果写了PermitRootLogin no在条件命中时它会覆盖全局设置。检查方法grep -n -i match\|allowusers\|denyusers\|denygroups /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf第二个是AllowUsers和DenyUsers。一旦配置了AllowUsers那些不在白名单里的用户就全部被拒绝即使PermitRootLogin yes也没用。如果机器是别人交接给你的这项很可能已经设了。4. 放开 root 密码登录的完整操作链路假设你已经确认了 1.2 的场景成立、确实需要密码直登下面是完整步骤。每一步我都写了“为什么要做”别跳步。4.1 给 root 设置密码同时确认状态sudo passwd root系统会提示输入两次新密码。密码我建议至少 16 位、包含大小写和特殊字符因为这类账号一旦对外就是重点照顾对象。别用字典词加年份这种组合自动化的字典库里全都有。设置完立刻确认状态sudo passwd -S root sudo grep -E ^root: /etc/shadow | cut -d: -f1,2 | cut -c1-20第一条应该显示第二列是P。第二条会输出root:$6$...这种$6$表示 SHA-512 加密说明密码真的写进去了。如果看到的是root:!或root:*说明还是锁定状态得回去查为什么passwd没生效——有时候是 PAM 配置或者只读文件系统的问题。4.2 修改 PermitRootLogin注意落点我推荐的做法是在 d 目录里建一个优先级最高的文件sudo tee /etc/ssh/sshd_config.d/00-root-login.conf /dev/null EOF PermitRootLogin yes EOF用00-前缀是因为字典序最靠前能保证它是“第一个出现的值”。tee加 heredoc 的写法比echo 更稳避免因为重定向权限问题写失败还看不出来。如果不想动 d 目录也可以直接改主文件但改完必须用sshd -T确认生效sudo sed -i s/^#\?PermitRootLogin.*/PermitRootLogin yes/ /etc/ssh/sshd_config这条sed会把注释掉的行也一并替换成新值比手动编辑不容易漏掉#号。但注意如果文件里有多行匹配sed会全部替换先用grep -n看一眼有几处。4.3 语法检查 → 重载 → 再检查顺序很重要不要直接重启sudo sshd -t # 语法检查静默即通过 sudo systemctl reload ssh # 重载配置不断开现有连接 sudo sshd -T | grep -i permitrootlogin # 确认生效值重载和重启的区别在这里很关键reload只是让 sshd 重新读配置已经建立的连接不受影响restart会把所有现有连接断掉如果你正在通过 SSH 操作这台机器重启会直接把你踢出去。唯一需要restart的场景是改动了监听端口或监听的 IP 地址因为这些是 socket 层面的设置reload 改不动。有一点版本差异要说清楚Ubuntu 上 SSH 的服务单元名是ssh而不是sshd。systemctl restart sshd在多数 Ubuntu 版本上会报找不到单元。如果你习惯性敲sshd可以记一下这个区别。4.4 从另一台机器实测别在当前会话里猜最忌讳的做法是在当前连接里改配置然后重连试试。因为如果改坏了你可能连不回去。正确做法是先从另一个终端窗口建立新连接测试成功之后再关掉旧会话。ssh -o PreferredAuthenticationspassword -o PubkeyAuthenticationno root目标IP显式指定只用密码认证是为了排除本机已有密钥的干扰。如果你的本地~/.ssh/里恰好有一把能匹配的私钥不指定的话会用密钥登录成功你就误以为密码登录也通了实际上没通。5. 密钥方式的完整落地细节密钥登录比密码登录安全得多但踩坑点也更集中几乎全在权限上。5.1 生成密钥对算法和参数怎么定ssh-keygen -t ed25519 -a 100 -C rootops-2024 -f ~/.ssh/id_ed25519_root逐项解释-t ed25519是目前推荐的算法密钥短、握手快、安全性足够。老的rsa也能用但如果要生成 RSA 密钥长度至少 3072 位2048 位在现在属于勉强及格。-a 100是 KDF 轮数只对带密码短语的私钥有意义作用是提高暴力破解私钥的成本。100 是个比较均衡的值太大会让你每次用密钥都要等一两秒。-C是注释我强烈建议写上用途和日期。半年后你看到id_ed25519_root这个名字可能已经想不起来它是给哪台机器用的了注释能救你。-f指定输出文件名。默认会写到~/.ssh/id_ed25519如果那个文件已存在会提示覆盖。给 root 专用密钥单独命名避免和日常密钥混在一起。5.2 把公钥投递到 root 家目录绕开登录死锁难点在于现在要配的是 root 的密钥但你可能登不进 root。所以走普通用户 sudo 的路径# 在本地执行把公钥内容显示出来 cat ~/.ssh/id_ed25519_root.pub然后登录普通用户在服务端执行sudo mkdir -p /root/.ssh sudo tee -a /root/.ssh/authorized_keys /dev/null EOF ssh-ed25519 AAAAC3Nza...粘贴刚才的公钥内容 EOF sudo chmod 700 /root/.ssh sudo chmod 600 /root/.ssh/authorized_keys sudo chown -R root:root /root/.ssh如果你本地和服务端之间已经通了普通用户也可以用管道一步到位cat ~/.ssh/id_ed25519_root.pub | ssh userhost sudo install -d -m 700 -o root -g root /root/.ssh sudo tee -a /root/.ssh/authorized_keys /dev/null sudo chmod 600 /root/.ssh/authorized_keysinstall -d -m 700这条一次搞定创建目录和设权限比mkdir加chmod分两步更不容易漏。5.3 权限链的每一环都不能松sshd 有个StrictModes设置默认是yes。它要求从家目录往下的整条路径都不能是组可写或其他人可写否则直接拒绝使用这个authorized_keys。这是安全设计但也是新手最常见的失败原因。完整检查链namei -l /root/.ssh/authorized_keys这个命令会一路列到根的每一级权限输出长这样drwxr-xr-x root root / drwx------ root root root drwx------ root root .ssh -rw------- root root authorized_keys关键看三行/root不能是组可写drwxrwx那种.ssh最好是700authorized_keys必须是600。只要看到rwxrwx或者组可写位是 1就是问题所在。日志里对应的错误是这句很容易被忽略Authentication refused: bad ownership or modes for file /root/.ssh/authorized_keys有一部分人会把整个家目录设成777图省事那密钥登录必然失败而且报错信息藏在 verbose 日志里不明显。5.4 用 ssh -v 看握手走到哪一步了客户端加-v能看到完整的认证协商过程比盲猜强得多。关键输出大致是这几段debug1: Authentications that can continue: publickey,password debug1: Offering public key: /root/.ssh/id_ed25519_root ED25519 debug1: Server accepts key: ... debug1: Authentication succeeded (publickey).如果看到Server accepts key之后卡住或者失败说明服务端认了这把钥匙但签名验证没过多半是私钥带密码短语而没进入交互输入。如果压根没有Server accepts key这一行说明服务端根本不认这把公钥回到 5.3 查权限和AuthorizedKeysFile配置。想看更细的服务端视角用ssh -vvv同时服务端配合sudo /usr/sbin/sshd -d -p 2222这个命令在前台起一个调试实例在 2222 端口直接把认证过程的每一步打到终端上是目前最直观的排障手段。用完CtrlC就退不影响正常服务。6. 登不进去时的完整排查链路这一节按“从外到内”的顺序组织照着走一遍基本能定位到根因。6.1 按顺序检查别跳步排查最忌讳东一榔头西一棒子。我的固定顺序是第一步网络层通不通。nc -zv 目标IP 22不通就是网络或防火墙问题跟 SSH 配置无关。这时候查sudo ufw status、安全组规则、以及 sshd 有没有在跑systemctl status ssh。第二步认证方式清单对不对。ssh -v root目标IP 21 | grep Authentications that can continue这行输出会告诉你服务端当前允许哪些认证方法。如果只有publickey而没有password那就是PasswordAuthentication no生效了。这份清单是服务端根据配置算出来的最权威。第三步看服务端日志。Ubuntu 上的服务端日志主要在这几处位置内容适用版本/var/log/auth.log认证相关全部记录20.04 及之前部分 22.04journalctl -u ssh -n 100systemd 收集的服务日志所有使用 systemd 的版本journalctl -t sshd -n 100按 syslog 标签过滤服务未走 journal 的情况我一般先试journalctl -u ssh因为 22.04 之后不少镜像已经不再默认写/var/log/auth.log了直接看文件会扑空。第四步看实际生效配置。就是 3.1 里说的sshd -T。第五步看账户层状态。passwd -S root、chage -l root。账户过期、密码过期这些在日志里也是Permission denied容易被当成配置问题。6.2 Permission denied 的六种成因对照表这个表我整理了很久基本覆盖了我遇到过的所有情况日志关键词根因处理方向Connection closed by authenticating user认证方式完全不匹配或AllowUsers拦了查sshd -T的认证相关项和 AllowUsersFailed password for root密码不对或账户被锁重置密码passwd -S确认状态Authentication refused: bad ownership or modes权限链有问题namei -l逐级查User root not allowed because shell ... is not listed in /etc/shellsroot 的登录 shell 不在白名单改 shell 或加/etc/shellserror: Received disconnect from ...: 3: com.jcraft.jsch.JSchException客户端密钥格式不兼容换算法或转换格式not allowed because account is locked账户锁定usermod -U root或重置密码最后一行那种情况特别隐蔽因为账户锁定状态下即使密钥认证也可能被拒取决于版本和 PAM 配置表现和密码错误完全一样。6.3 云镜像和 Match 块的隐藏拦截这一条值得单独说因为很多人在这上面栽过。不少云厂商的系统镜像在制作时就预置了PermitRootLogin的禁用策略放在 d 目录下的配置里甚至放在/etc/ssh/sshd_config.d/之外的某个初始化钩子里。表现是你改完配置、确认sshd -T输出了yes重启机器之后又变回去了。检查有没有这种“重启后回滚”的东西grep -rn PermitRootLogin /etc/ssh/ /etc/cloud/ /etc/systemd/ 2/dev/null systemctl list-unit-files | grep -i cloud如果发现有 cloud 相关的初始化服务把对应配置改掉或者用systemctl disable关掉那部分的重配行为否则改了也白改。还有一种情况是Match块按来源地址区分策略表面全局是yes但你所在的网段被Match Address命中了no。查法在 3.3 里给了。7. 放开之后必须补的安全措施配置通了不代表可以收工。尤其是密码直登这种等于给全网开了个口子必须叠几层防护。7.1 改端口不是安全措施但它确实有用很多人一上来就把 22 改成高位端口然后觉得安全了。这个认知要纠正改端口不提高任何安全强度它只是降低被自动化扫描的概率。真正的攻击者扫全端口只需要几十秒改端口挡不住任何人。但反过来说改成高位端口能过滤掉 99% 的量产扫描脚本日志干净了、系统负载低了、误报少了运维体验确实好。所以我倾向于“改但别指望它承担安全责任”。改端口需要一个动作防火墙和 SSH 配置同步改否则改完就失联。sudo ufw allow 2222/tcp # 先放行新端口 sudo sed -i s/^#\?Port 22/Port 2222/ /etc/ssh/sshd_config sudo systemctl restart ssh # 端口变了必须 restartreload 不生效 sudo ufw delete allow 22/tcp # 验证新端口能连上之后再删旧规则ufw delete一定要放到最后确认新端口能连之后再执行。顺序搞反了就是自己给自己断网。7.2 fail2ban 与密钥优先的组合策略如果环境允许我推荐这套组合第一层优先密钥认证。把PasswordAuthentication设成no只留pubkey。这一层挡掉所有密码爆破效果最直接。第二层fail2ban 兜底。即使只开了密钥也建议装——密钥爆破虽然不现实但日志里持续的错误尝试会污染审计记录也会占用连接资源。sudo apt install -y fail2ban sudo tee /etc/fail2ban/jail.local /dev/null EOF [sshd] enabled true port 2222 maxretry 3 findtime 10m bantime 1h EOF sudo systemctl enable --now fail2banmaxretry 3配findtime 10m是比较激进的值正常人手滑输错三次密码的概率不高能挡住大部分自动化尝试。bantime 1h是惩罚时长太短没效果太长可能误伤自己。查看封禁状态sudo fail2ban-client status sshd第三层来源限制。如果是固定 IP 的办公网访问直接在防火墙层做白名单SSH 端口只对特定网段开放效果比任何应用层措施都硬。云环境的安全组就干这个事。7.3 留一条回滚通道别把自己锁死这条是血泪经验。每次动 SSH 配置之前先做三件事第一备份配置文件。sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %F)。注意.bak后缀的文件如果放在sshd_config.d/目录里是会被加载的所以备份文件别放那个目录或者用不匹配*.conf的名字。第二开一个独立的会话保持在线。改配置的时候不要关掉当前那个能用的 SSH 连接新连接测试通过再关。这样改坏了至少还有一个活着的 shell 可以回滚。第三准备好带外恢复手段。云主机有控制台的 VNC 登录物理机有 IPMI本地虚拟机可以直接从宿主机进。确认这些手段真的可用——我有一次以为控制台能用真出事的时候发现密码忘了比 SSH 登不上还难受。回滚命令sudo cp /etc/ssh/sshd_config.bak.2024-01-01 /etc/ssh/sshd_config sudo systemctl restart ssh如果连 shell 都进不去只能走控制台那就用挂载救援镜像的方式改回配置或者挂载磁盘到另一台机器上改文件。我个人在实际操作中的体会是Ubuntu 上 root 登录这件事真正的门槛不在“改哪一行”而在“改完之后怎么确认它真的生效了”。sshd -T这个命令我建议你养成肌肉记忆任何一次 SSH 配置改动前后都跑一遍。另外一点是凡是涉及“为什么配置不生效”的问题先怀疑订单顺序和覆盖文件别怀疑软件本身——OpenSSH 稳定运行了二十多年它按规则办事的概率远高于出 Bug 的概率。最后再补一个小技巧把每次成功跑通的完整配置片段包括 d 目录里的文件、防火墙规则、密钥权限单独存成一个文本文件跟着机器走下次换机器的时候直接抄能省掉重新踩一遍坑的时间。

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

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

免费获取报价 →
↑