教程 搜索 / 发布与基础设施 / 为生产环境准备全新 Linux 服务器
📝 文字 ● 中级 更新于 2026-05-13

为生产环境准备全新 Linux 服务器

全新的 Ubuntu VPS 并不具备生产就绪的状态。公网 IP 上线后数小时内,自动化扫描器就会在 SSH 上尝试 root/admin/test 等常见密码。通用清单——更新、非 root 用户、仅 SSH 密钥认证、防火墙、fail2ban、swap、自动补丁——能将一台裸机变成可以放心指向域名的服务器。二十分钟的投入,在第一次真正需要的时候就会得到回报。

全新 Linux VPS 的默认状态是"一切放行,请进"。SSH 开启了 root 登录,密码认证也处于启用状态(通常是服务商通过邮件发送的默认密码),没有运行任何防火墙,也没有配置自动更新。公网 IP 分配后数分钟内,日志里就会出现来自自动化僵尸网络的大量 SSH 尝试记录,每小时数以千计,它们不断试探常见用户名和弱密码。大多数会失败,但也有漏网之鱼。

以下的加固措施并不特别。DigitalOcean、AWS、Hetzner、Linode 以及其他所有 VPS 服务商十年来一直在推荐同样的清单。之所以要认真完成,而不是跳过,是因为这些步骤有条件地很快:第一次需要二十分钟;之后每次复制同一份镜像或复用同一个 Ansible role 就行了。做过一次之后,这永远不会成为瓶颈。

本教程将逐步介绍 Ubuntu 22.04 / 24.04 服务器的通用首次启动清单(Debian 操作完全相同;Alpine 有所不同,相关差异会单独说明)。完成这些步骤后,你的服务器将具备:一个非 root 的部署用户、仅 SSH 密钥认证、活跃的防火墙、SSH 暴力破解限速、适合实例大小的 swap 文件、正常运行的日志轮转,以及配置好的无人值守安全更新。之后你就可以部署你的应用程序了。

你将学到什么

前置条件:一台具有公网 IP 和 root 访问权限的全新 Ubuntu 22.04 或 24.04 服务器。本地 SSH 公钥(~/.ssh/id_ed25519.pub~/.ssh/id_rsa.pub)。如果你的笔记本上还没有 SSH 密钥,请先生成一个:ssh-keygen -t ed25519 -C "[email protected]"不要跳过这一步、继续使用密码认证——那样会让本教程的其他步骤失去意义。

第一步:在做任何事之前先更新系统

1

新服务器上的第一条命令

你启动的镜像是某个时间点的快照。从快照到你启动之间,安全补丁已经发布。在做其他任何事之前先把它们拉下来,因为后续步骤假设你使用的是最新的软件包:

ssh root@<PUBLIC_IP>
apt update
apt full-upgrade -y
# 内核更新可能会提示重启——选择接受。
reboot

等待一分钟后重新连接。现在你运行的是最新的补丁。选用 full-upgrade 而非 upgrade,是因为后者会搁置那些需要依赖变更的包——而那些通常正是安全相关的包。

第二步:创建非 root 部署用户

2

root 留给紧急情况;日常工作通过 sudo 进行

每个常见的用户名——rootadminubuntudeploywww——都在被扫描器猜测。缓解措施是非默认用户名与仅密钥认证结合起来,但用户名的重要性其实低于人们的想象——防火墙和密钥认证才是真正起作用的。随便取个好记的名字;用 deploy 就挺好。

adduser deploy
# 设置一个密码。你基本上永远不会手动输入它;
# 随机生成一个长密码存进密码管理器即可。
# 只有在 sudo 要求时才会用到(你也可以禁用它)。

usermod -aG sudo deploy

将你的 SSH 密钥复制到新用户的 authorized_keys,这样你就可以无密码登录为 deploy

rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
# rsync 会将 root 的 .ssh/authorized_keys 复制到 /home/deploy/.ssh/
# 并修正所有权。验证:
ls -la /home/deploy/.ssh/

打开第二个终端(先不要断开 root 会话)并测试:

ssh deploy@<PUBLIC_IP>
# 应该可以无密码登录。从现在起,以 deploy 身份工作;
# 需要时用 sudo 提权。
暂时不要断开 root 会话。如果下一步错误配置了 SSH 并将你锁在外面,还开着的 root 会话就是你修复问题的方式。在第四步完整成功之前,请保持它开启。

第三步:禁用 SSH 的 root 登录和密码认证

3

sshd_config 中的两行配置;这是单次最大的安全收益

deploy 用户身份操作(这样你也能顺便验证 sudo 可用):

sudo nano /etc/ssh/sshd_config

找到并设置以下内容(如有注释则取消注释):

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
# 可选但建议:
ChallengeResponseAuthentication no
UsePAM yes
KbdInteractiveAuthentication no

保存。在 Ubuntu 22.04+ 上,SSH 配置经常分散在 /etc/ssh/sshd_config.d/*.conf 文件中;服务商镜像有时会放一个 50-cloud-init.conf,重新启用密码认证。检查一下:

sudo grep -rE "^(Password|PermitRoot)" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
# .d/ 目录中任何将 PasswordAuthentication 设为 yes 或
# PermitRootLogin 设为 yes 的行都会覆盖你的修改。将它们注释掉。

应用配置:

sudo systemctl restart ssh
# (在旧版 Ubuntu 上,unit 名称是 sshd.service。systemctl restart sshd
#  效果相同;systemd 会解析别名。)

在你的笔记本上,打开一个终端,尝试:

ssh deploy@<PUBLIC_IP>     # 应该仍然能正常登录
ssh root@<PUBLIC_IP>       # 应该被拒绝:"Permission denied (publickey)"

如果两者的表现都符合预期,原来的 root 会话就可以关闭了。如果出了任何问题,原窗口中开着的 root 终端就是你重新编辑 sshd_config 的方式。

第四步:启用 ufw 并配置每台 Web 服务器都需要的三条规则

4

默认拒绝,只开放必要端口

ufw("简单防火墙")是带有友好界面的 iptables。在 Ubuntu 上已预装但默认未激活:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH       # 端口 22
sudo ufw allow http          # 端口 80
sudo ufw allow https         # 端口 443
sudo ufw enable
# 它会提示断开 SSH 的警告——你在上一行已经放行了,所以是安全的。
sudo ufw status verbose

如果你的服务商有自己的外部防火墙(Lightsail 的防火墙标签、AWS 安全组、DigitalOcean Cloud Firewalls),请同时保留 ufw 服务商防火墙。双重防护;任何一层都能在另一层失效时守住阵线。

如果你的应用监听非标准端口(Node 开发用的 3000、Postgres 的 5432、Redis 的 6379)——不要在 ufw 中开放这些端口。将它们绑定到 127.0.0.1,通过 nginx 在 80/443 端口对外提供服务。防火墙规则列表应该永远只有三行;其他一切都走内部通道。

第五步:安装 fail2ban

5

自动封禁登录失败的 IP

fail2ban 监视日志文件中的失败登录尝试,并添加防火墙规则封禁相应 IP。在仅密钥 SSH 的情况下,这是双重保险,但保险本身的成本很低:

sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban

默认的 jail 会监控 SSH。在本地配置文件中覆盖默认设置(不要编辑原始文件——软件包更新会将其覆盖):

sudo tee /etc/fail2ban/jail.local >/dev/null <<'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
EOF
sudo systemctl restart fail2ban

验证:sudo fail2ban-client status sshd。已封禁的 IP 会随时间积累;如果你不小心把自己封禁了,使用 sudo fail2ban-client unban <IP> 解封。

第六步:添加 swap(小内存实例尤为重要)

6

不是为了性能——是为了在 OOM 时不挂掉

大多数 VPS 服务商默认不分配 swap。在 512 MB 或 1 GB 的实例上,一次突发内存峰值(失控的日志解析、配置有误的 Node 应用)会触发 Linux OOM killer,它通常会干掉内存占用最高的进程——往往就是你的应用。Swap 不能让系统变快;它给 OOM killer 提供了喘息空间,防止最糟糕的情况——"服务随机消失"。

# 对于 1 GB 实例,2 GB swap 是合理的。按比例调整。
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 使其在重启后持久化:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 调优:swap 使用的激进程度。数值越低 = 越晚才动用 swap。
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swap.conf
sudo sysctl -p /etc/sysctl.d/99-swap.conf

# 验证:
free -h

free -h 的输出现在会显示 Swap 行,容量为 2.0 GB。服务器再也不会因为一次内存峰值就 OOM 掉你的应用了。

第七步:配置 unattended-upgrades

7

安全补丁在你睡觉时自动打上

没打的补丁,就是漏洞利用的入口。Ubuntu 自带 unattended-upgrades 用于自动安全更新;在大多数服务商镜像上它已安装但未启用:

sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure --priority=low unattended-upgrades
# 对"自动下载并安装稳定版更新"选择"是"

# 验证它按计划运行:
systemctl status unattended-upgrades.timer

默认只有安全更新会自动应用。应用更新(Node、Postgres)保持手动——这些更新可能破坏兼容性,不应自动应用。如果想启用更广泛的自动更新(不推荐用于生产环境),编辑 /etc/apt/apt.conf.d/50unattended-upgrades 并取消注释其他源的行。

内核更新后自动重启默认关闭。如果你想开启(推荐用于无状态服务器),在同一文件中设置 Unattended-Upgrade::Automatic-Reboot "true";Unattended-Upgrade::Automatic-Reboot-Time "03:00";。有持久化状态的服务器(数据库)应保持手动重启。

第八步:日志轮转完整性检查

8

已经配置好了;验证它正常工作

Ubuntu 自带配置合理的 logrotate。你不需要额外设置,但要验证它确实在运行——配置错误的 logrotate 会在几个月内用旧日志填满磁盘:

sudo logrotate -d /etc/logrotate.conf 2>&1 | head -20
# -d 是"调试"模式——显示它将要做什么,但不实际执行。
# 你应该看到日志文件被识别并等待轮转。

# 手动运行(强制执行):
sudo logrotate -f /etc/logrotate.conf

当你安装自己的服务(nginx、postgres、自定义应用)时,它们通常会在 /etc/logrotate.d/ 中放置配置文件。默认设置——每周轮转、保留 4 周、压缩旧日志——对大多数服务来说都足够了。

第九步:最终重启并验证

9

在宣告胜利之前先确认能挺过重启

sudo reboot
# 等待 30 秒,然后重新连接:
ssh deploy@<PUBLIC_IP>

重新连接后,快速运行五项检查:

# 1. 你是 deploy 用户,而不是 root。
whoami    # → deploy

# 2. Swap 处于活跃状态。
free -h | grep Swap    # → 2.0Gi total

# 3. 防火墙处于活跃状态并已正确配置。
sudo ufw status    # → Status: active,以及你的三条规则

# 4. fail2ban 正在运行。
sudo systemctl is-active fail2ban    # → active

# 5. Unattended-upgrades 正在运行。
sudo systemctl is-active unattended-upgrades.timer    # → active

五项全绿:服务器已就绪。是时候安装你的应用程序栈并部署了。

本教程未涵盖的内容(以及如何进阶)

数据库 / 缓存——Postgres、Redis 等是独立的话题,各有其加固方式。参见安装 Postgres 和 Redis

将 SSH 移到非标准端口——将 SSH 从 22 迁移到随机高端口存在争议:它能减少日志中大部分僵尸网络的噪音,但这属于通过隐晦实现的安全(security-through-obscurity),而非真正的防护。fail2ban + 仅密钥认证 + 禁止 root 才是真正有效的防御。如果你确实要移动端口,记得 ufw allow <new-port>/tcp 并更新本地的 ~/.ssh/config

监控与日志传输——本地 syslog 作为审计记录还不错,但在服务器无法访问时毫无调试价值。CloudWatch agent(AWS)、Grafana Cloud(免费层)或 Sentry(用于应用错误)可以将日志传输到服务器之外。

备份——服务商快照是最简单的基础。对于数据库状态,通过 cron 定期执行 pg_dump | gzip | aws s3 cp -,并定期验证恢复流程。

幂等性重复执行——当你需要在第二台服务器上重复这些操作时,将它写成 Ansible playbook 或 bash 脚本。服务商专属镜像是下一步的选择;枯燥的文本脚本才是正确的起点。

接下来