在 VPS 上加固 SSH
锁定 VPS 上的 SSH:改用仅密钥登录,用 drop-in 配置文件禁用 root 和密码登录,再叠加 Fail2ban 和 VPN 防护。
为什么 SSH 是首先要加固的对象
SSH 是您控制服务器的方式,因此它也是每个攻击者最先撬动的那把锁。VPS 一上线,扫描器就会在 22 端口上不断猜测用户名和密码。几分钟内,您就能在日志里看到这类尝试。加固 SSH 的核心,是移除那些能被猜到的东西:彻底关闭密码登录,关闭 root 登录,只允许加密密钥进入。做到之后,持续的猜测根本无法得逞,因为已经没有密码可以找出来了。
本文假设您的 SSH 已经能正常工作。只要能登录,就能对它进行加固。请按顺序完成各个步骤,并在新会话确认可用之前,一直保持当前会话打开,这样即便出错也不会把自己锁在门外。
第 1 步:先确认密钥认证可用
密钥认证用一对密钥取代密码:私钥留在您自己的电脑上,公钥放到服务器上。服务器无需让私钥离开您的机器,就能验证您持有它。在禁用密码之前,请先确认密钥可用,否则您会把自己锁在门外。
在您自己的电脑上,如果还没有密钥,就先创建一个:
ssh-keygen -t ed25519把公钥部分复制到服务器上:
ssh-copy-id user@your-server然后打开一个新的 SSH 会话。如果它不再询问密码就让您登录,说明密钥有效,您可以放心关闭密码了。如果您刚接触密钥,或者会用到不止一台电脑,SSH 密钥管理基础 讲解了完整的模型:每台设备一把密钥、sshd 要求的权限,以及笔记本丢失时如何吊销一把密钥。
第 2 步:用 drop-in 文件加固 sshd
不要直接编辑 /etc/ssh/sshd_config。Ubuntu 24.04 会从 /etc/ssh/sshd_config.d/ 读取 drop-in 文件,在那里放一个小文件更整洁、能在软件包升级后保留下来,出问题时也容易删除。文件名很重要:对每一项设置,sshd 会保留它最先读到的值,而 Ubuntu 云镜像会在这个目录里放一个带 PasswordAuthentication yes 的 50-cloud-init.conf。请把您的文件命名为 00- 开头,让它排在那个文件前面并胜出;99- 开头的文件则会悄无声息地落败。创建一个:
sudo nano /etc/ssh/sshd_config.d/00-hardening.conf写入以下内容:
# Key-only login: no passwords to guess.
PasswordAuthentication no
KbdInteractiveAuthentication no
# No direct root login. Log in as your user, then use sudo.
PermitRootLogin no每一行都关上一道门。PasswordAuthentication no 是最关键的一条:关闭密码后,暴力破解就无从破解了。KbdInteractiveAuthentication no 关掉另一条类似密码的路径。PermitRootLogin no 意味着攻击者必须既知道您的用户名、又持有您的密钥,而不能只盯着每台机器上都存在的那个 root 账户下手。
第 3 步:先测试配置,再重载
在应用之前先检查配置有没有错误,这样一个拼写错误就不会弄坏服务:
sudo sshd -t如果它什么都不打印,说明配置有效。重载 SSH:
sudo systemctl reload ssh然后检查 sshd 实际使用的设置,以便发现某个输给了其他文件的 drop-in:
sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'两项都应显示 no。现在,在不关闭当前会话的情况下,从另一个终端打开一个全新的会话。如果它用您的密钥让您登录,就大功告成了。如果有任何问题,您的第一个会话仍然开着,可以用来修复。这种重叠就是安全网,所以千万不要跳过。
第 4 步:可选的非标准端口
把 SSH 从 22 端口挪到 2222 之类的端口,在任何实际意义上都不会让它更安全,因为有决心的攻击者会扫描所有端口。它真正的作用是减少日志噪音,因为大多数自动化扫描器只尝试 22 端口。如果您想这么做,就在 drop-in 文件里加上 Port 2222,先在防火墙里放行新端口,然后运行 sudo systemctl daemon-reload && sudo systemctl restart ssh.socket,再用 ssh -p 2222 连接。在 Ubuntu 24.04 上,ssh.socket 掌管着监听端口,所以单纯的 reload ssh 会让 sshd 仍停在 22 端口;重启这个 socket 才会启用新端口。请把它当作整洁,而非防护。
第 5 步:叠加更多防御层
加固过的 SSH 密钥是基础,之上还有两层可以叠加。
Fail2ban 会盯着您的日志,封禁那些反复失败的地址,从而削减扫描器噪音并尽早把它们赶走。它与仅密钥认证天然契合:参见 用 Ubuntu 上的 Fail2ban 阻止 SSH 攻击。
更强的做法是让 SSH 彻底远离公共互联网。如果您把 SSH 放到 WireGuard VPN 之后,并用防火墙把 22 端口限制在隧道之内,那么 VPN 之外的任何人都根本无法访问它,暴力猜测就从“困难”变成了“不可能”。这一切都以底层有一个默认拒绝的防火墙为前提,也就是在 VPS 上配置好的 UFW。
SSH 只是一份更大清单中的一行:新 VPS 的前 10 分钟把这些步骤排好了顺序,而Ubuntu 上的自动安全更新则在之后持续为机器打补丁。
FAQ
如何在 Ubuntu 24.04 上禁用 SSH 密码登录?
在 /etc/ssh/sshd_config.d/00-hardening.conf 创建一个 drop-in 文件(00 前缀让它排在 50-cloud-init.conf 前面,否则后者的 PasswordAuthentication yes 会胜出,因为 sshd 会保留它最先读到的值),文件内容为 PasswordAuthentication no 和 KbdInteractiveAuthentication no,运行 sudo sshd -t 检查它,然后 sudo systemctl reload ssh。在依赖它之前,先在新会话中确认密钥登录可用。编辑 drop-in 文件而非 sshd_config,能在软件包升级后保留下来,也容易撤销。
我应该禁用 SSH 的 root 登录吗?
应该。设置 PermitRootLogin no,这样就没人能直接以 root 登录。请以您的普通用户登录,用 sudo 处理管理任务。root 存在于每一台 Linux 机器上,因此让它可被访问,等于把一个已知用户名递给攻击者当靶子。禁用它意味着攻击者必须既知道您的账户名、又持有您的密钥。
更改 SSH 端口会让我的服务器更安全吗?
没什么实际意义。挪离 22 端口能让您躲开那些只探测 22 的懒惰扫描器,从而减少日志噪音,但真正的攻击者会扫描每个端口,照样能找到它。仅密钥认证才是真正阻止入侵的东西。如果您要更改端口,请先在防火墙里放行新端口,然后运行 sudo systemctl daemon-reload && sudo systemctl restart ssh.socket;在 Ubuntu 24.04 上,socket 掌管着监听器,单纯的 reload 会让 sshd 仍停在 22 端口。
如果我使用 SSH 密钥,还需要 Fail2ban 吗?
它是可选的,但仍然有用。在仅密钥认证下,密码猜测无法得逞,所以 Fail2ban 并不是把攻击者挡在门外的东西。它会对同一地址的反复失败做限速,从而削减日志里的扫描器噪音、尽早赶走惯犯;而缓慢的分布式攻击本来就会保持在它的封禁阈值之下。请把它叠加在密钥认证之上,并且最好让 SSH 待在 VPN 之后。
如果我把自己锁在 SSH 门外,该如何恢复?
使用您服务商的网页控制台,它通过串口或 VNC 连接到达服务器,不经过 SSH。从那里您可以登录、修复 sshd 的 drop-in 文件并重载服务。这正是为什么您要在关闭第一个会话之前,先用第二个终端测试新的 SSH 配置,也是为什么在关闭密码之前,密钥认证就应当已经能正常工作。