据我们整个服务器集群的统计,一台刚上线、使用公网 IP 的新服务器,平均约 4 分钟内就会遭遇第一次 SSH 暴力破解尝试。随后的攻击大多并不高明:猜密码、探测老旧管理面板的扫描器、寻找开放 Redis 的机器人。这份清单,就是我们建议您在部署任何业务之前先花掉的那一小时,各步骤按重要程度排序,基于 Ubuntu 24.04。只需稍作改动,每一步同样适用于 Debian。
0. 登录之前
下单之前,请先在控制面板的 SSH 密钥中添加您的 SSH 公钥。使用密钥部署的服务器,从首次启动起就已禁用密码认证,这样第 1 步就只是检查一下,而不必修改什么。如果您是用密码部署的,请立即在同一次会话中完成第 1 步和第 2 步。
1. 仅允许密钥登录,锁定 root
创建一个用户,把您的密钥交给它,并对所有人关闭密码认证。
adduser --disabled-password --gecos "" ops
usermod -aG sudo ops
mkdir -p /home/ops/.ssh && cp ~/.ssh/authorized_keys /home/ops/.ssh/
chown -R ops:ops /home/ops/.ssh && chmod 700 /home/ops/.ssh
cat > /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 20
EOF
sshd -t && systemctl reload ssh
请打开第二个终端,确认可以用密钥以 ops 身份登录,之后才能关闭第一个终端。关键在于使用 drop-in 目录:Ubuntu 的更新不会覆盖它,而直接修改 sshd_config 本身则不然。
2. 默认拒绝的防火墙
三条规则就能覆盖大多数服务器:放行 SSH,放行 HTTP 和 HTTPS,其余一律拒绝。nftables 已经预装;ufw 是它一个好用的前端。
ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'ssh, rate limited'
ufw allow 80,443/tcp comment 'web'
ufw enable
在 22 端口上用 limit 代替 allow,同一个地址在 30 秒内发起 6 次连接尝试后,就会被拒绝,这样不等 fail2ban 出手,就已经削弱了暴力破解的势头。请注意,Docker 默认会绕过 ufw 直接发布端口;如果您运行容器,要么把端口绑定到 127.0.0.1 并在前面放一个反向代理,要么把 "iptables": false 写入 /etc/docker/daemon.json 并自行管理规则。
3. 自动安装安全补丁
我们的镜像已预装无人值守升级(unattended-upgrades),但不一定处于启用状态。请把它打开,并为机器设置一个重启时间窗口,这样内核更新才能真正生效。
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
cat > /etc/apt/apt.conf.d/52reboot <<'EOF'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
EOF
如果每晚重启无法接受,就不要开启自动重启,转而订阅 Ubuntu 的安全公告邮件列表。真正不可接受的,是没人打补丁的服务器;我们收到的滥用举报,大多就是从这里开始的。
4. 用 fail2ban 应对长尾攻击
apt install -y fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
EOF
systemctl enable --now fail2ban
在仅允许密钥登录的情况下,fail2ban 与其说是在保护登录,不如说是在让日志保持可读。在 24.04 上,systemd 后端很重要,因为 /var/log/auth.log 默认不再写入。
5. 时间、主机名与区域设置
证书、日志和双重验证码都依赖准确的时钟。我们的镜像使用 systemd-timesyncd,向我们自己的 stratum-2 服务器同步时间;请用 timedatectl 确认输出中是 System clock synchronized: yes。再用 hostnamectl set-hostname web-01 把主机名设置为您在控制面板中使用的那个,这样反向 DNS、邮件和监控才能保持一致。
6. 几项内核参数
cat > /etc/sysctl.d/60-hardening.conf <<'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_syncookies = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
EOF
sysctl --system
这些是加固型发行版上的默认值,而且不会带来任何代价。不要从论坛上随手复制各种所谓“性能优化”的 sysctl 参数;其中大多数是为更早的内核写的,有些反而会适得其反。
7. 弄清楚哪些服务在监听
ss -tulpn 会列出所有处于监听状态的套接字。在全新的镜像上,它应当只显示 22 端口上的 sshd 和 127.0.0.53 上的 systemd-resolved,别无其他。此后您添加的每一项服务,要么绑定到 localhost,要么配有一条您能解释清楚的防火墙规则。Redis、MongoDB、Elasticsearch 和 Docker 的 API,都曾在不同时期以“监听所有网络接口且没有认证”的状态发布;每一种都曾让某些人遭遇数据泄露。
8. 用户与 sudo
一人一个账户。共用 root 密码,就是您搞不清谁删了什么的根源。保持 sudo 需要输入密码(这是默认行为);如果有多人管理这台服务器,请把 Defaults logfile=/var/log/sudo.log 写入 /etc/sudoers.d/ 下的某个文件,这样特权命令就会被记录下来。
9. 在控制面板启用双重验证,SSH 则不必
请在您的 CheapServ 账户上启用 TOTP 双重验证:控制面板可以重装服务器、打开控制台并修改 root 密码,因此它才是更有价值的攻击目标。使用由硬件保护的密钥进行 SSH 登录(例如通过 ssh-keygen -t ed25519-sk 创建的 YubiKey 密钥),比在 SSH 上叠加 TOTP 更安全,操作也更简单。
10. 首次部署前先做一次备份
趁系统干净、状态确认良好,现在就在控制面板中创建一个快照。这将是您能拥有的最快的回滚手段。然后,为您之后会产生的数据设置异地备份;我们另写了一篇单独的指南,其中有一套每月只需几美元的 restic 方案。
11. 出问题时能收到通知
至少要有一个免费的在线监控,每分钟访问一次您的网站,并在异常时给您发邮件。更好的做法是使用 netdata(我们的附加服务之一)实时查看 CPU、磁盘和网络,如果您的服务器不止一台,再加上日志转发工具。您不会天天盯着监控仪表盘,您需要的是告警。
12. 十分钟复查,然后部署
ssh ops@server用密钥可以登录;ssh root@server用密码登录会被拒绝。ufw status verbose显示默认拒绝,并列出您设置的那三条规则。systemctl status unattended-upgrades fail2ban显示两个服务均为 active 状态。ss -tulpn的输出里,没有任何您说不出名字的东西。- 已有一个快照,并且已在控制面板中启用双重验证。
这就是那一个小时。此后的一切,从反向代理到数据库,都建立在这样一个基础之上:它不会成为您在凌晨 3 点接到电话的原因。如果您不想手动完成这些步骤,可以在订购页面中选择 fail2ban、自动安全更新和 Tailscale 附加服务,它们会在开通过程中帮您完成第 3 步、第 4 步,并建立一条私有的管理通道。
