去年有一天,我的一台测试服务器被当成了挖矿机。
CPU 跑满、进程藏得很深、SSH 日志里有一堆来自越南和巴西的登录尝试。我当时意识到:服务器买来第一件事不是跑服务,是先把门锁好。
跑了两年 Homelab,现在有 12 个 Docker 服务。我把每次新服务器到手必做的安全配置整理成本文。
1. SSH 加固:密码登录是最大的漏洞
SSH 是服务器的入口。只要这一个入口没锁好,其他安全措施全是白搭。
先检查当前 SSH 配置:
sudo nano /etc/ssh/sshd_config必改项:
# 禁止 root 登录
PermitRootLogin no
# 只允许密钥登录(关闭密码验证)
PasswordAuthentication no
PubkeyAuthentication yes
# 改默认端口(22 → 一个高位端口,比如 22022)
Port 22022
# 禁止空密码
PermitEmptyPasswords no
# 限制登录失败的尝次
MaxAuthTries 3改完之后重启 SSH 服务,但不要关掉当前这个终端窗口。新开一个窗口测试,确认能登录再断开旧连接。
生成密钥对:
ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip -p 22022密钥登录验证成功后,立刻把密码登录关掉。
这一套做下来,暴力破解 SSH 的成功率基本为零。不是因为密码强度不够,是因为根本没密码可破。
2. UFW 防火墙:只开必要的端口
Ubuntu 默认带了 UFW(Uncomplicated Firewall),比 iptables 好用十倍。
先看当前状态:
sudo ufw status verbose基础规则配置:
# 先设置默认策略:拒绝所有入站,放行所有出站
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 放行 SSH(改过端口的用新端口号)
sudo ufw allow 22022/tcp
# 放行 Web 服务端口(如果有)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 如果用 Docker,Docker 会自动操作 iptables,通常不需要额外配置
# 但如果发现容器无法访问外网,检查 UFW 是否阻止了 Docker 的流量转发
sudo ufw allow in on docker0# 启用防火墙
sudo ufw enable
# 确认规则
sudo ufw status numbered有个常见坑:Docker 容器暴露端口时,如果服务器有 UFW,那些端口会绕过防火墙直接暴露到公网。需要加一条规则限制 Docker 的流量:
# 禁止 Docker 容器直接暴露到公网(只允许通过反向代理访问)
# 在 /etc/docker/daemon.json 配置
{
"iptables": true
}或者更简单:Docker 服务只监听本地 localhost,不绑定 0.0.0.0。
3. fail2ban:自动封禁暴力破解者
SSH 加固了还不够。fail2ban 会自动追踪多次登录失败的 IP,把它们封掉。
安装:
sudo apt install fail2ban配置文件复制一份:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local修改 jail.local 里的 SSH 配置段:
[sshd]
enabled = true
port = 22022
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600 # 封禁 1 小时
findtime = 600 # 10 分钟内失败 5 次就封重启生效:
sudo systemctl restart fail2ban查看封禁记录:
sudo fail2ban-client status sshd跑几天来看一眼日志,会发现被封的 IP 一堆。那些都是全网扫描的机器人,早该被封。
4. 定期备份:灾难恢复的最后防线
服务器被黑、数据损坏、磁盘故障,任何一种情况都可能发生。备份是唯一保险。
我用的是这套组合:
方案 A:Borg Backup(增量备份,适合整盘)
# 安装
sudo apt install borg
# 初始化备份仓库
borg init --encryption=repokey /path/to/backup.repo
# 备份整台服务器(排除不需要的目录)
borg create /path/to/backup.repo::'{hostname}-{now:%Y-%m-%d}' \
/ \
--exclude 'exclude=/proc' \
--exclude 'exclude=/sys' \
--exclude 'exclude=/dev' \
--exclude 'exclude=/run' \
--exclude 'exclude=/tmp' \
--exclude 'exclude=/var/cache' \
--exclude 'exclude=/swap.img'# 定时任务:每天凌晨 3 点备份
# crontab -e
0 3 * * * borg create /path/to/backup.repo::'{hostname}-{now:%Y-%m-%d}' / --exclude-caches ...方案 B:Docker 数据备份(更实用)
跑 Docker 服务的话,备份数据卷比备份整盘更有针对性:
#!/bin/bash
# backup-docker-volumes.sh
BACKUP_DIR="/path/to/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# 备份所有 named volumes
for vol in $(docker volume ls -q); do
docker run --rm \
-v ${vol}:/data \
-v $BACKUP_DIR:/backup \
alpine \
tar czf /backup/${vol}_${DATE}.tar.gz -C /data .
done
# 清理 30 天以上的备份
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete这套脚本每晚跑一次,备份到外接硬盘 + 另一台 NAS。
备份验证:备份做完了不验证等于没做。每月做一次恢复测试:
# 从备份恢复一个 volume
docker run --rm \
-v my_volume:/data \
-v $BACKUP_DIR:/backup \
alpine \
sh -c "cd /data && tar xzf /backup/my_volume_20260904.tar.gz"5. 一个更实用的清单:新服务器到手十分钟搞定
把上面这套做成脚本,第一次部署直接跑:
#!/bin/bash
# setup-secure-server.sh
set -e
NEW_SSH_PORT=22022
NEW_USER=youruser
# 1. 创建非 root 用户
adduser $NEW_USER
usermod -aG sudo $NEW_USER
# 2. 安装基础工具
apt update && apt install -y ufw fail2ban curl wget
# 3. SSH 加固(修改端口、禁用密码)
sed -i "s/^Port 22$/Port $NEW_SSH_PORT/" /etc/ssh/sshd_config
sed -i "s/^#*PasswordAuthentication yes$/PasswordAuthentication no/" /etc/ssh/sshd_config
sed -i "s/^#*PermitRootLogin yes$/PermitRootLogin no/" /etc/ssh/sshd_config
systemctl restart sshd
# 4. 配置 UFW
ufw default deny incoming
ufw default allow outgoing
ufw allow $NEW_SSH_PORT/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
# 5. 安装 Docker
curl -fsSL https://get.docker.com | sh
usermod -aG docker $NEW_USER
echo "Done. SSH port: $NEW_SSH_PORT"跑完这个脚本,花十分钟手动复制 SSH 公钥,再花五分钟验证登录是否正常。基础安全配置就全做完了。
做完这些,剩下的交给服务本身
门锁好了,监控装了,备份跑了。服务器层面的安全做到这里,剩下的靠每个服务的正确配置:容器不跑 root、不暴露非必要端口、定期更新镜像。
被黑的服务器,九成是上面某一步没做到位。密码登录 + 默认端口 + 没装 fail2ban,这三条凑齐基本等于公开邀请。
这四件事做一次不到半小时,之后每年维护时间不超过二十分钟。但它省掉的是半夜三点被告警叫醒的那通电话。