Language:Chinese VersionEnglish Version

大多数服务器安全加固指南都是为团队编写的。它们假设你有一位专门的 DevOps 人员,有变更管理流程,并且有足够的时间阅读 40 页的安全策略。如果你是独立开发者,为一两个项目运行一三台 VPS 实例——可能是一个 WordPress 站点、一个 Node.js API 或一个基于 Docker 的副项目——那些指南要么会让你不知所措,要么会让你陷入配置的兔子洞,在第三步时就放弃了。

本指南则不同。它围绕独立开发者在启动新 VPS 后的前 30 分钟实际需要做的事情,持续的安全加固真正能保护你什么,而哪些只是安全表演,以及如何设置足够的自动化监控,让你不需要每晚盯着日志。它反映了在同时为多个项目运行精简基础设施过程中吸取的惨痛教训。

为什么独立开发者是特别有吸引力的攻击目标

人们普遍存在一种误解,认为独立开发者不值得攻击。现实几乎正好相反。你的服务器通常比企业服务器更容易受到攻击,不是因为你技能不足,而是因为你时间有限。你需要独自平衡功能开发、客户支持和基础设施维护。安全任务被推迟了。

自动化扫描器不在乎你是财富 500 强公司还是单人运营。在新 provision 一个 DigitalOcean droplet 或 Linode 实例后的几分钟内,机器人已经在探测你的 SSH 端口。使用默认设置和弱 root 密码的服务器通常会在几小时内被攻破。针对 SSH 的暴力破解不是有针对性的——它们是自动化的且无情的。

通用的安全加固指南通常在两个方面让独立开发者失望。首先,它们假设你有时间为多个系统进行深度防御分层。其次,它们通常推荐在企业环境中重要但对独立运营者来说会带来实际操作摩擦的安全措施,而没有相应的收益。这里的目标是找出能阻止 80% 真实威胁的 20% 的措施。

前 30 分钟:其他任何事情之前该做什么

当你启动一个新的 VPS 时,计时就开始了。在安装应用程序之前,在配置 Web 服务器之前,在做任何其他事情之前,请执行这个流程。

步骤 1:立即创建非 root 用户

以 root 身份登录的时间刚好够创建一个你将用于其他所有事情的非特权用户。

adduser deploy
usermod -aG sudo deploy

然后将你的 SSH 密钥复制到该用户:

rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

步骤 2:在接触任何东西之前更新系统

在安装任何软件包之前:

apt update && apt upgrade -y

在全新的 Debian 或 Ubuntu 系统上,这通常会修复基础镜像中附带的 20 到 40 个已知漏洞。

步骤 3:立即加固 SSH

打开 /etc/ssh/sshd_config 并修改以下值:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port 2222

将 SSH 端口更改为 22 以外的其他值并不能阻止有决心的攻击者,但它可以消除认证日志中大约 90% 的自动化噪音。这使得真正的威胁信号更容易被发现。编辑后,重启服务:

systemctl restart sshd

在从第二个终端窗口验证新配置正常工作之前,不要关闭现有的 SSH 会话

步骤 4:设置自动安全更新

安装 unattended-upgrades 并配置它自动应用安全补丁:

apt install unattended-upgrades
dpkg-reconfigure unattended-upgrades

编辑 /etc/apt/apt.conf.d/50unattended-upgrades 并确保安全行未被注释。这不是可选的。未打补丁的服务器是针对小型基础设施的真实攻击中最常见的入口点。

步骤 5:安装和配置 fail2ban

apt install fail2ban

创建 /etc/fail2ban/jail.local 文件,内容如下:

[sshd]
enabled = true
port = 2222
maxretry = 3
bantime = 3600
findtime = 600

这会禁止任何在 10 分钟内 SSH 认证失败 3 次的 IP 地址一小时。如果您希望禁止时间更长,可以增加 bantime 的值。对于大多数独立开发者设置,此配置单独就能阻止绝大多数自动化暴力破解尝试。

SSH 加固:真正重要的措施与安全表演

SSH 是大多数独立开发者在不影响实际安全的情况下花费不成比例时间的地方。让我们直接谈谈什么才是真正重要的。

真正能保护你的措施

  • 完全禁用密码认证。如果您仍在使用密码进行 SSH 认证,这是您最高优先级的修复。基于密钥的认证意味着对 SSH 进行暴力破解在计算上变得不可行。
  • 使用 Ed25519 密钥而非 RSA。如果您正在生成新的 SSH 密钥,请使用 ssh-keygen -t ed25519。比旧的 RSA 2048 位密钥更短、更快,并且更能抵抗侧信道攻击。
  • AllowUsers 指令。在您的 sshd_config 中添加 AllowUsers deploy。这意味着即使有人通过其他漏洞创建了另一个用户账户,他们也无法通过 SSH 登录。
  • SSH 密钥密码短语。如果您的笔记本电脑被盗,您的私钥就会暴露。密钥上的密码短语添加了第二个身份验证因素,而无需任何服务器端配置更改。

对独立开发者而言大多是安全表演

  • 端口敲门技术。 在某些情况下是合法的,但在多台客户端机器上配置敲门序列的操作开销,使得对于独立设置来说,这种做法带来的痛苦多于益处。
  • 通过 TOTP 的 SSH 双因素认证。 配置复杂性以及将自己锁在外的风险——特别是在凌晨2点故障排查时——往往已经超过了在你已经使用带密码的密钥认证时所带来的好处。
  • 将 SSH 端口隐藏在非常高且不常用的端口号上。 现在的机器人会扫描完整的端口范围。隐藏端口可以减少日志噪音,但提供的实际安全性非常有限。

典型独立开发者堆栈的防火墙规则

典型的独立开发者 VPS 运行着 WordPress、Node.js 和 Docker 容器的某种组合。以下是针对该堆栈的实用 UFW 配置。

安装并重置 UFW 为默认设置:

apt install ufw
ufw default deny incoming
ufw default allow outgoing

然后只打开你实际需要的内容:

ufw allow 2222/tcp    # 自定义端口的 SSH
ufw allow 80/tcp      # HTTP
ufw allow 443/tcp     # HTTPS
ufw enable

如果你正在运行一个监听端口3000的 Node.js 应用,不要公开开放该端口。让 Nginx 代理到它,只暴露80和443端口。你的 Nginx 配置看起来像这样:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

Docker 和你可能不知道的防火墙问题

这是一个特定且严重的问题,会让独立开发者措手不及。Docker 直接修改 iptables 规则并绕过 UFW。如果你使用 -p 3000:3000 运行 Docker 容器,无论你的 UFW 规则如何,Docker 都会向全世界开放该端口。

要解决此问题,编辑 /etc/docker/daemon.json

{
  "iptables": false
}

然后管理你自己的 iptables 规则,或者使用将容器仅绑定到本地主机的替代方法:-p 127.0.0.1:3000:3000。对于大多数独立开发者设置来说,本地主机绑定方法更简单,不需要直接操作 iptables。

自动化监控:当你是唯一值班人员时需要设置什么

当你是一个人团队时,你无法时刻查看日志。解决方案是让日志自行监控,并在重要事件发生时通知你。

使用 Logwatch 获取每日摘要

apt install logwatch
logwatch --output mail --mailto your@email.com --detail medium

配置它以发送每日摘要。您将获得关于身份验证尝试、sudo使用、服务故障和磁盘空间的易读摘要。每天早上扫描不到两分钟,并能发现您完全可能忽略的异常情况。

使用Monit监控服务健康状态

Monit是一个轻量级进程监控器,可以自动重启服务,并在某些服务停止时向您发出警报:

apt install monit

Nginx的最小/etc/monit/monitrc配置如下:

check process nginx with pidfile /run/nginx.pid
  start program = "/bin/systemctl start nginx"
  stop  program = "/bin/systemctl stop nginx"
  if failed port 80 protocol http then restart
  if 3 restarts within 5 cycles then timeout

为您的Node.js进程、MySQL或Postgres以及任何其他关键服务设置类似的配置块。Monit将尝试自动重启服务,如果在您指定的时间阈值内无法恢复,则会向您发送电子邮件。

使用外部服务进行简单的运行时间监控

Monit监控服务器上的进程,但它无法告诉您整个服务器何时无法访问。使用外部免费服务如UptimeRobot或Better Uptime,每五分钟ping一次您的域名。两者的免费套餐对于大多数独立开发者项目来说已经足够,配置大约需要10分钟。

在磁盘空间成为危机前发出警报

独立开发者环境中意外停机最常见的原因之一是磁盘已满。一个简单的cron作业可以处理这个问题:

0 8 * * * df -h | awk '$5 > 80 {print}' | mail -s "Disk usage alert" your@email.com

这个命令每天早上8点运行,如果任何分区超过80%已满,则会向您发送电子邮件。虽然简单,但有效。

不依赖”抽空去做”的备份策略

关于独立开发者备份,诚实的说法是:如果需要手动步骤,最终可能会被忽略。唯一可靠的备份是那些无需您思考就会自动运行的备份。

独立项目的3-2-1基准

  • 3份数据副本
  • 2种不同的存储类型
  • 1份异地备份

对于典型的独立设置,这意味着:您VPS上的实时数据,托管服务提供商内的自动快照(DigitalOcean、Linode和Vultr都提供定时快照),以及每周或每日备份到异地位置如Backblaze B2或AWS S3。

自动将MySQL备份到S3

安装AWS CLI并使用特定存储桶的IAM用户进行配置。然后创建/usr/local/bin/backup-db.sh

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases | gzip > /tmp/backup_$DATE.sql.gz
aws s3 cp /tmp/backup_$DATE.sql.gz s3://your-backup-bucket/mysql/
rm /tmp/backup_$DATE.sql.gz

使其可执行并添加到 crontab:

chmod +x /usr/local/bin/backup-db.sh
0 2 * * * /usr/local/bin/backup-db.sh

MYSQL_ROOT_PASSWORD 存储在 /etc/environment 中,而不是硬编码在脚本中。为此 S3 存储桶创建的 IAM 用户应该对该特定存储桶具有写入权限,而不能有其他权限。

测试您的备份

这是大多数人都会跳过的部分。每月一次,将备份恢复到测试实例并验证数据是否完整。从未测试过的备份是不可信赖的备份。

个人设置及实现过程

本指南中描述的配置接近于处理真实流量的多个生产服务器运行的配置。它并非一直如此整洁。早期的设置包括数月未更新的 WordPress 安装,由于在夜间部署期间应用程序配置错误,导致 MySQL 监听公共端口,以及增长到填满整个磁盘的日志文件,这些日志文件在没有任何警告的情况下导致服务崩溃。

这些教训在时间上是昂贵的,如果不是金钱上的话。其中几个被证明最有用:

第一个攻击向量几乎总是某些旧东西。在每一个值得调查的事件中,入口点都是过时的插件、未修补的库,或一个应该被禁用却仍在运行的服务。自动更新和定期运行 apt upgrade 可以消除大部分可利用的攻击面。

审计实际在监听的内容。定期运行 ss -tlnp。通常会发现应该在 127.0.0.1 上可访问的服务却在监听 0.0.0.0。特别是 Redis,有很长一段历史被开发者无意中暴露到公共网络,因为他们没有意识到它是可访问的。

将服务器设置清单保存在版本控制中。维护一个简短的 Markdown 文件,记录安装了什么、为什么安装以及如何配置,这不是官僚主义——当你设置某物三个月后在半夜进行故障排除时,这是必不可少的。

监控开销是前期投入的。设置 Logwatch、fail2ban、Monit 和 UptimeRobot 总共大约需要两小时。之后,它们几乎不需要持续关注。这两小时的投资已经多次发现了问题,否则这些问题可能会演变成数小时的停机。

如果您只有一小时,应该优先考虑什么

如果您已经有一个正在运行的 VPS 并阅读本文,且只有有限时间,优先顺序如下:

  1. 禁用 root SSH 登录和密码认证
  2. 启用自动安全更新
  3. 安装并配置 fail2ban
  4. 使用 ss -tlnpufw status 检查哪些端口是公开可访问的
  5. 设置至少一个外部运行时间监控
  6. 确认自动化数据库备份正在运行,并测试其中一个

步骤 1 到 3 可以阻止最常见的自动化攻击。步骤 4 和 5 提供可见性。步骤 6 是在其他所有措施失效时能拯救你的措施。

对于独立开发者来说,服务器加固不是要实现完美安全。而是要提高攻击成本超过你目标的价值,移除自动化扫描器依赖的低垂果实,并确保当确实出现问题时——最终总会出现问题——你有快速恢复所需的数据和备份。从最初的 30 分钟检查清单开始,在此基础上逐步构建,并自动化所有可能被跳过的操作。

By Michael Sun

Founder and Editor-in-Chief of NovVista. Software engineer with hands-on experience in cloud infrastructure, full-stack development, and DevOps. Writes about AI tools, developer workflows, server architecture, and the practical side of technology. Based in China.

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed