每次连服务器看到Permission denied这行字我心里都会先叹一口气。搞运维这些年这句报错出现的频率高到离谱而且坑你的方式千奇百怪有时候是密码没错但就是进不去有时候是密钥明明配好了却被拒之门外还有时候服务端日志里压根不给你一句完整解释。这篇文章就专门把 SSH 连远程服务器时Permission denied这个老大难问题掰开揉碎讲清楚从报错本义到排查手法再到几种高频场景vscode 连接、git 认证、树莓派登录、免密配置失效的定向解法全部按真实环境里的路子来。如果你是刚接触服务器的新手或者被这个问题折磨过几次的老手这篇都能给你点有用的东西。下面直接从最基础但又最容易被搞混的错误类型讲起。1. 先搞清楚这句 Permission denied 到底在说哪个环节1.1 三种常见 SSH 报错别混淆我见过太多人在群里问SSH 连不上显示 permission denied结果贴出来的报错压根不是这句。SSH 连不上的报错大致分三类方向完全不同报错内容真实含义常见原因Permission denied (publickey,password)认证失败密码或密钥被拒绝密码错误、密钥不匹配、权限配置错误Connection refused连接被拒绝服务端没监听起来sshd 未启动、端口不对、防火墙拦截Connection timed out连接超时网络不通IP 不可达、防火墙丢包、安全组没放行Permission denied是三者里唯一明确表示你已经摸到了服务端、SSH 服务也活着、但身份验证这关没过的报错。所以第一步就是确认你看到的到底是不是这句别拿着Connection refused去排查密钥权限那就是南辕北辙。还有一种容易混淆的是Host key verification failed这跟用户认证无关是客户端第一次连新机器或者服务端密钥变了时才会弹出来属于信任关系问题。排障时千万别把它跟Permission denied混在一起。1.2 为什么服务端报错如此吝啬你有没有好奇过为什么 SSH 报错不直接告诉你用户名不存在或者密码错误这是故意设计的。如果服务端明确告诉你这个用户存在但密码错了黑客就能通过试错穷举合法用户名。所以 OpenSSH 对所有认证失败的场景统一回复Permission denied细节全部藏在服务端日志里。这意味着排障的基本盘就一句话不要光盯着客户端报错瞎猜去看服务端日志。Permission denied只是结果日志才是原因。注意有些修改过的 SSH 服务端比如蜜罐、定制化版本反应可能不一样但主流发行版自带的 OpenSSH 都遵循这个设计原则。2. 排障第一步永远是翻日志2.1 服务端日志去哪找不同发行版日志路径不一样我列个速查表系统日志位置 / 查看方式Ubuntu / Debian/var/log/auth.logCentOS / RHEL / Fedora/var/log/secure通用 systemd 环境journalctl -u ssh -u sshd --since todayFreeBSD / macOS/var/log/auth.log或/var/log/system.log最稳的办法是不管什么发行版直接跑journalctl -u ssh -u sshd --no-pager -n 100或者tail -f对应日志文件。我自己习惯双窗口操作一个窗口tail -f /var/log/auth.log实时盯着另一个窗口用客户端连接复现问题报错配合日志一起看十次有八次能立刻定位。2.2 一份真实形态的日志逐行拆解给你看一段典型的失败日志隐私部分脱敏Feb 20 11:23:45 web01 sshd[12345]: Failed password for invalid user admin from 192.168.1.100 port 52310 ssh2 Feb 20 11:23:47 web01 sshd[12345]: Connection closed by authenticating user admin 192.168.1.100 port 52310 [preauth]第一行里invalid user admin非常关键它直接告诉你服务器上根本没有 admin 这个账号。如果换成Feb 20 11:24:01 web01 sshd[12346]: Failed password for root from 192.168.1.100 port 52312 ssh2那说明用户存在单纯是密码输入错误。再往后如果日志里出现Feb 20 11:25:10 web01 sshd[12347]: Connection closed by authenticating user zhangsan 192.168.1.100 port 52320 [preauth]而且前面没有Failed password只有Connection closed这是什么意思这通常表示客户端在认证还没完成时就主动断开了。最常见的原因是客户端配置了某种认证方式且失败后直接放弃你继续往下看 4、5 章就明白了。2.3 日志行关键词速查表日志关键词含义对应的方向invalid user xxx用户名不存在检查账号名别拿小号去登Failed password for xxx密码错误检查密码检查认证方式Permission denied (publickey)密钥认证失败检查密钥、权限、授权文件Connection closed ... [preauth]认证前连接被关闭客户端策略或服务端限制User xxx not allowed用户被显式禁止访问检查 AllowUsers 等策略Account locked/expired账号锁定或过期检查账户状态pam_unix(sshd:auth)带authentication failurePAM 认证失败密码/账户过期/二次验证问题日志不撒谎它比任何客户端报错都诚实。排障时第一步先把日志拉出来能少走一大半弯路。3. 密码认证这条线的三个暗坑3.1 用户名和密码看起来没错但就是登不上先说一个我实际踩过的坑。有一次同事怎么都连不上一台新装好的 Ubuntu 服务器密码确认了 N 遍没问题最后发现他用的是管理员给他发的文档里的用户名文档写的是root但那台机器的 OpenSSH 配置里PermitRootLogin是prohibit-password明文密码登 root 直接被拒。日志里对应Failed password for root from 192.168.1.100 port 52330 ssh2后来换成普通用户加sudo才进去。所以第一步先确认三件事用户名拼写是否精确、大小写是否对、目标账号在服务器上是否真的存在。不要拿自己本机的用户名惯性代入特别是有很多人的本机用户名和服务器用户名不是同一个。注意登录时用户名是严格区分大小写的ZhangSan和zhangsan是两个账号。3.2 PasswordAuthentication 被偷偷关掉了现在很多一键加固脚本会执行sed -i s/#PasswordAuthentication yes/PasswordAuthentication no/ /etc/ssh/sshd_config。你要是接手了一台被加固过的服务器密码设置得再正确也会被拒。客户端侧看到的报错一般长这样ssh userserver userservers password: Permission denied, please try again.但其实服务端日志里写的是sshd[12348]: userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth] sshd[12348]: Connection closed by authenticating user ...嗯严谨点说如果纯密码模式被禁用日志通常更像是这样客户端发来 password 请求但服务端并不响应。不过很多系统默认PasswordAuthentication yes被改了就很有迷惑性。检查方法sudo sshd -T | grep -i passwordauthentication输出passwordauthentication no就是被关了。想临时开的话编辑/etc/ssh/sshd_config改成PasswordAuthentication yes然后sudo systemctl restart sshd。注意sshd -T这个姿势很有用它直接输出生效配置而不只是文件里的文本能帮你识破那些被 include 文件覆盖的坑。3.3 客户端认证顺序与交互式登录的差异OpenSSH 支持好几种密码类认证password和keyboard-interactive。很多发行版默认PasswordAuthentication yes但KbdInteractiveAuthentication可能还没开或者反过来。某些命令行工具比如部分旧版 python 的 paramiko 脚本用 keyboard-interactive 方式发密码服务端不支持就会失败。这时你看到的现象是客户端里密码输得飞快刷一下就Permission denied。如果你用的是 ssh 命令本身可以用-o PreferredAuthenticationspassword -o PubkeyAuthenticationno强制只走密码认证快速排除是不是密钥环节在捣乱。这也是排查第一条实用命令。4. 密钥认证的 Permission denied重灾区现场4.1 报错长相和日志形态密钥认证失败时最经典的客户端报错是userserver: Permission denied (publickey).服务端日志对应常见两种Failed publickey for zhangsan from 192.168.1.100 port 52331 ssh2: RSA SHA256:xxxx...或者User zhangsan from 192.168.1.100 not allowed because not in group allow前者代表公钥验证没通过后者则是更上层的访问控制。公钥验证没通过的原因通常是这几个服务器端authorized_keys里没有对应的公钥、公钥文件内容格式错误、文件权限不对、或者客户端私钥权限不对。4.2 服务器端 authorized_keys 权限与目录权限这是整个 SSH 配置里最容易出问题的地方也是最需要死记硬背的部分。OpenSSH 对服务器端~/.ssh目录和authorized_keys文件的权限有严格要求防止其他用户把你目录里的公钥文件改掉来黑你的账号。需要满足的权限规则chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chown -R $(whoami):$(whoami) ~/.ssh为什么是 700 和 600因为如果.ssh目录对其他用户可写比如 775 甚至 777SSH 服务端认为你有被人塞入恶意公钥的风险直接拒绝使用该目录下的认证文件。如果authorized_keys是 644理论上其他用户能读不危险但不能写必须保证有些配置比较严格的环境也会拒绝。把这两条权限改成上面的值能解决大量密钥认证Permission denied的问题。还有个大坑是家目录本身权限。如果/home/zhangsan被 chmod 成 777或者属主不对StrictModes 一卡同样拒绝。修正方式chmod 755 /home/zhangsan chown zhangsan:zhangsan /home/zhangsan提示StrictModes默认是 yes。如果你不想排权限问题可以在/etc/ssh/sshd_config里设StrictModes no临时绕过但这会降低安全性不建议长期开。4.3 客户端私钥的权限要求服务器端权限搞定了客户端私钥权限不对同样失败。在 Linux / macOS 上chmod 600 ~/.ssh/id_rsaWindows 用户如果是用 cmd 或 PowerShell 跑 ssh私钥权限检查没那么苛刻但如果你是 WSL 环境就完全遵循 Linux 那套。很多人在 WSL 里把密钥放在/mnt/c/Users/xxx/.ssh/下权限是 Windows 文件系统那种OpenSSH 直接报bad permissions这时一定要把密钥复制到 WSL 原生文件系统里再chmod 600。4.4 多个密钥并存时的选择问题Permission denied (publickey)还有一种隐蔽场景你本地~/.ssh下有好几个密钥客户端默认会按固定顺序逐个尝试如果第一个密钥不对通常不会自动尝试后面那个匹配的高版本会尝试所有但行为不尽相同。Git 连接时报错多半和这个有关具体在第 6 章展开。5. 服务端那些藏着的限制策略5.1 root 登录的默认策略现在主流发行版对 root 的 SSH 登录限制非常严格。Ubuntu 22.04 的/etc/ssh/sshd_config里默认是PermitRootLogin prohibit-password意思是 root 可以拿密钥登录但不能用密码登录。CentOS 7/8 老版本默认是PermitRootLogin yes但新版也有很多改成了prohibit-password。所以如果你用 root 密码登录被拒优先看这个参数。改成PermitRootLogin yes可以放行但生产环境不建议建议用普通用户 sudo。sudo sshd -T | grep permitrootlogin5.2 用户账号被锁死nologin、过期、锁定密码还有一种看起来完全一头雾水的Permission denied你确定密码没错、权限没问题、配置也正常但就是登不进去。这时候查一下账号本身状态sudo passwd -S zhangsan输出里如果是L开头的锁定位说明密码被passwd -l锁过。如果用户 shell 是/sbin/nologin或/usr/sbin/nologin那反而会提示This account is currently not available不是Permission denied。真正容易被忽略的是账户过期sudo chage -l zhangsan如果Account expires显示过去的日期登录会被 PAM 拦截。这种场景在日志里能看到pam_unix(sshd:auth): authentication failure; logname uid0 euid0 ttyssh ruser rhost192.168.1.100 userzhangsan但客户端看到的还是冷冷一句Permission denied。5.3 AllowUsers、AllowGroups、DenyUsers 的显式控制/etc/ssh/sshd_config里可以配AllowUsers zhangsan lisi AllowGroups sshusers DenyUsers guest这些规则很直接不在名单里的人一律拒绝。但服务端日志会写得比较清楚——你可能会看到User guest from 192.168.1.100 not allowed because listed in DenyUsers这种报错后面连密码都不问直接拒。如果你的使用场景恰好是被某个组卡住先确认自己的账号是不是真的在对应组里groups zhangsan5.4 SELinux 和 AppArmor 是附加裁判CentOS / RHEL 系最容易忽视的是 SELinux尤其当你玩转~/.ssh目录软链接、把 home 目录放到非常规路径时。SELinux 会给 sshd 一个上下文限制restorecon -Rv /root/.ssh可以恢复。简单判断方法sudo getenforce输出Enforcing时看下/var/log/audit/audit.log有没有sshd相关的 denied 记录。临时放行可以setenforce 0验证确认是 SELinux 再考虑写策略或者restorecon。Ubuntu/Debian 系则是 AppArmor一般不会主动挡 SSH但如果你自定义了 sshd_config 里的授权文件路径到奇怪位置理论上可能被 profile 挡住。真遇到非常规路径检查/etc/apparmor.d/usr.sbin.sshd。6. 高频场景的定向排查vscode、git、树莓派、免密失效6.1 vscode Remote-SSH 反复提示 Permission denied现在用 vscode 连远程服务器的人非常多它报Permission denied的方式也很有迷惑性弹密码框你输了密码过两秒又弹反反复复。先排查基础项密码对不对、远程账号对不对、服务端是否允许密码认证。vscode 其实只是调用本机 ssh 后端所以它失败时你直接在 vscode 的终端里手动跑ssh -vvv userserver看具体卡在哪。还有个特殊场景是 vscode 需要在远程下载 server 包如果下载途中因为权限问题比如目标目录不可写报错会表现为连上后闪断或者显示过程停在downloading server package。这时检查远程用户对~/.vscode-server以及家目录有没有写权限简单粗暴的方式是直接删掉远程的~/.vscode-server让它重新装。另外vscode 连接时如果设置了Remote.SSH: Path指向了一个自定义 ssh 客户端Windows 上可能因为 ssh 版本过老导致算法协商失败表现为阴间报错。建议保持默认或升级到最新的 OpenSSH for Windows。6.2 git 平台 SSH 认证失败的密钥混淆问题GitHub / Gitee 这类平台在 clone 或 push 时报Permission denied (publickey)不会问你要密码因为密钥优先直接告诉你认证失败。常见原因有三当前 ssh-agent 里加载了错误的密钥。用ssh-add -l查看已加载的指纹如果有多把密钥且不是目标平台那把试试ssh-add ~/.ssh/id_ed25519手动加载或者ssh-agent -k后重新加载。平台后台的公钥和本地私钥不配对。去平台设置页删掉旧公钥重新添加一次注意公钥文件内容要完整复制别漏了前后缀和换行。密钥算法太老被平台拒绝。GitHub 对ssh-rsa已经不推荐了最好用ed25519ssh-keygen -t ed25519 -C your_emailexample.com测试连接用ssh -T gitgithub.com连通后通常回一句欢迎语如果回的是Permission denied (publickey)那就还是密钥没配对。6.3 树莓派、工控板和嵌入式系统树莓派这类设备上Permission denied最常见的原因是默认密码失效。树莓派官方系统第一次开机会强制改密码你如果还在用初始的raspberry被拒绝很正常。有些精简嵌入式系统连~/.ssh目录在首次开机时都不自动生成需要你手动创建mkdir -p ~/.ssh touch ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys还有一类小硬件跑的是定制系统默认只有 root 用户但 sshd_config 里PermitRootLogin是no这就是文章前面说的配置策略问题改一下再重载服务即可sudo sed -i s/^#*PermitRootLogin.*/PermitRootLogin yes/ /etc/ssh/sshd_config sudo systemctl restart sshd别嫌这些操作低级越小的设备越容易在这些基础配置上翻车。6.4 免密登录突然失效平时用得好好的免密登录某天突然变成Permission denied。除了权限被动过这种可能还有个高频场景是你重新生成了密钥对但只改了服务器端 authorized_keys没有清理本机 ssh-agent 里的缓存。ssh-agent 在会话里会缓存旧私钥连接时优先用缓存里的旧身份去匹配匹配不上就失败。解决办法ssh-add -D # 清空所有缓存密钥 ssh-add ~/.ssh/id_ed25519 # 重新添加新私钥 ssh -o IdentitiesOnlyyes userserverIdentitiesOnlyyes这个参数很关键它告诉客户端只用我明确指定的身份文件不要去遍历 ssh-agent 里的所有密钥能极大减少密钥太多被拒的问题。7. 几个真正好用的排查命令组合7.1 三件套-vvv、journalctl、sshd -T很多老手排 SSH 问题都有自己的一套肌肉记忆。我常用的三件套# 客户端全程调试信息输出 ssh -vvv -o IdentitiesOnlyyes userserver # 服务端实时日志 sudo journalctl -u ssh -f # 或者 sudo tail -f /var/log/auth.log # 服务端检查生效配置 sudo sshd -T | grep -Ei password|pubkey|permitroot|allowusers|denyusersssh -vvv -o IdentitiesOnlyyes的输出里能看到客户端尝试了哪些认证方法debug1: Next authentication method: publickey、debug1: Offering public key: /home/you/.ssh/id_ed25519、Authentications that can continue: publickey,password。配合另一端实时日志信息非常完整。7.2 兜底强制密码认证和强制密钥认证如果你想快速区分是密码环节问题还是密钥环节问题用这两个命令# 强制只用密码彻底不走密钥 ssh -o PubkeyAuthenticationno -o PreferredAuthenticationspassword userserver # 强制只用密钥彻底不走密码 ssh -o PasswordAuthenticationno -o PubkeyAuthenticationyes -i ~/.ssh/id_ed25519 userserver如果前者能连上说明问题在密钥体系如果只能靠后者连上说明服务端已经禁止密码登录。这一步能帮你把Permission denied的排查范围缩小一半。7.3 配置文件语法检查与重载在服务端改完/etc/ssh/sshd_config后必须养成的习惯sudo sshd -t没有输出就是语法正常然后再sudo systemctl restart sshd。有时候配置文件里写错一个参数sshd 直接拒绝启动登录反而变成Connection refused那就不是认证问题了。8. 实战中总结的几条个人经验最后分享几个我觉得特别值得记住的经验。第一个是关于StrictModes 和家目录权限的教训。有一回我给一台 Ubuntu 服务器配免密密钥内容、权限全对了但就是Permission denied (publickey)。查了半天日志最后发现用户的/home/zhangsan目录权限被某个脚本改成了 777。OpenSSH 的 StrictModes 一发现家目录可写连公钥文件都懒得查直接拒绝。这个错位非常隐蔽因为客户端看到的是公钥认证被拒你不会第一时间想到家目录权限。第二个经验是不要过度依赖 ssh-agent 的自动选择。当你的~/.ssh里有一堆密钥的时候OpenSSH 的遍历顺序不是按名字排序然后逐个试它按照文件的优先级顺序来。如果你经常遇到某个特定服务器连不上其他服务器正常大概率是它在遍历中选了错误的那把钥匙去开错误的锁。这时候-i 指定私钥路径永远最可靠。第三个经验有点反直觉有些服务器拒绝你的原因根本不在 SSH 层而在 PAM 层。比如服务器配了双因素认证或者特定 PAM 模块你的密码虽然是正确的但 PAM 要求额外条件不满足日志里出现pam_unix和pam_google_authenticator这类字样。这时候你不光要看 sshd 的日志还要留意 PAM 相关日志、甚至journalctl -u sshd -e里更靠后的记录。说实话Permission denied这个报错看起来让人头大但只要养成了客户端看 -vvv、服务端看日志、配置用 sshd -T 核对这套习惯90% 的问题都能在一两分钟内定位。剩下那 10%多半出在一些稀奇古怪的自定义配置上——这种情况我会直接先把服务器上的~/.ssh目录整个删了重新配往往比在错误配置里绕圈子快得多。