为什么要改用密钥
开启密码登录时,任何能连到服务器 SSH 端口的人都可以不停地猜密码。OpenSSH 默认就是这样:手册中 PasswordAuthentication 的默认值是 yes。密钥要难猜得多,而且关闭密码登录还能挡住密码检查环节本身的漏洞。本周 Alpine Linux 上的 OpenSSH 漏洞就是一个例子:Alpine 的公告说明,关闭了密码登录的服务器不受影响。
你需要一个已经能登录服务器、并且可以使用 sudo 或 doas 的账号。
1. 在你的电脑上创建密钥
在你自己的电脑上运行下面的命令,而不是在服务器上:
ssh-keygen -t ed25519
按回车接受默认位置,然后输入一个密钥口令保护密钥文件本身的密码,这样即使文件被偷,单凭它也无法登录。你会在主目录的 .ssh 文件夹里得到两个文件:私钥 id_ed25519,它永远不离开你的电脑;公钥 id_ed25519.pub,它要放到服务器上。
2. 把公钥复制到服务器
在 Linux 和 macOS 上:
ssh-copy-id 用户名@服务器
它会最后一次询问你的密码,并把公钥加入服务器上的 ~/.ssh/authorized_keys。如果没有 ssh-copy-id(例如在 Windows 上),可以打开 id_ed25519.pub,复制其中唯一的一行,粘贴到服务器上的 ~/.ssh/authorized_keys 里。.ssh 文件夹应只允许你自己读取(chmod 700 ~/.ssh 和 chmod 600 ~/.ssh/authorized_keys)。
3. 改设置之前先测试密钥
保持当前会话不断开。在一个新的终端窗口中运行 ssh 用户名@服务器。如果它询问密钥口令,或者直接登录成功,说明密钥可用。如果它询问账号密码,请先停下来把密钥弄好,否则下一步你会把自己锁在门外。
4. 关闭密码登录
在服务器上,把下面两行加入 SSH 服务器的设置:写在 /etc/ssh/sshd_config 中,或者如果系统使用 /etc/ssh/sshd_config.d/ 文件夹,就在其中新建一个文件:
PasswordAuthentication no
KbdInteractiveAuthentication no
第二行关闭另一种类似密码的方式,即 keyboard-interactive一种登录方式,服务器通过提示提出问题,通常就是询问密码;该选项的旧名称是 ChallengeResponseAuthentication。对于 root 账号,OpenSSH 的默认设置 PermitRootLogin prohibit-password 已经禁止用密码登录。
这里有个陷阱。手册写道,对于每个选项,“将使用最先得到的值”。有些系统(包括一些云镜像)会在 sshd_config.d 里放一个自带的文件,设置 PasswordAuthentication yes;如果这个文件先被读取,你写的那一行就不会生效。第 6 步会教你如何发现这种情况。
5. 检查配置并重启服务
运行 sudo sshd -t。它会检查配置,没有问题时不输出任何内容。然后重启 SSH 服务:
- Debian 和 Ubuntu:
sudo systemctl restart ssh - Fedora、Red Hat 及类似系统:
sudo systemctl restart sshd - Alpine:
rc-service sshd restart
在完成下一步之前,保持旧会话不断开。
6. 确认已经生效
在服务器上运行:
sudo sshd -T | grep -i -E 'passwordauthentication|kbdinteractiveauthentication'
sshd -T 会显示 sshd 实际使用的设置。两行都应该以 no 结尾。如果有一行是 yes,请到 /etc/ssh/sshd_config.d/ 中找出更早设置它的文件。
在你的电脑上,尝试强制使用密码登录:
ssh -o PubkeyAuthentication=no 用户名@服务器
服务器应该以 “Permission denied (publickey)” 拒绝。平时的 ssh 用户名@服务器 仍应能用密钥正常登录。
如果出了问题
如果失去了访问权限,请使用主机服务商或虚拟机软件提供的控制台,撤销更改并重启 SSH 服务。这就是为什么要一直保持旧会话到最后。把私钥的副本保存在安全的地方:如果唯一的副本丢了,你就需要通过那个控制台添加新的公钥。
信息来源
本文核对过的一手来源: