为什么要改用密钥

开启密码登录时,任何能连到服务器 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 服务。这就是为什么要一直保持旧会话到最后。把私钥的副本保存在安全的地方:如果唯一的副本丢了,你就需要通过那个控制台添加新的公钥。

信息来源

本文核对过的一手来源: