为什么要做这件事

云厂商交付的新服务器,默认配置是”为开机即用,不为安全”

  • root 可以远程登录
  • 密码登录开启
  • SSH 监听标准端口 22
  • 没有任何登录失败防护
  • 安全补丁需要手动打

只要公网 IP 暴露几小时,就会被全球扫描器命中。常见后果:

  • 🔴 几千次/天的自动化爆破尝试
  • 🔴 弱密码服务器几天内被攻陷
  • 🔴 被攻陷后:当矿池节点、DDoS 跳板、被勒索

🟢 好消息:做对几件事就能挡住 99% 的攻击。这份手册是给”想用好一台 VPS、但不想成为安全专家”的人。

适用人群

  • 适用:个人 VPS、学习用机、单用户开发服务器
  • 不适用:生产环境多用户系统、有合规要求的企业部署(那需要更专业的方案)

三阶段心智模型

把所有安全措施按目的 + 时间维度分成三层:

阶段防什么时间维度类比
**P0**默认弱配置现在把门关上
**P1**直接的网络/协议攻击现在换防盗门 + 装猫眼
**P2****长期运行衰减**未来装监控 + 自动报警

🔑 关键认知

  • P0 / P1preventive(防止进入)
  • P2resilient(长期韧性)

跳过 P2 → 短期没事,但未来某天有新漏洞、配置漂移时你没保险。 跳过 P0 / P1 → 现在就已经在裸奔。

三阶段分别做什么

阶段工具防的是
**P0**收紧安全组 / 系统更新 / 创建用户出厂默认值
**P1**密钥登录 / 改端口 / 禁 root + 禁密码直接的协议层攻击
**P2**自动安全更新 / fail2ban / ufw(可选)未来漏洞、配置漂移、应用泄露

核心原则(4 条铁律)


P0 · 基础准备

P0.1 立刻在云厂商控制台收紧安全组

🛠️ 控制台路径:实例 → 安全组 → 入方向规则 默认通常会开三个端口给整个互联网:

  • 22 (SSH) 来源 0.0.0.0/0
  • 3389 (RDP) 来源 0.0.0.0/0
  • ICMP 来源 0.0.0.0/0

立刻做:

  • 22 的来源改为你家庭公网 IP(查询:curl ifconfig.me 或访问 ipinfo.io
  • 删除 3389(你不是 Windows 不需要)
  • ICMP 可保留方便 ping 排障

验证:用手机 4G 流量试连 SSH → 应该连不上(说明白名单生效)

P0.2 升级系统打补丁

用云厂商提供的初始凭证(通常是 root + 密码)登入:

sudo apt update && sudo apt upgrade -y

❗ 这步必须做。新机器经常带着已知漏洞。

P0.3 创建普通用户

直接用 root 是大忌。创建一个普通用户:

adduser dev              # 用你喜欢的名字,避开 admin/user/test
usermod -aG sudo dev     # 加 sudo 组

💡 命名提醒:用一个不在常见用户名字典里的名字(不要叫 admin、user、root2 这种)。攻击者扫描时会优先尝试这些常见名字。

✅ **验证**:`su - dev` 能进 → `whoami` 显示 `dev` → `sudo -v` 能升权

P1 · 核心加固

这是最关键的阶段。做完 P1 你就拥有了四层防御的前三层。

P1.1 配置 SSH 密钥登录

在客户端生成密钥

类型选 ED25519(不要用 RSA 2048,已不推荐)。

  • macOS / Linux: ssh-keygen -t ed25519 -C "your_comment"
  • Windows: Termius / PuTTY / VS Code 图形界面生成
  • 移动端: Termius

🔑 强烈推荐设 passphrase——即使密钥被偷也能多挡一层。

把公钥贴到服务器

在服务器上切到普通用户:

su - dev
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys      # 粘贴一行公钥
chmod 600 ~/.ssh/authorized_keys

用新会话测试

不要关 root 会话。新建/复制一个客户端 Host:

  • 用户名: dev
  • 密码: 留空
  • 关联生成的密钥

✅ 能登入 → whoami 显示 devsudo -v 工作 → 才能进 P1.2

P1.2 改 SSH 端口避开扫描

把 SSH 从标准 22 改到一个不显眼的端口。

选端口

  • 范围:10000–65535
  • 验证没被占用:sudo ss -tlnp | grep <端口> → 空 = 可用
  • 本文示例用 60022(请你自己选一个不同的,避免大家都用一样的)

控制台先放行新端口

🛠️ 安全组新增入方向规则:

  • 端口:60022
  • 来源:和 22 那条一样(家庭 IP 白名单 或 0.0.0.0/0
  • 描述:SSH-new-port

保留旧 22 规则不要删——先建后拆。

P1.3 加固 sshd 配置

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano /etc/ssh/sshd_config

确认以下四行存在且没有 #

Port 60022
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

每条的含义:

配置含义
`Port 60022`监听新端口
`PermitRootLogin no`禁止 root 直接 SSH 登录
`PasswordAuthentication no`禁止密码登录(**必须先确认密钥能用**!)
`PubkeyAuthentication yes`允许密钥登录
验证语法正确: ``` sudo sshd -t # 没输出 = OK,有错就显示哪行 ```

P1.4 处理 Ubuntu socket activation 坑(24.04+ 必看)

怎么判断你踩到了

sudo systemctl status ssh.socket

如果 Active: active → 中招了。ssh.socket 写死了监听 22,会忽略 sshd_configPort

解决:切换到传统服务模式

sudo systemctl disable --now ssh.socket
sudo systemctl enable --now ssh.service

处理可能的遗留 sshd 进程

sudo ss -tlnp | grep ssh

如果还看到 22 端口在监听(PID 是个旧的):

sudo kill <旧PID>

最终状态ss -tlnp 只显示新端口,22 完全消失。

客户端测试新端口

复制刚才的 dev Host → 改端口为 60022 → 新开连接。 能连 = 完成

P1.5 收尾清理

确认新端口能稳定登录后:

  • 🛠️ 控制台删除旧的 22 安全组规则
  • 客户端删除旧的 root + 密码 + 22 Host 配置
  • sshd_config.bak 备份保留 24 小时,确认无异常后再删

📊 P1 完成后的”四层防御”

控制状态
**网络层**防火墙 / 安全组✔ 仅放行新端口
**传输层**端口选择✔ 避开扫描器热点
**协议层**sshd 配置✔ 禁 root、禁密码、强制密钥
**身份层**密钥✔ ED25519 + passphrase
任一层被突破,下面还有三层 → **defense in depth**。

P2 · 长期韧性

P1 完成后你已经”现在安全”了。P2 是让你长期保持安全

P2.1 自动安全更新(unattended-upgrades)

很多 Ubuntu 系统默认已经装,但要验证配置正确:

sudo systemctl status unattended-upgrades
sudo cat /etc/apt/apt.conf.d/20auto-upgrades

期望看到:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

如果是 "0" 或文件不存在:

sudo dpkg-reconfigure -plow unattended-upgrades
# 弹出对话框选 Yes

看日志确认在工作:

sudo cat /var/log/unattended-upgrades/unattended-upgrades.log | tail -20

P2.2 自动封禁爆破 IP(fail2ban)

它做什么

监控 SSH 失败登录,达到阈值就自动用防火墙规则封禁来源 IP

安装

sudo apt install fail2ban -y
sudo nano /etc/fail2ban/jail.local

粘贴:

[DEFAULT]
bantime  = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
port    = 60022
backend = systemd

💡 含义:「10 分钟内失败 5 次 → 封禁 1 小时」

启动 + 验证

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

⚠️ 动态 IP 用户:放宽参数

如果你在多地登录、IP 经常变化,不要用 ignoreip 白名单——改用更宽松的参数避免误封自己:

[sshd]
enabled  = true
port     = 60022
backend  = systemd
maxretry = 10
findtime = 300
bantime  = 600

万一锁了自己怎么办

云服务器的优势:有控制台救援通道。 🛠️ 阿里云:实例 → 远程连接 → Workbench / VNC —— 这条通道绕过 SSH 独立工作,fail2ban 封不到。 进去后:

sudo fail2ban-client unban --all

🟢 30 秒救援完成。

P2.3 OS 防火墙 ufw(可选)

是否需要

你已经有云厂商安全组这一层防火墙了。ufw 是 OS 侧的第二层

你的情况建议
学习目的、想完整体验🟢 做
极简主义、够用就好🟡 跳过
担心安全组被误删🟢 做
计划本机跑很多服务🟢 做

安装步骤(严格按顺序,否则锁外面)

# Step 1: 先放行新端口!
sudo ufw allow 60022/tcp comment 'SSH'

# Step 2: 检查
sudo ufw show added

# Step 3: 设置默认策略
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Step 4: 启用(会提示风险)
sudo ufw enable

# Step 5: 验证
sudo ufw status verbose

❗ 启用前必须先放行新端口,否则会断开自己。


🐞 踩坑合集

按”现象 → 真因 → 解决”排列:

现象真因解决
改了 `Port` 但 sshd 还是听 22Ubuntu socket activation`disable ssh.socket` · `enable ssh.service`
用密钥登不上,`Permission denied (publickey)``.ssh` 或 `authorized_keys` 权限错`chmod 700 .ssh && chmod 600 authorized_keys && chown -R user:user ~/.ssh`
改完 sshd 重启就连不上端口没在云安全组放行控制台**先放行再**重启 sshd
`adduser` 卡死无响应被 `Ctrl+Z` 挂起了`jobs` → `kill %1` → `passwd 重设`
客户端显示能连 22,服务器查不到监听客户端 UI 缓存信服务器端 `ss -tlnp` 的输出
ping 首包 70ms 后续 30msARP / 路由首次建立正常现象,不是问题
改完 IP 白名单后被锁外面家庭 IP 动态变了控制台改回 `0.0.0.0/0`,靠端口+密钥防御
装完 fail2ban 误封自己maxretry 太严放宽参数 或 Workbench 救援后 unban
kill 旧 sshd 后又有遗留进程ssh.socket 还在 enable 状态`disable --now ssh.socket` 再 kill

💡 排雷思路(记住一辈子用得上)

凡是”客户端说能 / 不能,但服务器端看到的不一样”——信服务器端。 机制信仰排序:

kernel 状态 > 系统命令输出 > 服务端日志 > 客户端日志 > 客户端 UI 显示

ss -tlnp 读的是内核 socket 表,是事实。客户端 UI 缓存什么的都是表象。


✅ 加固完成验收清单

服务器上跑:

# 监听端口
sudo ss -tlnp | grep ssh
# → 只看到新端口,没 22

# 服务状态
sudo systemctl is-active ssh.service unattended-upgrades fail2ban
# → 都是 active(ufw 可选)

# socket activation 状态
sudo systemctl is-enabled ssh.socket
# → disabled

# sshd 关键配置
sudo grep -E "^(Port|PermitRootLogin|PasswordAuthentication|PubkeyAuthentication)" /etc/ssh/sshd_config
# → 应该看到全部四行正确

云厂商控制台核对:

  • 仅保留新端口入方向规则
  • 旧的 22 / 3389 / 其他默认端口已删
  • ICMP 视需保留

客户端验证:

  • 用密码登(应失败:Permission denied
  • 用 root 登(应失败:Permission denied
  • 用 dev + 密钥 + 新端口(应成功)

🧠 进阶心法

安全是分层的,不是单点的

不要追求”一个完美的防御”。追求多层 OK 的防御。任一层被突破,剩下的还能挡。

密钥被偷?还有 passphrase
passphrase 被试出来?还要登对端口
端口被找到?还有 fail2ban
fail2ban 失效?还有禁 root + 禁密码
全失败?还有云控制台 Workbench 救援

够用就好,别追求”最优解”

完美主义最大的成本不是钱,是注意力

状态收益
0 → 60% 加固**巨大**(挡住 95% 攻击)
60% → 90% 加固中等(挡住额外 4% 攻击)
90% → 100% 加固微小(最后 1%,可能耗你 10 倍时间)
**做到 90% 就停**。剩下 10% 用时间和经验慢慢补。

公网 IP 不是秘密

公网 IP 的设计就是让全世界都能找到。别人知道你的 IP 不等于能进来。Shodan、Censys 这类引擎每天扫一遍全球 IPv4,你的 IP 早就在公开数据库里了。 真正的保护不是藏 IP,是让”IP 被找到”这件事变成无害的事——这正是本指南做的事。

动态 IP 用户特别提醒

如果你经常换地点登录(咖啡馆、4G、出差),不要用 IP 白名单。改用:

  • 安全组放 0.0.0.0/0(依赖端口+密钥防御)
  • fail2ban 用宽松参数避免误封
  • 知道 Workbench 救援路径

装服务时的黄金原则

任何监听网络的服务,第一选择是 127.0.0.1,不是 0.0.0.0

# ❌ 不安全:监听所有网卡,公网可达
redis-server --bind 0.0.0.0

# ✔ 安全:只监听本机,外部进不来
redis-server --bind 127.0.0.1

这一条比装防火墙更根本——从源头消除暴露,而不是事后补救。


🎯 总结

新服务器加固 = 用最小特权身份 + 非标端口 + 仅密钥 + 网络层先收紧,过程中永远留一条回退通道。 完成后你拥有:

  • ✅ 4 层防御(网络 / 传输 / 协议 / 身份)
  • ✅ 长期韧性(自动补丁 + 行为兜底)
  • ✅ 救援通道(云厂商 Workbench)

剩下的事就是用得开心