返回博客

SSH Permission denied (publickey):真正的修复方法

2026年9月18日

02:10,一次部署死在一行我打过上千遍的命令上:ssh deploy@stagingPermission denied (publickey)。密钥没问题,出问题的是提供这一环。

TL;DR:Permission denied (publickey) 意味着客户端提供的每个密钥都被服务器拒绝了——这是协商的判决,不是密码打错。ssh -vvv(提供了哪些密钥)和服务器日志 journalctl -u ssh(拒绝了哪些密钥、为什么)来诊断。然后修复四个原因之一:用户不对、密钥不在 authorized_keys 里、权限太开放,或者密钥是 OpenSSH 8.8+ 拒收的 ssh-rsassh-copy-id 能预防大多数复发。

$ ssh -T [email protected]
[email protected]: Permission denied (publickey).

这个报错是判决,不是线索:提供清单里的东西全被拒了。

SSH 为什么说 “Permission denied (publickey)“?

公钥认证是一场“提供—拒绝”的协商。客户端会把它能找到的每一把密钥都提供出去——来自 agent 的身份、~/.ssh/config、默认文件名(id_ed25519id_rsa),以及用 -i 传入的一切。服务器把每次提供与目标账号的 ~/.ssh/authorized_keys 比对。当没有任何一把匹配,而密码认证又被禁用或已用尽时,客户端就打出那行每个 devops 工程师都背得出的字。

调试时有两个事实很关键。第一,这条消息不会告诉你刚才用的是哪个用户——一半的案例是一把完全正常的密钥躺在错误账号的 authorized_keys 里。第二,服务器早就把原因说了:sshd 会记录每一次被拒绝的提供。那份没人想要但人人都需要的“堆栈跟踪”就是 -vvv

怎么看 SSH 实际提供了哪把密钥?

ssh -vvv deploy@staging 2>&1 | grep -iE "offering|identity|denied|authentications"
# debug1: Offering public key: /home/you/.ssh/id_ed25519 RSA-SHA256 (explicit)
# debug1: Authentications that can continue: publickey
# deploy@staging: Permission denied (publickey).

读三行:Offering public key(客户端提供了什么)、Authentications that can continue(服务器还接受什么——如果 password 不在其中,密码提示永远不会出现)和最终判决。如果目标密钥从未出现在提供清单里,问题在客户端一侧:-i 路径写错、~/.ssh/config 里忘了删的 IdentityFile,或者一个 agent(ssh-add -l)拿着过期的身份抢先应答。

四个真正的原因是什么?

原因特征修复
用户不对密钥对 root@host 有效,对 deploy@host 失败密钥必须在那个用户的 ~/.ssh/authorized_keys
密钥未安装服务器日志每次都显示 Failed publickeyssh-copy-id user@host
权限太开放客户端:WARNING: UNPROTECTED PRIVATE KEY FILE——密钥被跳过,根本不会提供chmod 700 ~/.ssh; chmod 600 ~/.ssh/*
过时的 ssh-rsa 密钥服务器跑 OpenSSH 8.8+;旧的 RSA 密钥永远不会被接受用 ed25519 重新生成,或升级密钥

权限这个原因值得单独说一句,因为 OpenSSH 执行得很硬:群组或其他人可读的私钥会被客户端自己拒绝,悄悄退出提供清单。修复只要两条命令:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/authorized_keys

ssh-rsa 这个原因坑的是老的 CI 镜像和树莓派:OpenSSH 8.8(2021-10-01 发布,openssh.com/txt/release-8.8)默认禁用了 ssh-rsa 签名——基于 SHA-1 的那一变体。用发布说明的原话说:“the ssh-rsa signature scheme is … disabled by default”。2018 年生成的 RSA 密钥并没有坏,只是它的签名格式不再被“通用”了。重新生成一把 ed25519 密钥(ssh-keygen -t ed25519)才是长久之计;而且云厂商的控制台一般提供串口控制台,就算修到一半把自己锁在门外也能回来。

如果你在改配置的时候 sshd 起不来了,那是另一场围猎——见 systemd service not starting

怎么看服务器为什么拒绝了我的密钥?

服务器端的日志是诚实的证人。在 Ubuntu(systemd)上,sshd 会记录每次提供的判决:

journalctl -u ssh -n 50 --no-pager | grep -iE "publickey|denied|accepted"
# sshd[4421]: Failed publickey for deploy from 203.0.113.7 port 51422 ssh2: RSA SHA256:...
# sshd[4421]: Accepted publickey for deploy from 203.0.113.7 port 51422 ssh2: ED25519 SHA256:...

带着陌生指纹的 Failed publickey 说明客户端提供的不是你以为的那把密钥——回到 -vvv。带着的指纹的 Failed publickey 说明密钥没错,只是没装到那个账号下,或者服务器侧的家目录权限不对(同样的 700/600 规则也适用于服务器的 ~/.ssh~/.ssh/authorized_keys)。

怎样正确安装我的密钥?

ssh-copy-id deploy@staging
# Number of key(s) added: 1
ssh -o BatchMode=yes deploy@staging 'echo ok'   # 非交互验证

ssh-copy-id 会把你的公钥以妥当的权限追加进目标账号的 authorized_keys——这条命令的存在,正是因为手工往 authorized_keys 里粘贴总会在某个多出来的换行符或不存在的目录上失败,而且失败得刚刚好够频繁。BatchMode 检查才是真本事:它关掉所有交互提示,所以成功就意味着单凭密钥完成了登录。密钥一旦通了,scprsync 白拿同样的认证——两者之间选谁,是带宽问题,不是凭据问题(rsync vs scp)。

30 秒分诊

ssh -vvv user@host 2>&1 | grep -i offering      # 1. 提供了哪些密钥?
journalctl -u ssh -n 50 | grep -i publickey     # 2. 为什么被拒绝?
ls -ld ~/.ssh ~/.ssh/authorized_keys            # 3. 是 700 / 600 吗?
ssh-copy-id user@host                           # 4. 安装,再用 BatchMode 验证

Permission denied (publickey) 是协商的结果,不是密码提示——服务器拒绝了提供清单里的每一把密钥,而它的日志早就知道为什么。

按顺序跑完这四条命令,故障就不再是谜:每一次都会有其中一条点名元凶。我那次是一个过期的 agent 身份抢在正确的密钥之前应答——ssh-add -d 之后,部署在 02:31 重新变绿。

FAQ

SSH 为什么提示 Permission denied (publickey)?

它表示客户端提供的每一个密钥都被服务器拒绝了,而密码或 keyboard-interactive 认证未被尝试或未获允许。这是对你所提供密钥的协商结论——不是密码打错了。

在 Ubuntu 上如何修复 SSH permission denied (publickey)?

用 journalctl -u ssh -n 50 检查服务器端,确认你的公钥在目标用户的 ~/.ssh/authorized_keys 里,并确保 ~/.ssh 权限为 700,authorized_keys 和私钥权限为 600。

为什么 ssh -i 指定密钥仍然失败?

常见原因有三:私钥权限太开放导致 OpenSSH 拒绝加载;服务器运行 OpenSSH 8.8+,已默认禁用 ssh-rsa(SHA-1 签名);或者密钥不在正确用户的 authorized_keys 里。

如何把自己的公钥添加到服务器?

运行 ssh-copy-id user@host——它会把你的公钥以正确权限追加到 ~/.ssh/authorized_keys。如果密码登录已被禁用,请从你仍能访问的控制台操作。

— mrsaynothing

— mrsaynothing

关于 AI、Linux 与自托管的一线笔记。

Get the next one by email

One email per post. No spam, no algorithms.

self-hosted · no third parties · one-click unsubscribe

what is this?

.gitignore 不生效?真正的修复在这里

喜欢这些文章?我的本职工作就是这样的工程。 雇用我