这个场景要解决什么
SSH 报 Permission denied 时,这句话本身只说明所有允许的认证方式都失败了,并不指出是哪一步出错。有效做法是把认证链拆成三层:服务端实际生效的配置、服务端日志写下的拒绝原因、客户端协商的过程。三层对照完,答案基本就浮出来了。另外括号里列出的方式就是服务端允许的方式,括号里没有 password,说明密码认证已经被关闭。
基本信息
| 适用场景 | 客户端提示 Permission denied,或密钥登录始终不成功 |
|---|---|
| 目标 | 定位认证失败发生在配置层、密钥层还是安全策略层 |
| 工具链 | sshd -T、journalctl -u sshd、ssh -vvv、namei -l、restorecon |
| 关键点 | 配置以 sshd -T 的输出为准,服务端日志与客户端输出要对着看 |
常见根因
- 家目录或 ~/.ssh 权限过宽,sshd 静默拒绝密钥
- authorized_keys 内容粘贴出错,或文件权限不是 600
- 服务端禁用了密码认证,客户端却只准备用密码登录
- AllowUsers、DenyUsers、DenyGroups 把目标用户挡在外面
- SELinux 上密钥文件的安全上下文不对,在策略层被拒绝
第 1 步 读出生效配置
第 1 步 读出生效配置
sshd -T | grep -E 'passwordauthentication|pubkeyauthentication|permitrootlogin|authorizedkeysfile|allowusers|denyusers|usepam'第 2 步 看服务端日志
第 2 步 看服务端日志
journalctl -u sshd -n 50 --no-pager
tail -n 50 /var/log/secure # RHEL 系
tail -n 50 /var/log/auth.log # Debian 系第 3 步 客户端调试连接
第 3 步 客户端调试连接
ssh -vvv user@host 2>&1 | tail -n 60
# 重点看 Offered public key 与 Authentications that can continue 两行第 4 步 权限与安全上下文
第 4 步 权限与安全上下文
namei -l /home/user/.ssh/authorized_keys # 一次看清路径上每一级的权限
chmod 700 /home/user/.ssh
chmod 600 /home/user/.ssh/authorized_keys
chown -R user:user /home/user/.ssh
restorecon -Rv /home/user/.ssh 2>/dev/null || true日志提示对照
| 日志或提示 | 含义 | 处理方向 |
|---|---|---|
| bad ownership or modes | 目录或文件权限过宽 | 修成 700 与 600,家目录不要对同组或其他人可写 |
| Failed publickey for user | 密钥不被接受 | 核对 authorized_keys 内容与服务端相关选项 |
| Failed password for user | 密码错误或密码认证被禁用 | 先确认服务端是否还允许密码登录 |
| Connection closed by authenticating user | 认证阶段被中断 | 查看 MaxAuthTries 与 PAM 相关限制 |
| maximum authentication attempts exceeded | 尝试次数超限 | 检查客户端一次带了几把密钥供尝试 |
sshd -T 输出的是合并 Include 之后的最终生效值,比翻配置文件可靠;改完 sshd_config 先用 sshd -t 做语法检查,再 reload。
修权限不要对家目录整体递归 chmod 700:那样会连带 web 目录之类依赖组权限的路径一起失效。只调整 .ssh 目录及其内部文件。
排查顺序建议
- 先在客户端加 -vvv 跑一次,看它提交了哪些密钥、服务端允许哪些方式
- 再到服务端跑 sshd -T,确认实际生效的配置与设想一致
- 最后看服务端日志,日志里的拒绝原因通常就是最终答案
- 三者对不上时优先信服务端日志,其次是 sshd -T 的输出
配置项速查
| 配置项 | 作用 | 常见取值 |
|---|---|---|
| PasswordAuthentication | 是否允许密码认证 | no 表示只允许密钥 |
| PubkeyAuthentication | 是否允许公钥认证 | 一般保持 yes |
| PermitRootLogin | 是否允许 root 登录 | prohibit-password 表示只允许密钥 |
| AuthorizedKeysFile | 公钥文件位置 | 默认 .ssh/authorized_keys |
| MaxAuthTries | 单次连接的认证尝试上限 | 默认 6,调小可更快锁定爆破 |
| AllowUsers / DenyUsers | 用户级黑白名单 | 写错会直接把人挡在门外 |
客户端报 Too many authentication failures,通常是代理里堆了太多密钥:加 -o IdentitiesOnly=yes -i 指定密钥,只提交你真正想用的那一把。
关闭密码认证之前,先用另一个窗口验证密钥登录确实可用,并确认至少还有一个能登录的账号;改错一次可能把自己和同事一起锁在门外。
用 ssh-copy-id 安装公钥比手工建目录再粘贴可靠:它会追加到 authorized_keys 并设定正确的属主与权限,已有同内容密钥时也不会重复写入。
客户端调试输出要点
| 输出片段 | 含义 | 下一步 |
|---|---|---|
| 尝试提供公钥 | 客户端已给出密钥 | 确认服务端是否接受该密钥 |
| 没有更多认证方式 | 所有方式均被拒 | 回到服务端日志与账号状态 |
| 公钥认证被拒 | 密钥这一环不通过 | 核对授权文件内容与权限 |
| 连接被拒绝 | 端口层面不通 | 查看服务与监听状态 |
| 连接超时 | 链路层不通 | 排查网络与访问控制 |
第一步永远是确认账号本身可用:账号是否被锁定、登录 shell 是否被设为不可登录、家目录与授权文件权限是否过宽。这些问题在服务端往往只留下一句很笼统的拒绝记录,必须结合客户端的详细输出才能一一对应,否则容易在网络上白找一圈。