SSH 登录报错Permission denied和 VNC 登录报错Hint: Caps Lock on,这俩提示我估计做过服务器运维或者远程桌面支持的人都见过,而且十有八九都经历过同一件事:看到报错的第一反应是“密码输错了”,然后反复重试,最后发现根本不是那回事。这两个报错藏着的坑比表面上看起来多得多,尤其是当你同时在维护几台服务器、记着好几套密码的时候,它们会把你的排查思路带进死胡同。这篇文章我不打算讲什么高深理论,就围绕这两个报错,把我这些年实测过的排查链路、真正的原因分布、以及那些文档里不会写的细节,全部拆开揉碎讲一遍。
先说清楚适合什么人看:刚接触 Linux 服务器的新手、需要给同事远程做技术支持的人、自己搭过 VNC 但又时不时登不上的朋友。如果你已经熟到扫一眼日志就知道问题所在,这篇里的部分内容对你可能偏基础,但第三节和第四节里的配置细节、权限要求,包括日志关键行的解读方式,还是值得快速过一遍。
1. Permission denied 不等于密码错误:先分清五种常见假象
很多人看到Permission denied, please try again就默认是密码不对,其实这是最大的误判。SSH 的密码认证失败会报这个,但密钥认证失败、用户不存在、账户被锁、甚至客户端本地的键盘映射出问题,最终被服务端拒绝时,呈现给你的几乎都是同一句话。我统计过自己踩过的坑,真正是“单纯密码记错”的情况其实只占一小部分,多数时候是下面几种假象在干扰判断。
1.1 用户名就写错了,但客户端不会告诉你
SSH 登录时你输入的是用户名@地址,如果用户名本身不存在或者写错,服务端在默认配置下不会直接说“这个用户不存在”,而是同样返回Permission denied。这么做的目的是防止攻击者通过报错猜测有效用户名。很多人这时候会死磕密码,把大小写、特殊字符试了个遍,其实应该先确认用户名本身。
你可以用ssh -v user@host看客户端的输出,它会明确告诉你正在尝试的用户名是什么。另外一个我常用的办法是换一个自己确定存在的用户登录试试,如果换用户之后能进,那问题十有八九出在用户名上,而不是密码。
1.2 键盘布局和密码里的特殊字符
这个坑隐蔽得很。远程服务器密码里如果包含@、#、~、^这类字符,而你的本地键盘布局是中文输入法状态,或者笔记本没有开启数字小键盘切换,打出来的字符和你想的完全不是同一个。比如中文输入法下按@可能直接没反应,或者输入了全角字符,服务端收到的密码自然不对。
更坑的是,VNC 那类图形化登录界面里密码框是密文的,你压根看不到自己输了个啥。SSH 终端虽然也是密文,但起码可以在本地先开个文本编辑器,切到英文布局,把密码敲进去看一眼再复制粘贴。记住一个原则:涉及密码的操作,先把输入法切到英文、把大写锁定关掉再动手。
1.3 密钥优先,密码被忽略
如果你的~/.ssh目录里配置了密钥,SSH 客户端默认会优先尝试公钥认证。这时候就算密码是对的,只要密钥认证失败或者服务端不接受该密钥,客户端也不会回头用密码去试(至少 OpenSSH 默认行为是这样,除非你在命令行加了-o PreferredAuthentications=password)。于是你看到的现象就是:密码明明没错,但一直Permission denied。
我遇到过一次最离谱的情况:某台服务器上,我发现.ssh目录里不知道什么时候多了一个权限是 777 的id_rsa,客户端加载私钥的时候直接拒绝使用“权限过于开放”的密钥,结果卡在密钥认证环节反复失败。删掉那个密钥文件、改用密码登录就好了。所以,遇到 Permission denied,先确认是不是有密钥在“捣乱”,用ssh -v看认证过程到底走的是哪条路。
1.4 账户锁定和 PAM 策略
服务端的 PAM 配置如果开启了账户锁定模块(比如pam_faillock),连续输错几次密码之后,账户会被临时锁定。这时候你拿到正确密码也登不进去,依然显示Permission denied。属于“密码没错,但被锁了”。
服务端日志里会明确看到Account locked due to N failed logins之类的记录,而客户端这边除了Permission denied什么都看不出来。这个假象最有迷惑性,因为你压根不知道账户被锁,只会觉得“难道我又记错密码了”。
1.5 防火墙或 Fail2ban 在背后拦截
还有一种情况更隐蔽:客户端连上了,TCP 握手也完成了,但还没到密码认证这一步就被服务端的 Fail2ban 丢进黑洞。表现为 SSH 端口能通、输入用户名之后要等很久才返回Permission denied,或者干脆直接断连。这其实是服务端把某个 IP 列入黑名单导致的,换一台机器或者换个网络就能避开,但只要你的 IP 还在黑名单里,密码再对也没用。
这个假象最好用ssh -vvv识别——如果你发现程序卡在banner exchange阶段很长时间,之后才报错,多半是在防火墙或 Fail2ban 层面被卡住了,而不是密码本身的锅。
2. 真正的破案现场:SSH 服务端日志与客户端详细模式
排查 SSH 登录问题,别在客户端反复试密码,那是浪费生命。真正告诉你“为什么被拒”的,是服务端的日志。我通常的做法是:一边在客户端用ssh -vvv重试,一边在服务端盯日志,两边对照,半小时以内基本都能定位。
2.1 服务端日志去哪里看
Debian 系的系统看/var/log/auth.log,Red Hat 系看/var/log/secure,用 systemd 的发行版还可以直接journalctl -u sshd(或者journalctl -u ssh,具体看你的服务名)。日志里会出现这几类关键行:
| 日志关键内容 | 含义 | 对应解决方案 |
|---|---|---|
Failed password for invalid user xxx | 用户名不存在或写错 | 核实用户名 |
Failed password for xxx from IP | 密码真的不对 | 重置密码或检查键盘布局 |
User xxx not allowed because account is locked | 账户被锁定 | passwd -u解锁 |
Connection closed by authenticating user xxx | 密钥认证过程中直接断连 | 检查公钥格式、authorized_keys 权限 |
Unable to negotiate with IP port xxx: no matching host key type | 客户端与服务端密钥算法不匹配 | 在服务器配置中开放相应算法或更新客户端 |
我建议你把journalctl -u sshd -f挂在一个终端窗口里,然后去客户端重试一次登录,观察日志实时输出的那几行。这一招能瞬间帮你把问题从“猜”变成“看”,比你在客户端瞎琢磨高效十倍。
2.2 客户端ssh -vvv的关键行解读
-v级别已经能看到认证过程的大致走向,-vvv几乎把每一步都摆出来了。我在排查时最关注这几行:
debug1: Next authentication method: publickey—— 说明现在正在尝试公钥认证。debug1: Offering public key: ...—— 客户端把本地某个公钥发给服务端了,但这里不代表成功,只是“试探”。debug1: Authentications that can continue: publickey,password—— 服务端说你还可以继续用公钥或密码,说明公钥尝试被拒了。Permission denied (publickey,password).—— 所有可用认证方式都试过且失败了。
有一个容易忽略的细节:如果客户端输出的最后一行只有publickey而没有password,说明服务端根本没开放密码登录。你输多少次密码都没用,得先改服务端配置或者改用密钥。
2.3 判断问题归属:用户问题还是配置问题
知道怎么在两端收集信息之后,判断归属就变得简单了:服务端日志显示“密码错误”,那就是用户侧问题(密码、键盘、记错);日志显示“invalid user”,那是用户名问题;日志显示“Connection closed”且客户端报错在认证协商阶段,那是配置或网络中间设备问题。只要你能看到日志,就不要再反复试密码,先读日志,读懂了再动手。盲目重试不仅浪费时间,还可能触发账户锁定策略,给自己挖更大的坑。
3. 授权文件与配置文件的隐蔽雷区:authorized_keys 权限和 sshd_config 细节
排除了上面那些用户层面的假象之后,如果依然Permission denied,那问题大概率落在服务端的授权文件和配置上。这里面的坑比看上去多得多,而且很多是“改一次就坏、坏一次就很久查不出来”的类型。
3.1 权限要求不是玄学,是安全底线
.ssh目录权限必须是700,authorized_keys文件权限必须是600,这俩是硬性要求。OpenSSH 对权限过于宽松的密钥文件会直接拒绝使用,原因很简单——如果其他用户能读你的私钥或改你的授权文件,那密钥认证就失去了意义。
我身边一位同事就踩过这个坑:他想给authorized_keys加一个新公钥,为了方便直接chmod 777 authorized_keys,改完发现所有密钥登录全部失效,而且报错就是笼统的Permission denied。他在客户端折腾了两个小时,最后我用ssh -vvv看到Authentication refused: bad ownership or modes,一看权限,秒破。改完权限之后,用ls -ld ~/.ssh && ls -l ~/.ssh/authorized_keys确认一下,别假装自己记住了。
如果是通过 root 帮普通用户添加公钥,还要注意文件属主是不是对应用户,比如把 root 生成的authorized_keys直接复制给某个用户,属主是 root,用户登录一样会被拒。这是权限问题里最容易被忽略的一环。
3.2 sshd_config 里的隐形开关
/etc/ssh/sshd_config中这几个参数基本决定了登录成败:
PasswordAuthentication yes—— 是否允许密码登录,如果为 no,密码正确也没用。PubkeyAuthentication yes—— 是否允许公钥登录,如果为 no,密钥配得再好也没用。PermitRootLogin prohibit-password—— 字面意思是禁止 root 用密码登录,但不同版本默认值不同,有时候你以为开了密码登录,其实 root 就是被排除在外的。AllowUsers/AllowGroups—— 白名单机制,只要不在这列表里的用户一律拒绝,而且提示也是 Permission denied。
我在某个项目里搭过一批测试服务器,复制配置的时候不小心把AllowUsers写成了AllowUser,配置文件里的旧参数被覆盖,所有用户全部无法登录。服务端日志里能看到User xxx not allowed because not listed in AllowUsers,但客户端显示还是一样的Permission denied。改完sshd_config之后,至少用sshd -t做一次语法检查,同时保持当前的 SSH 连接不退出,避免把自己锁在外面。
3.3 密钥类型和注释符的坑
有公钥加进去了却依然登不上,还有一种情况是公钥格式写错了。authorized_keys文件里每行只能有一条公钥,以ssh-rsa、ssh-ed25519等开头。如果你复制的时候不小心把ssh-rsa AAA...里的ssh-rsa丢了三两个字符,或者把注释里的#放在了不该放的位置,整条密钥可能直接失效。另外,老版本 OpenSSH 不认新版的ssh-ed25519格式,新版本对ssh-rsa的默认策略也变了,导致某些旧密钥连接时报no matching host key type。这种情况在客户端-vvv的输出里会出现no mutual signature algorithm这类明确提示,根源是服务端和客户端各自只支持不同算法,需要手动在服务端启用对应算法,或者升级客户端。
4. VNC 的 “Caps Lock on” 提示:表象与真实原因
VNC 登录报错里弹出一个Hint: Caps Lock on,这个提示的设计初衷是好的——它想告诉你说,当前密码框检测到大写锁定是开着的,如果你密码里包含小写字母,可能会因此输错。但实际使用中,这个提示经常有误导性,你明明关掉了大写锁定,它还是会弹出来。那说明什么?说明问题根本不在 Caps Lock 上。
4.1 为什么 VNC 会专门提示 Caps Lock
VNC 的密码认证机制特殊在它是自己的独立密码体系。常见的 VNC 服务(比如 TigerVNC、x11vnc、TightVNC)默认使用独立的密码文件,这个密码是安装时用vncpasswd生成或设置的,和系统用户的登录密码是两套东西。因此,VNC 登录界面无法像 SSH 那样做服务端账户认证,它只能靠密码本身对不对来判。如果你输入的密码和密码文件里保存的不一致,VNC 服务为了“引导”用户,会在返回错误时附带一个提示:检查大写锁定。这本来是对“用户输错密码”的一种猜测性提示,但很多人直接根据提示去检查 Caps Lock,结果发现没问题,然后又陷入死循环。
4.2 密码大小写确认之后,密码对不上的真实原因
我遇到过的 VNC 登录失败,根因分布大概是这样:
- 把 SSH 密码、系统登录密码当成了 VNC 密码——这是最常见的坑。VNC 密码是独立设置的,跟系统密码完全可以不一样。
vncpasswd设置时没有回显,输错了自己不知道。VNC 密码输入框是密文的,设置时你看着像在打空气,很容易在设置阶段就把错误密码存了进去。当时设的时候没注意大小写,后面登录自然怎么输都不对。- 密码文件权限不对:
~/.vnc/passwd文件权限如果不是600,部分 VNC 服务出于安全考虑会拒绝读取或认作无效。这就表现为密码明明没变但突然登录失败。 - VNC 密码的长度限制:很多 VNC 实现只取密码的前 8 位,超过 8 位的部分会被悄悄截断。你设置了一个 10 位的密码,登录时输入 10 位,如果输入法或键盘映射让前八位中某一位出错,服务端拿到的密码就废了。
- 密码过期或账户锁定:在 x11vnc 开启了 PAM 认证的前提下,VNC 其实是在调系统账户认证,这时候如果系统用户密码过期、账户被锁,VNC 登录也会失败,但提示同样可能包含
Caps Lock on。
4.3 键盘映射差异:Windows 客户端和 macOS 客户端的不同表现
VNC 客户端在不同操作系统上的键盘映射处理并不统一。Windows 下的某些 VNC Viewer 对大小写锁定的处理方式跟服务端不一致,可能你本地看到 Caps Lock 是关的,但服务端收到的按键流仍然是“大写锁定生效”的状态。更诡异的是某些客户端在密码输入框里会自动忽略你的 Caps Lock 状态,直接把按键映射成大写字母。
处理方法是先在本地任意文本编辑器里输入一遍密码,确认大小写和实际内容,然后再复制到 VNC 客户端,尽量让客户端以“粘贴”方式而不是“击键”方式输入密码。另外,如果客户端有“自动锁定大小写”这类选项,先关掉再试。
4.4 Silently authenticate: 日志才是唯一的裁判
和 SSH 一样,VNC 服务端也会记录认证尝试的结果。TigerVNC 的日志在~/.vnc/*.log里,x11vnc 可以用-o参数指定日志文件。如果日志里明确写了Authentication failed,那问题就是在密码本身;如果日志里没有认证失败记录,只是连接被断开,那就要去看防火墙、显示编号端口(默认 5900 起)是否可达。被提示误导的时候,先把日志找出来看一眼,比按提示狂切大小写有效得多。
5. 一张表理清排查顺序:从快速判断到根因定位
说了这么多,最后给一份可以直接照着走的排查清单。不用记那么多理论,按这个顺序操作,绝大多数问题都能在十分钟内定位。
5.1 SSH 侧快速排查表
| 步骤 | 操作 | 判断依据 | 对应处理 |
|---|---|---|---|
| 1 | 确认用户名正确 | 服务端日志是否显示invalid user | 修正用户名 |
| 2 | 本机输入法切英文,确认密码无误 | 换已知密码的用户在小范围试一次 | 重置密码或用已知账户验证 |
| 3 | ssh -v观察认证方法 | 看是走了 publickey 还是 password | 按需加-o PreferredAuthentications=password |
| 4 | 查看服务端日志 | Failed password、not allowed、locked等关键词 | 按关键词修正 |
| 5 | 检查authorized_keys和.ssh权限 | bad ownership or modes | chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys |
| 6 | 检查sshd_config里的白名单与认证开关 | AllowUsers、PasswordAuthentication | 修改后sshd -t并重载 |
5.2 VNC 侧快速排查表
| 步骤 | 操作 | 判断依据 | 对应处理 |
|---|---|---|---|
| 1 | 确认 Caps Lock 状态并关闭 | 本地编辑器里输入密码检查大小写 | 关闭 Caps Lock 后重试 |
| 2 | 确认用的是 VNC 独立密码,不是系统密码 | 你是否设置过vncpasswd | 用vncpasswd重新设置独立密码 |
| 3 | 确认密码文件权限 | ls -l ~/.vnc/passwd | 修改为600 |
| 4 | 检查密码长度 | VNC 密码是否超过 8 位 | 缩短到 8 位以内再试 |
| 5 | 看 VNC 服务端日志 | 是否有Authentication failed | 根据日志类型处理 |
| 6 | 确认端口和防火墙 | 5900/5901 等端口是否可达 | 放行端口或检查显示编号 |
5.3 组合场景下的快速判断
有时候 SSH 能登,VNC 不能登,或者反过来。这里说几个我实际遇到的组合:
- SSH 能登、VNC 不能登:几乎可以排除系统账户本身的问题,重点查 VNC 独立密码、密码文件权限、VNC 服务进程是否在监听对应端口。
- SSH 不能登、VNC 能登:说明系统账户和密码没问题,问题很可能出在 SSH 密钥、服务端
sshd_config的认证开关、AllowUsers白名单上。 - 两个都登不上:优先怀疑系统账户锁定或密码过期,先到服务端本地用
passwd或图形界面确认账户状态,再回头处理远程工具。 - 换网络后 SSH 能登、原网络不能登:大概率是 IP 被防火墙或 Fail2ban 封禁,别改密码,先解封。
6. 我自己的处理习惯与一个冷门小技巧
最后分享一点个人经验。我平时排查这类问题,绝不会在同一个终端里重复尝试超过三次。第一次失败,我会去服务端打开日志;第二次失败,我会对照ssh -vvv输出和服务端日志;第三次失败还查不出来,我就放弃“猜”,直接用 tcpdump 在服务端抓包看认证过程的交互顺序——虽然这个手段对新手来说有点重,但对判断“密钥协商到哪一步才断的”极其有效。
再送一个冷门但很实用的小技巧:如果是自己临时用的服务器,密码中包含大小写字母,可以先在登录信息里故意写成小写,引发一次登录失败,再观察服务端日志里的Failed password for xxx from IP这一行。日志里你录入的密码是明文回显的(在 auth.log 里是Failed password for test from 192.168.x.x port 22 ssh2,不显示密码内容,但在某些配置下会显示用户名),虽然看不到密码本身,但能看到服务端是否认为这个用户存在、是否因某种策略拒绝。对于判断是“用户不存在”还是“密码错误”,这一步零成本且极快。
说实话,这两个报错单独看都是入门级别的问题,但把它们叠加在真实的服务器环境里,背后隐藏的用户锁定、权限问题、密钥优先级、独立密码机制,任何一个环节出错都会让排查变得异常痛苦。把这些链条理顺之后,你会发现大多数“登录失败”问题根本轮不到重装、轮不到重置系统,往往只是一个小配置、一个权限、一个客户端输入状态的问题。