SSH 免密登录:公钥认证的原理、部署与排错

SSH 免密登录:公钥认证的原理、部署与排错
乔阳每次连服务器都要敲密码,既繁琐又是攻击面。SSH 公钥认证用一对密钥替代密码:本地保留私钥,服务器只存公钥,登录时由客户端自动完成挑战响应,不再需要人工输入任何东西。配好之后不仅能免密登录,更重要的是可以关掉密码登录,直接消除暴力破解这条最常见的外部攻击路径。
公钥认证是怎么工作的
理解原理能帮你排错,而不是机械照抄命令。
SSH 公钥认证基于非对称加密。本地有一对密钥,比如 id_ed25519(私钥)和 id_ed25519.pub(公钥)。私钥永远不离开你的机器,公钥可以随便分发。
登录时,服务器用你预先放上去的公钥发起一个数学挑战,只有持有对应私钥的人才能正确应答。整个过程不传输任何密码,服务器也不需要存储任何密码信息。认证通过后直接拿到 shell。
服务器怎么知道用哪个公钥来挑战你?靠 ~/.ssh/authorized_keys 文件。这个文件里每一行是一个公钥,服务器逐行尝试,匹配上你本地私钥的就生效。所以”部署公钥”的本质就是把公钥追加到这个文件里。
权限是这个机制里最容易踩坑的地方。sshd 对权限极其敏感,如果 ~/.ssh 或 authorized_keys 对其他用户可读可写,它会直接拒绝使用,认证失败。原因很直接:如果别人能改你的 authorized_keys,就等于能往里塞自己的公钥,等同于密码泄露。所以下面会反复出现 chmod 700 ~/.ssh 和 chmod 600 authorized_keys 这两条命令,不是多余,是硬性前提。
生成密钥对
先看看本地有没有现成的密钥,避免覆盖:
1 | # Mac / Linux |
已经有 id_rsa / id_rsa.pub 或 id_ed25519 / id_ed25519.pub 就可以直接用,跳到下一步。
生成新密钥:
1 | ssh-keygen -t ed25519 -C "your_email@example.com" |
参数说明:
-t ed25519:推荐用 Ed25519 算法。密钥只有 256 位(RSA 4096 是 4096 位),签名快,实现更简洁不易出错。OpenSSH 6.5(2014 年发布)以后都支持。-C:只是个注释,方便你日后在一堆密钥里认出哪个是哪个,不影响功能。
如果你的服务器或客户端比较老(OpenSSH 低于 6.5),退回 RSA:
1 | ssh-keygen -t rsa -b 4096 -C "your_email@example.com" |
-b 4096 指定 4096 位密钥长度。RSA 2048 位目前仍算安全(NIST 认为可用到 2030 年),但 4096 是兼顾性能和长期安全的常用选择。
执行后会问两个问题:
1 | Enter file in which to save the key (/home/user/.ssh/id_ed25519): [直接回车用默认路径] |
passphrase 是给私钥本身加的密码。不设的话,任何拿到你私钥文件的人就能直接登录服务器。设了的话,每次用密钥需要输一次 passphrase,可以用 ssh-agent 缓存,后面会讲。
我的建议是:个人开发机设 passphrase,用 ssh-agent 缓存;临时环境或 CI 里可以留空,但那台机器本身要做好访问控制。
部署公钥到服务器
方法一:ssh-copy-id(推荐)
1 | ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote-server |
这条命令会登录服务器(最后一次输密码)、把公钥追加到 ~/.ssh/authorized_keys、自动设置正确的权限。一行搞定,不容易出错。
方法二:手动部署
没有 ssh-copy-id(比如某些 Windows 环境)就手动来。先复制公钥到剪贴板:
1 | # Mac |
然后登录服务器,追加公钥并设权限:
1 | mkdir -p ~/.ssh |
chmod 700 ~/.ssh 让只有你自己能读写执行这个目录,chmod 600 authorized_keys 让只有你自己能读写这个文件。这两条是 sshd 接受公钥认证的硬性前提,少一条都登不上。
验证并禁用密码登录
测试免密登录:
1 | ssh user@remote-server |
看到欢迎信息就成功了:
1 | Welcome to Ubuntu 22.04.5 LTS |
确认公钥认证正常后,可以关掉密码登录,堵死暴力破解:
1 | sudo nano /etc/ssh/sshd_config |
确保这几项:
1 | PubkeyAuthentication yes |
改完重载配置:
1 | sudo systemctl reload sshd |
这一步之后,没有你私钥的人无论如何都登不进来。
注意,关掉密码登录是不可逆的便利性损失。如果你弄丢了私钥,自己也被锁在外面。所以关之前确保私钥有备份,passphrase 也记得住。万一真被锁外面,只能走带外通道:云厂商的 VNC 控制台、物理终端、或者找管理员。
排错:Permission denied (publickey)
最常见的报错。按这个顺序排查。
1. 本地私钥路径对不对
加 -v 看详细日志:
1 | ssh -v user@remote-server |
确认 ssh 客户端确实在尝试你期望的那个密钥文件。如果生成了多个密钥,可能需要 ~/.ssh/config 显式指定:
1 | Host myserver |
2. 服务器端权限
1 | chmod 700 ~/.ssh |
再检查家目录权限,不能对其他用户可写:
1 | chmod go-w ~ |
sshd 还会检查家目录的属主是不是你自己。用 ls -ld ~ 确认。
3. 服务器端 sshd 配置
1 | sudo grep -E 'PubkeyAuthentication|AuthorizedKeysFile|PasswordAuthentication' /etc/ssh/sshd_config |
确保:
1 | PubkeyAuthentication yes |
如果改过配置,记得 sudo systemctl reload sshd。
4. SELinux
CentOS / RHEL 上 SELinux 可能拦掉非标准路径下的 authorized_keys。用这条命令修复标签:
1 | restorecon -Rv ~/.ssh |
安全取舍
几个需要想清楚的决定。
passphrase 要不要设
设了更安全,但每次用密钥要输一次。折中是用 ssh-agent:启动时输一次 passphrase,之后缓存到内存,直到你关机或主动清除。
1 | eval "$(ssh-agent -s)" |
macOS 上可以配合 ssh-add --apple-use-keychain 把 passphrase 存进钥匙串,Linux 上有 gnome-keyring 或 kwallet 做同样的事。
RSA 还是 Ed25519
新环境无脑选 Ed25519。RSA 4096 也安全,但密钥长、签名慢,而且 RSA 的安全性随算力增长衰减得比 Ed25519 快。只有在兼容老系统时才退回 RSA。
密钥泄露怎么办
立即登录服务器,从 ~/.ssh/authorized_keys 里删掉对应那一行公钥,本地生成新密钥对重新部署。如果连服务器都登不上了(比如私钥丢了又被关了密码登录),就得走前面提到的带外通道。
别用同一对密钥到处部署
每台服务器或每个用途一对密钥。某台机器被攻破时只需要轮换那一对,不影响其他。管理多个密钥用 ~/.ssh/config 文件按 Host 分组指定 IdentityFile。







