SSH 免密登录配置不生效,多数情况要先把两件事拆开:公钥有没有被 sshd 真正读到,读到之后有没有被接受。前者是文件路径和内容问题,后者通常是目录或文件属主、权限问题。绕开这两步直接去改 /etc/ssh/sshd_config,经常改的是一份没被加载的配置,或者改完忘了重新加载,反而让判断更乱。下面按排查顺序给出可核对的命令、关键的检查动作和一条能直接照着做完的路径。
先分清两件事:公钥是否被 sshd 读到,以及读到后是否被接受。前者查 AuthorizedKeysFile 指向的路径和文件里的指纹,后者查 StrictModes 相关的目录与文件属主权限。日志里出现 bad ownership or modes,问题在后者;没有这行,多半在前者。动手改配置前,先确认系统实际加载的是 ssh.service 还是 sshd.service,避免改了一份没生效的文件。
SSH免密登录:先把能核对的事实摆清楚
这一节只列能直接看到、能对上的东西,不下结论。先确认处理对象:客户端是发起 ssh user@host 的那台机器,服务端是目标机,免密的目标是让服务端接受客户端私钥对应的公钥,走 publickey 认证方式。
可核对的现象分四组:
- 客户端密钥对是否存在、权限是否正常:
ls -l ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub,私钥通常应为 600,.ssh 目录通常 700。公钥指纹用ssh-keygen -lf ~/.ssh/id_ed25519.pub读出,记下这一行,后面要比对。 - 服务端 sshd 实际生效的配置项:
sudo sshd -T | grep -Ei 'pubkeyauthentication|authorizedkeysfile|strictmodes|permitrootlogin|allowusers|pubkeyacceptedalgorithms'。sshd -T输出的是解析后的最终值,比直接看配置文件可靠,但前提是它解析的是真正被加载的那份文件。 - 服务端日志位置:Debian/Ubuntu 系一般在
/var/log/auth.log,RHEL 系一般在/var/log/secure,systemd 环境下用sudo journalctl -u ssh -n 50 --no-pager或-u sshd。单元名是 ssh 还是 sshd,需要按机器实际情况确认。 - 若目标机由云控制台创建,控制台里的“密钥对”项通常只在创建或绑定实例时注入一次,且注入到哪个用户(root 还是镜像默认用户)要按你所用的控制台页面实际字段确认。
顺带核实一个容易忽略的前提:客户端与服务端是否在同一网络路径上、是否有跳板或堡垒机介入。如果中间还有一层转发,实际做公钥认证的可能不是你以为的那台机器。
SSH免密登录最容易搞错的一点
最常见的误解是:把公钥追加进某个 authorized_keys 就算配好了。实际上“追加了”和“sshd 用上了”之间还隔着两步——文件路径是否就是 AuthorizedKeysFile 展开后的位置,以及这个文件连同它的上级目录是否通过 StrictModes 的属主与权限检查。用 sudo 或从别的账户执行 ssh-copy-id 时,~/.ssh 和 authorized_keys 的属主变成 root,就是典型的一例。
能把这两种情况区分开的检查动作是:在客户端用详细模式连一次,同时看服务端日志。

ssh -vvv user@host 2>&1 | grep -E 'Offering public key|Server accepts key|Authentications that can continue|Permission denied'
sudo journalctl -u ssh -n 30 --no-pager | grep -Ei 'refused|authorized_keys|publickey'
判断分界:日志里出现 Authentication refused: bad ownership or modes for file ...authorized_keys 或指向 .ssh 目录的同类提示,问题出在权限和属主,公钥本身是收到了的;日志里没有任何 refused 相关行,客户端 Offering public key 之后直接落到 Authentications that can continue: publickey,password,那更可能是公钥没在 sshd 读取的那个文件里,或用户名与 %h 展开的家目录对不上。如果客户端已经打印 Server accepts key 却仍然失败,方向要转到账户状态、登录 shell 是否有效、以及该账户是否被附加了强制命令等限制。
SSH免密登录:一个照着就能做完的做法
下面这条路径以 ed25519 密钥、普通用户 user、目标机 host 为例,替换成你自己的值即可。每一步都注明该看到什么。
第一步,在客户端生成密钥对(已有可跳过):
ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)" -f ~/.ssh/id_ed25519
正常会输出密钥保存路径、一串 fingerprint 和 randomart 图案。如果提示文件已存在,先确认现有私钥能不能用,不要直接覆盖。
第二步,把公钥装到服务端:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
没有 ssh-copy-id 时用等价写法,注意先建目录和设权限再写入:
cat ~/.ssh/id_ed25519.pub | ssh user@host 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
这一步会要求输入一次密码,成功时 ssh-copy-id 会提示密钥已添加,并建议再连一次验证。
第三步,到服务端核对文件内容和属主权限:
ssh user@host 'ls -ld ~ ~/.ssh ~/.ssh/authorized_keys; ssh-keygen -lf ~/.ssh/authorized_keys'
应当看到 ~ 的属主是 user、组或其他用户不可写;~/.ssh 为 700;authorized_keys 为 600。指纹输出里应当能找到第一步客户端 ssh-keygen -lf 打印的那一行。如果属主是 root,在该用户下修正:chown -R user:user ~/.ssh && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys && chmod g-w,o-w ~。
第四步,确认 sshd 侧开关,只在确有必要时改配置:

sudo sshd -T | grep -Ei 'pubkeyauthentication|authorizedkeysfile|strictmodes'
sudo sshd -t # 语法自检,无输出表示通过
sudo systemctl reload ssh 2>/dev/null || sudo systemctl reload sshd
三项通常应分别为 yes、.ssh/authorized_keys .ssh/authorized_keys2、yes。改完 reload 后再次执行 sudo sshd -T,确认值已经变化,否则说明改的不是被加载的那份文件。
SSH免密登录:怎么验证结果对不对
验证要排除密码和交互式认证的干扰,用非交互模式连一次,看退出码:
ssh -o BatchMode=yes -o PreferredAuthentications=publickey user@host 'echo ok'
echo $?
正常结果是只输出 ok,退出码 0,全程不提示输入密码。BatchMode=yes 让客户端在需要交互时直接失败,正好能把“其实还在走密码”这种情况暴露出来。如果提示 Permission denied (publickey),说明 publickey 这条路没通,回到上一节的两步检查。
需要看认证细节时,用 ssh -vvv user@host,关注 Offering public key、Server accepts key、Authenticated to 这几行的先后顺序,它们能把失败点定位到“公钥未被读取”还是“读取后被拒绝”。
如果目标机由控制台管理,绑定密钥对之后要确认实例重启前后 authorized_keys 是否被重置,云厂商处理这一项的方式不统一,建议以你所使用控制台对应页面的说明和实例上的实际文件为准。验证通过后,若要长期保持,把新生成的私钥备份到受控位置,并避免在脚本里硬编码密码作为回退。