速读#
VPS 一接公网就会被扫,SSH 加固的主防御是密钥登录、禁密码、禁 root 密码登录。改端口有价值,但它是降噪,不是主防御;私钥永远留本地,改配置时也必须保留当前会话当退路。
接上公网后,扫描很快就会出现#
这不是吓唬。Debian、Ubuntu 可以看 journalctl -u ssh --since today 或 /var/log/auth.log,RHEL 系通常看 journalctl -u sshd 或 /var/log/secure,公网机器很快就会出现对常见用户名和密码的自动尝试。强且唯一的密码并不等于明文暴露,但只要保留密码登录,就要长期承受暴力猜测、凭据填充和人为复用密码的风险;对只由自己维护的 VPS,我没有保留这条入口的必要。
密钥:不靠猜不到,靠解不开#
密码靠保守秘密:强度够不够、有没有在别处泄露过、会不会图省事复用,每一环都系在人身上。密钥靠数学:私钥不出门,公钥被拿走也推不出私钥,登录验证靠签名完成——网络上传输的东西里没有可以截下来重放的秘密。持续扫描磨得穿人性的弱点,磨不动数学。
ssh-keygen -t ed25519 -a 64 -C "s1oopx@laptop" # 在本地生成,按提示设置口令ssh-copy-id s1oopx@你的服务器IP # WSL / Git Bash 下安装公钥ssh s1oopx@你的服务器IP 'id && sudo -v' # 先验证登录与 sudo这里假设服务器上已经有 s1oopx 这个 sudo 用户;如果服务商只给 root,先在当前会话里创建普通用户,再安装公钥。私钥永远留本地,服务器只放 .pub 公钥。 -a 64 提高私钥口令的 KDF 轮数,不能替代一个真正的口令;日常嫌重复输入麻烦,可以交给系统自带的 ssh-agent,不要保存一份无口令私钥来换方便。
为什么 ed25519 不是 RSA#
| RSA | ed25519 | |
|---|---|---|
| 新密钥的使用体验 | 密钥与签名较大,参数要选长度 | 密钥短,参数少,性能稳定 |
| 我的选择 | 能用 | 新场景默认它 |
ed25519 的公钥短,生成和签名快,也没有「RSA 到底选 2048 还是 4096 位」的参数纠结。现代 OpenSSH 环境里,它是简单而稳妥的默认选择。
不是 RSA 错了:老设备、旧 OpenSSH,以及要求 FIPS 算法集合的环境仍可能需要 RSA。我的个人 VPS 没有这些兼容约束,所以不额外背一套选择成本;遇到受限环境时再生成一把 RSA 3072 或 4096 密钥,并为不同主机分别配置即可。
改端口:降噪,不是主防御#
Port 22222扫完整端口当然能找到新端口,所以改端口不能提高认证强度。它的现实作用只是减少只碰 22 端口的低成本扫描,让认证日志安静一些;具体能降多少取决于 IP 暴露程度和扫描器策略,不应把它算进核心安全边界。
| 措施 | 性质 |
|---|---|
| 密钥登录 | 主防御 |
| 禁密码 / 禁 root 密码登录 | 主防御 |
| 改默认端口 | 降噪,非主防御 |
| 防火墙只放行需要的端口 | 主防御 |
把「降噪」和「防御」分开记,就不会对改端口抱不切实际的期待,也不会因为它挡不住定向攻击就否定降噪价值。
降噪的实际收益是日志质量:失败尝试少一些,真正异常的来源更容易被看见。但非默认端口也会增加客户端、防火墙和自动化配置成本;如果已经通过 VPN、Tailnet 或来源 IP 白名单限制 SSH,继续改端口的收益通常很小,可以不做。
改了端口,每次 ssh -p 22222 也烦。写进本地 ~/.ssh/config,以后一个 ssh vps 搞定:
Host vps HostName 你的服务器IP Port 22222 User s1oopx IdentityFile ~/.ssh/id_ed25519退路纪律:别关当前会话#
我的 Debian / Ubuntu 主机会把本机策略写到独立文件,避免升级时和主配置混在一起。OpenSSH 对多数单值配置采用「先读到的值生效」,因此先检查现有 drop-in,再使用靠前的文件名:
sudo grep -RHE '^(Port|PubkeyAuthentication|PasswordAuthentication|KbdInteractiveAuthentication|PermitRootLogin)' \ /etc/ssh/sshd_config /etc/ssh/sshd_config.d 2>/dev/nullsudoedit /etc/ssh/sshd_config.d/00-hardening.confPubkeyAuthentication yesPasswordAuthentication noKbdInteractiveAuthentication noPermitRootLogin noPort 22222如果必须保留 root 密钥作为救援入口,可以把最后一项改成 PermitRootLogin prohibit-password,但这意味着 root 仍可直接登录,不要把它描述成「禁 root」。我已经验证普通用户能登录且能 sudo,所以直接选择 no。
sudo ufw allow 22222/tcp # 使用 UFW 时,先放行新端口sudo sshd -t # 语法检查,无输出才是通过sudo sshd -T | grep -E '^(port|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin) 'sudo systemctl reload ssh # RHEL 系服务名通常是 sshd整个过程都保留当前已登录会话,再开本地新窗口执行 ssh -p 22222 s1oopx@服务器IP。 新会话里再次运行 sudo -v,确认无误后才删除旧的 22 端口防火墙规则并关闭旧窗口。sshd -t 只保证语法能解析,sshd -T 才让我看到合并主配置、drop-in 后的最终值;新窗口实登则验证了密钥、网络和权限这一整条链。即便操作失误,服务商控制台仍应作为最后退路,但我不会拿它代替前面的验证。
半破坏性操作:先验证 → 留退路 → 新窗测试 → 确认通了再关旧会话。和防火墙启用前先放行 SSH 端口同源。
和 Tunnel 一正一反#
Cloudflare Tunnel 能让 Web 服务不直接开放公网入站,这篇守的是仍暴露在公网的 SSH 管理口。我的 SSH 没塞进同一条 Tunnel,原因不是隧道不安全,而是不想让 Web 接入层故障时同时失去管理入口;代价是 SSH 仍要独立承受公网扫描。如果机器数量增加,或来源网络稳定,我会优先考虑 Tailnet、VPN 或防火墙白名单,把管理口从公网扫描面上拿掉,而不是继续叠更多 SSH 参数。
做完这些再看 journalctl -u ssh 里被拒的密码尝试,会觉得庆幸——扫描是 7×24 不停的。
