SSH 密钥被拒主因是服务端 sshd_config 配置不当或权限过宽:AuthorizedKeysFile 路径需匹配、PubkeyAuthentication 必须开启、StrictModes 要求~/.ssh 目录及 authorized_keys 文件无组 / 其他写权限。

为什么 ssh-keygen 生成的密钥在某些服务器上直接被拒绝?
不是密钥没传对,大概率是服务端 sshd_config 没开关键配置,或者权限太松。OpenSSH 默认会拒绝 ~/.ssh/authorized_keys 所在目录或文件权限过宽(比如组可写),这是硬性安全策略,不关日志等级也看不到具体原因。
-
AuthorizedKeysFile必须匹配客户端上传路径,默认是~/.ssh/authorized_keys,但有些系统改成了.ssh/authorized_keys(少了个波浪号),导致读不到 -
PubkeyAuthentication yes和AuthenticationMethods publickey(如需强制)必须显式开启,CentOS 7+ 默认是开启的,但 Ubuntu 22.04 的 cloud-init 可能覆盖为no -
StrictModes yes是默认值,意味着~/.ssh目录不能有 group/other 写权限(即不能是775或777),authorized_keys文件也不能是664或更宽松
如何用 ssh-copy-id 避免手动传密钥出错?
ssh-copy-id 不只是拷文件,它会自动处理权限、创建目录、追加而非覆盖,并校验目标用户 shell 是否合法。但它依赖底层 ssh 能连通——如果目标只允许密钥登录且还没配好,它就失败,这时候得先用密码临时登一次。
- 执行前确认目标用户家目录存在且可写,否则
ssh-copy-id会在/tmp创建临时文件失败 - 如果目标服务器禁用了
PasswordAuthentication,又没提前配好密钥,ssh-copy-id会卡住或报Permission denied (publickey),此时需临时启用密码登录或改用scp手动传 - 支持指定端口:
ssh-copy-id -p 2222 user@host;也支持指定密钥:ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
ssh-agent 和 ssh-add 在批量登录时为什么总失效?
根本原因是 shell 生命周期问题:ssh-agent 启动后只对当前 shell 及其子进程有效,新开终端、切换用户、重连 tmux/session 都不会继承 SSH_AUTH_SOCK 环境变量。很多人以为运行一次 ssh-add 就一劳永逸,其实不是。
- 不要在脚本里反复调用
eval $(ssh-agent),这会启动新 agent 并覆盖旧的,导致已添加的密钥“丢失” - 检查是否已有 agent:运行
echo $SSH_AUTH_SOCK,为空说明没继承到;可用pgrep -u $USER ssh-agent查进程 - 长期方案是把
ssh-add -t 7200(2 小时有效期)放进 shell 初始化文件(如~/.bashrc),但要加判断:[-z "$SSH_AUTH_SOCK"] && eval "$(ssh-agent -s)"; ssh-add -l >/dev/null || ssh-add
大规模部署时,~/.ssh/authorized_keys 被覆盖或权限错乱怎么办?
Ansible、Salt 或自写脚本批量写入时,容易用 copy 模块覆盖整个文件,或忽略 mode 参数导致权限变成 644,触发 OpenSSH 拒绝。更隐蔽的问题是多任务并发写同一个文件,造成内容损坏。
- 永远用
lineinfile或blockinfile(Ansible)追加公钥,而不是copy整个文件;确保create: yes且mode: '0600' - 若必须替换整个文件,请先用
stat检查目标是否存在、权限是否正确,再用原子写入(如atomic: true)避免中间态 - 生产环境建议用
ssh-keyscan配合known_hosts预置,否则首次连接仍会交互提示,破坏自动化流程
证书认证本身不难,难的是每个环节都依赖前序环节的严格状态:密钥生成方式、传输路径、服务端配置、权限控制、agent 生命周期、批量工具的行为边界——漏掉任意一个,就会在凌晨三点连不上某台数据库从库。






























