VPS 安全加固实战指南 2026:从零到堡垒级服务器防护
新开的 VPS 交付时几乎都是同一副模样:SSH 开在 22 端口、允许密码登录、root 可以直登、防火墙没启用、带着一批没用过的服务。自动化程序在不间断地扫描这类机器,撞上弱口令或未修补的漏洞只是时间问题。加固不难,难的是顺序——次序决定了你前几个小时挡住的是真实入侵路径,还是无关紧要的角落。
本文的章节顺序就是执行顺序:攻击者最先够得着登录入口,其次是敞开的端口,再次是没打补丁的系统与服务;三层收紧后,才轮到监控告警这类「发现问题」的手段;最后是备份与恢复,决定最坏情况下你损失多大。每章都会讲清这一步在防什么、为什么排在这里。命令以 Debian / Ubuntu 为准。
加固优先级总表
时间有限时照这张表从上往下做,P0 几项一小时内能落地,已能挡住绝大多数自动化入侵。
| 优先级 | 动作 | 防的是什么 | 不做的后果 |
|---|---|---|---|
| P0 | 密钥登录并禁用密码认证 | SSH 暴力破解、撞库 | 密码不够强,被攻破只是时间问题 |
| P0 | 日常使用非 root 用户 + sudo | 误操作、失守即满盘皆输 | 一次提权漏洞即拿到最高权限 |
| P0 | 防火墙默认拒绝、只放行必要端口 | 端口扫描后的漏洞利用 | 数据库、面板、调试端口裸露公网 |
| P0 | 开启自动安全更新 | 已知 CVE 被批量利用 | 未修补的机器是扫描重点 |
| P1 | Fail2ban 守护 SSH | 持续性的分布式爆破 | 爆破不断且无法自动反击 |
| P1 | 关停无用服务、收紧关键文件权限 | 冷门服务的漏洞 | 每个多开的服务都是多一份攻击面 |
| P1 | 网络与内核参数加固 | IP 欺骗、重定向劫持 | 特定网络环境下可被借道或劫持 |
| P2 | 登录告警 + 文件完整性监控 | 已得手的入侵长期潜伏 | 被黑数月无人察觉 |
| P2 | 备份异地保存并做恢复演练 | 勒索、误删、整机损坏 | 出事时才发现备份不可用 |
一、账户与登录:先把大门换锁
绝大多数入侵的第一步是拿到一个能登录的账户,所以加固从登录开始:目标是只有持你私钥、用你指定账户的人能登进来。
1.1 先建普通管理员,再谈禁用 root
直接在 root 上做所有加固有个隐患:某步配置写错把自己关在门外时,连补救的余地都没有。先建日常管理员账户,全程用它 + sudo 操作,root 只留作应急。
# 创建日常管理用户
adduser deploy
# 授予 sudo 权限
usermod -aG sudo deploy
# 切换过去验证 sudo 可用(应返回 root)
su - deploy
sudo whoami1.2 密钥登录:先验证,再关密码
顺序在这里至关重要:必须先在新终端里确认密钥能登录成功,再去关密码认证。反过来操作,是新手把自己锁在门外最常见的原因。
# 本地生成 ED25519 密钥对(签名短、强度够,是目前的首选)
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/vps_key
# 把公钥送到服务器(此时仍用密码登录一次)
ssh-copy-id -i ~/.ssh/vps_key.pub deploy@your-vps-ip
# 另开一个终端,用密钥测试登录——成功之前不要动服务器配置
ssh -i ~/.ssh/vps_key deploy@your-vps-ip密钥验证通过后,再修改 SSH 服务端配置。改之前先留一份备份,改完先做语法检查再重启服务:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano /etc/ssh/sshd_config# 只允许密钥,密码认证彻底关闭
PasswordAuthentication no
PermitEmptyPasswords no
# root 不允许密码登录;保留密钥应急时可用 prohibit-password,想彻底关就写 no
PermitRootLogin prohibit-password
# 只允许这几个账户走 SSH
AllowUsers deploy
# 压缩爆破窗口:认证尝试次数与登录宽限时间
MaxAuthTries 3
LoginGraceTime 30
# 无人值守的会话自动断开(秒)
ClientAliveInterval 300
ClientAliveCountMax 2
# 用不到的转发功能关掉
X11Forwarding no
AllowTcpForwarding no# 先验语法,再重启(Debian/Ubuntu 的服务名是 ssh)
sudo sshd -t
sudo systemctl restart ssh一点提醒:不要往 sshd_config 里抄 Ciphers / MACs / KexAlgorithms 白名单。OpenSSH 的默认组合会随版本淘汰弱算法,手工钉死的列表只会变得比默认更差,还可能让新客户端连不上。高敏感场景可再加一层 TOTP 双因子(libpam-google-authenticator),但先把密钥和禁用密码做完。密钥细节见 SSH 密钥配置详解,双因子见 VPS 安全进阶:超越 Fail2ban。
1.3 改 SSH 端口:降噪可以,当护城河不行
把 22 改成高位随机端口,能让 auth.log 立刻清净——绝大多数自动化扫描只敲默认端口。但它不增加实质安全性:一次全端口扫描就能找到新端口,更防不住漏洞利用。可以改,改完记得同步防火墙和 Fail2ban 的端口号;但真正的护城河是上一节的密钥登录,不是端口号。
1.4 Fail2ban:登录口的自动门卫
密钥登录挡住了「猜对密码」,Fail2ban 处理另一件事:让反复试探的 IP 付出代价。配置不要改自带的 jail.conf,新建 jail.local 覆盖即可:
sudo apt install -y fail2ban
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# 10 分钟内失败 3 次,封禁 1 小时
findtime = 600
maxretry = 3
bantime = 3600
# 用 UFW 执行封禁(与后文防火墙保持同一套工具)
banaction = ufw
# 把自己的固定管理 IP 加进来,避免误封自己
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
# 如果改了 SSH 端口,这里同步改
port = ssh
# 累犯递增封禁,最长一周
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800sudo systemctl enable --now fail2ban
# 日常查看与解封
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 203.0.113.66Web 机器可再给 Nginx 加 limit-req、botsearch 等 jail,思路相同。完整玩法见 Fail2ban 防暴力破解配置完全指南。
1.5 sudo 的边界:两处常见误配要避开
sudoers 只用 visudo 编辑,它会在保存时做语法检查,写错不至于把自己锁死:
sudo visudo -f /etc/sudoers.d/security# 密码输错 3 次退出,凭证缓存 5 分钟
Defaults passwd_tries = 3
Defaults timestamp_timeout = 5两个常被抄进教程的选项不要加。Defaults requiretty 要求真实终端才能 sudo,会让大量脚本化运维失效,收益却接近于零。log_input / log_output 会把 sudo 会话里敲过的内容完整录进日志,包括手滑输进去的密码和密钥,是给自己埋的泄露坑;默认的执行日志(谁、何时、执行了什么)已足够审计。
二、网络暴露面:公网上只留你打算给人看的
登录锁好之后,数清楚这台机器在公网上敞着几个口。原则只有一句:默认全部拒绝,只逐个放行你要提供服务的端口。
2.1 先盘点:看看现在谁在监听
加固之前先做一次盘点,很多人会惊讶于自己机器上还跑着从没用过的服务:
# 查看所有监听端口及对应进程
sudo ss -tunlp
# 查看当前已建立的连接
ss -tunp | grep ESTABLISHEDss 的输出就是你的攻击面清单。每个监听端口问自己:这是什么服务、还在用吗、必须对全公网开放吗。第三个问题对管理型服务答案多半是否定的——数据库、缓存、面板后台要么绑回环只供本机用,要么只放行自己的来源 IP。
2.2 防火墙:默认拒绝,逐个放行
Ubuntu / Debian 上用 UFW 足够,它是给底层规则做的一层易用封装。注意顺序:先放行 SSH 端口再 enable,反了同样会把自己关在外面。
sudo apt install -y ufw
# 默认策略:入站拒绝、出站放行
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH 用 limit 而非 allow:自带每 30 秒最多 6 次新连接的限速
sudo ufw limit 22/tcp comment 'SSH'
# 对外提供 Web 服务才开这两项
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# 管理型端口只放行自己的 IP(示例为面板端口)
sudo ufw allow from 198.51.100.10 to any port 2053 proto tcp comment 'Panel'
sudo ufw enable
sudo ufw status verbose特别提醒数据库:MySQL 的 3306、Redis 的 6379 永远不要直接对公网开放。Redis 有过大量因公网暴露且无密码被批量入侵的案例;让应用和数据库同机或走内网,数据库只监听 127.0.0.1 或内网地址。
2.3 防火墙工具只选一个,别叠着用
UFW、nftables、裸 iptables 不要混搭:UFW 自带一整套规则链,再手工追加 iptables 规则,经常排不到执行位置,或被 UFW 重载时冲掉。选一个作为唯一入口:图省事选 UFW;想要更细的规则组织用 nftables(写 /etc/nftables.conf,默认 drop、放行 established 与回环、逐项放行端口)。怎么选、怎么写,见 Linux 防火墙端口开放完全指南 和 iptables 防火墙指南。云厂商的安全组是独立的第二道门,两边都收紧才算数。
2.4 内核网络参数:关掉几种老式的网络欺骗手法
这组 sysctl 参数处理网络层:让内核拒绝来源可疑、路径不合常理的数据包。防不住主流入侵,但在复杂路由环境里能堵住借道与劫持的口子,成本几乎为零。
sudo nano /etc/sysctl.d/99-security.conf# SYN 洪水时用 syncookies 维持服务,并限制半开连接占用
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
# 反向路径校验:来源与路由对不上的包直接丢(防 IP 伪造)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 不接受 ICMP 重定向与源路由(防中间人与路径劫持)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# 不回应广播 ping(防 Smurf 放大),记录路径异常的包
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1sudo sysctl -p /etc/sysctl.d/99-security.conf
sudo sysctl net.ipv4.tcp_syncookies2.5 关于 DDoS:主机层面能做的其实有限
先把预期放正:大流量 DDoS 靠调主机参数扛不住——管道先于主机被打满,防御必须在上游。SYN cookies、连接限速只能缓解小规模骚扰;Web 站点最实际的做法是接入 Cloudflare 这类 CDN(代理 + Full (Strict) SSL + Bot Fight Mode),让流量先在边缘清洗。还要澄清一个误解:BBR 是提高外发吞吐的拥塞控制算法,和抗 DDoS 无关。不想暴露源站的服务走 Cloudflare Tunnel 内网穿透;出口线路见 Cloudflare WARP 中转。
三、系统与服务:让补丁自己跟上,让闲着的服务下岗
攻击面的第三层是系统本身。漏洞每天都在披露,靠人记着打补丁必然有空窗;装好就没人管的闲置服务,是扫描器最喜欢的突破口。
3.1 自动安全更新:开,但重启要留给人决定
Debian / Ubuntu 的 unattended-upgrades 可以只自动安装安全源的更新,稳定性风险很低,应该默认开启:
sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
sudo nano /etc/apt/apt.conf.d/20auto-upgradesAPT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";在 50unattended-upgrades 里确认允许来源只含发行版与 security 源,然后特别注意这一行:
# 保持 false:更新装好后由你挑时间窗口手动重启
Unattended-Upgrade::Automatic-Reboot "false";不少教程默认把自动重启打开并定在凌晨。个人小站问题不大,但对跑数据库、队列、长连接服务的机器,半夜一次没人盯着的重启可能直接演变成事故。合理做法是:更新自动装,重启手动挑窗口;数据库、Docker 引擎这类影响面大的包放进 Unattended-Upgrade::Package-Blacklist 排除,走「更新前快照、手动升级、验证后保留」的流程。
# 改完配置先空跑验证一遍
sudo unattended-upgrade --dry-run --verbose3.2 关停无用服务,系统账户别乱动
延续 2.1 的盘点结果:确认没在用的服务,直接停掉并禁止自启,少一个监听就少一份风险:
# 列出正在运行的服务,逐个确认用途
systemctl list-units --type=service --state=running
# 停用某个确认无用的服务
sudo systemctl disable --now 服务名用户账户方面要纠正一个流传很广的做法:批量把系统账户改成 nologin、锁定 nobody 之类。现代发行版里它们本来就没有可登录的 shell,逐个「加固」收益为零,还可能误伤依赖系统账户的服务。真正要查的是有没有多出来的、带真实 shell 的陌生账户:
# 只列出能交互登录的账户,名单应该全是你认识的
grep -E "(/bin/bash|/bin/sh)$" /etc/passwd3.3 关键文件权限:一条命令的事
敏感文件的权限发行版默认基本正确,改动过或接手二手系统时检查一遍:
sudo chmod 644 /etc/passwd
sudo chmod 640 /etc/shadow
sudo chmod 644 /etc/group
sudo chmod 640 /etc/gshadow
sudo chmod 440 /etc/sudoers
# 自己家的 SSH 目录(对每个用户执行)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys3.4 内核自身参数:减少信息泄露与提权面
再补一组与内核自身相关的参数,写进同一个 sysctl 配置文件后重新 sysctl -p 生效:
# 地址随机化开满,限制内核日志与内核指针的可见范围
kernel.randomize_va_space = 2
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.perf_event_paranoid = 3
# 普通用户不能用 eBPF,suid 程序不产生核心转储
kernel.unprivileged_bpf_disabled = 1
fs.suid_dumpable = 0
# 链接与 FIFO 保护,堵 TOCTOU 类攻击
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2一条例外:跑 WireGuard 或 3X-UI 这类转发服务的机器,net.ipv4.ip_forward 必须保持为 1,那是功能需求,不算敞口。配置见 WireGuard 自建 VPN 完整教程 与 3X-UI 面板自建代理教程。
3.5 Docker:跑容器的话,把默认的高权限收回来
Docker 守护进程本身就是 root 级别的存在,容器以特权模式跑起来后逃逸回宿主机并不困难。加固抓三个重点:守护进程别裸奔、容器别用特权、资源设上限。
sudo nano /etc/docker/daemon.json{
"icc": false,
"no-new-privileges": true,
"userland-proxy": false,
"live-restore": true,
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}# 容器侧:丢弃全部能力、只读根文件系统、非 root 用户、限资源
docker run -d \
--read-only \
--tmpfs /tmp \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges \
--user 1000:1000 \
--memory="512m" \
--cpus="1.0" \
--pids-limit=100 \
-p 80:80 \
nginx:alpinedaemon.json 里的 userns-remap(把容器内 root 映射为宿主机普通用户)隔离效果好,但会改变卷的属主语义,不少现成镜像会直接跑不起来——先用能力收敛打底,确认兼容后再单独评估,不要不加验证就写进生产配置。需要写入的目录单独挂 volume 或 tmpfs 即可。
四、监控与发现:假设已经有人进来了
前三章解决「别让人进来」,这一章换个前提:假设防护已被绕过,你能不能在几天内、而不是几个月后看出来。小团队用不着重型安全平台,下面四件事做齐就够。
4.1 登录告警:每一次成功登录都通知你
SSH 登录是低频事件,一天也没几次,可以做到每次登录都推送通知——异常 IP 或异常时段的登录会非常扎眼。写一个 sshrc 脚本即可:
sudo nano /etc/ssh/sshrc#!/bin/bash
IP=$(echo "$SSH_CONNECTION" | awk '{print $1}')
TIME=$(date "+%Y-%m-%d %H:%M:%S")
curl -s "https://api.telegram.org/bot<TOKEN>/sendMessage" \
-d chat_id="<CHAT_ID>" \
-d text="SSH 登录:$(whoami) 来自 $IP 于 $TIME 登录 $(hostname)"sudo chmod +x /etc/ssh/sshrc脚本里的 Bot Token 本身就是凭证,文件权限不要放开;没有 Telegram 的,用 mail 发到自己邮箱效果相同。再配合 Fail2ban 的封禁邮件、云厂商登录通知,多一个独立渠道,少一个单点失明。
4.2 每日摘要:让日志自己送上门
原始日志没人天天翻,logwatch 把昨天的登录、磁盘、服务状态汇总成一封邮件,扫一眼两分钟:
sudo apt install -y logwatch
sudo nano /etc/logwatch/conf/logwatch.confMailTo = admin@y-m.top
MailFrom = logwatch@y-m.top
Range = yesterday
Detail = High
Service = All它安装时已挂进 cron.daily,配好收件人后每天自动执行。再配合 last -20 看历史登录、journalctl -u ssh 看认证细节,周度花十分钟过一遍,是能长期坚持的节奏。
4.3 AIDE:给关键文件建一份指纹档案
AIDE 给系统关键文件算一份带哈希的基准档案,每次检查都与基准比对,篡改、替换、新增的可疑变化都会被列出来——后门和木马最怕这种「和昨天不一样」。
sudo apt install -y aide
# 首次初始化(全量扫描,耗时较长)
sudo aideinit
sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# 手动检查
sudo aide --check两个使用要点。第一,基准数据库务必再复制一份到服务器之外:入侵者拿到 root 后可以重建基准掩盖痕迹,本机这份只能防君子,异地那份才是证据。第二,每日检查挂到 root 的计划任务里直接跑,不要层层 sudo:
# /etc/cron.daily/aide-check(root 属主、可执行)
#!/bin/bash
REPORT=$(aide --check 2>&1)
echo "$REPORT" | grep -q "All entries match" || \
echo "$REPORT" | mail -s "[ALERT] AIDE 检测到文件变更" admin@y-m.top自己升级软件、改过配置后报告里出现大量变更是正常的,逐项确认是自己所为,再用 aide --update 刷新基准;确认不了来源的变更,按第五章的出事流程处理。
4.4 rkhunter:顺手扫一遍常见后门
rkhunter 针对已知 rootkit 与常见后门特征,装好跑一次基线、再挂每日扫描即可(rkhunter --update && rkhunter --propupd && rkhunter --check --sk)。它单独用处有限,价值在于和 AIDE、登录告警拼成互相印证的信号。深入调优见 Linux 服务器入侵检测与安全审计实战。
五、备份与恢复演练:最后一道防线要先演习过
加固的意义是让入侵变难、被发现得早,但没有配置能把概率降到零。真正决定事故损失的,是手上有没有一份干净、近期、且验证过能恢复的备份。
5.1 备什么、备到哪
备份对象分三类:业务数据(数据库、上传文件)、服务配置(/etc 下改过的部分、应用配置目录)、密钥与账户清单(单独加密保存)。存放遵循 3-2-1:三份、两种介质、一份异地——同账户的云快照不算异地,账户被盗时快照会一起没。rsync 增量同步到另一台机器或对象存储是成本最低的异地方案,见 VPS 数据备份与恢复完整指南 与 rsync 增量备份指南。
5.2 恢复演练:没恢复过的备份只是愿望
至少每季度做一次完整演练,步骤就四件:
- 新开一台最小规格实例,从异地备份拉取数据与配置。
- 按部署记录逐个拉起服务,逐项验证:站点能打开、数据库能读写、定时任务在跑。
- 记录耗时和缺的东西——缺的往往不是数据,而是某个没进备份的环境变量或授权文件。
- 把缺项补进备份清单和恢复文档,再销毁演练实例。
演练还顺带验证了恢复文档是否写得让「凌晨三点、半梦半醒的你」也能照着做完。
5.3 真出事了:先隔离留证,再默认重装
发现挖矿进程、陌生账户、异常外连这类明确信号时,顺序不要乱:
- 先隔离:在云厂商控制台改安全组只放行自己的 IP,或直接关机。不要用 SSH 登上去慢慢查——登录动作可能惊动对方,断网也要经控制台,别把自己锁在外面。
- 先快照留证,再排查:整机快照保存现场,再在隔离环境里翻日志、看进程。不要一上来就删文件、清计划任务——那既破坏证据,也证明不了你清干净了。
- 排查只为回答两个问题:对方从哪进来、在这里多久了。这组命令覆盖最常见的藏身处:
w && last -20 # 当前与历史登录
ps aux --sort=-%cpu | head -20 # 异常高占用进程
ss -tunp | grep ESTABLISHED # 异常外连
crontab -l; sudo crontab -l; ls /etc/cron.d/ # 计划任务
cat ~/.ssh/authorized_keys # 有没有陌生公钥
grep -E "(/bin/bash|/bin/sh)$" /etc/passwd # 有没有陌生账户
find /tmp /var/tmp /dev/shm -type f -executable # 临时目录可执行文件
find / -perm -4000 -type f 2>/dev/null # 新增的 SUID 文件- 默认重装,不要试图「清理后继续用」:确认入侵成立后,可靠的收尾只有一条——保留业务数据(逐项检查后再迁)、整机重装、按本文顺序重新加固、所有密码与密钥全部轮换。被 root 过的系统,没法证明后门已清完。
上线前自查清单
新机器交付或大版本变更后,逐项打勾再对外提供服务:
账户与登录
网络暴露面
系统与服务
监控与备份
FAQ
加固后 SSH 连不上了怎么办? 从云厂商的 VNC / 网页控制台登进去排查:grep "^Port" /etc/ssh/sshd_config 确认端口、systemctl status ssh 看服务、ufw status 看放行、fail2ban-client status sshd 看是不是误封了自己。有改前的 sshd_config.bak 就还原重启——这也是每改一步先备份的原因。
Fail2ban 把我自己封了怎么办? 控制台登录后 fail2ban-client set sshd unbanip 你的IP 解封,再把管理 IP 写进 jail.local 的 ignoreip。IP 常变的,适当放宽 maxretry 即可。
UFW、nftables、iptables 到底用哪个? 三选一,不要叠加。新手选 UFW,规则想写得更结构化选 nftables,裸 iptables 只建议维护老系统时用。标准不是哪个更强,而是改规则时能不能一眼看清生效的是什么。
结尾与延伸阅读
五章做完,你的 VPS 已经越过了「自动化扫描眼里的软柿子」这条线。安全配置不是一次性工程:新开服务多问一句该不该露在公网,新上组件确认补丁跟不跟得上,告警通道每季度测一次。选购阶段先看 VPS 供应商横向对比,Web 层细节见 Nginx 反向代理与 SSL 配置,其余深入配置都在正文对应位置。
延伸阅读
免责声明
本文仅供技术交流和学习参考。涉及第三方服务的链接可能包含 sponsored 标记,请自行核实服务条款、价格和可用性,并遵守当地法律法规。