🛡️ 服务器加固
SSH key-only、UFW 防火墙、Fail2ban、自动安全更新——VPS 上线后的第一件事
🔑 SSH 加固
SSH 是你服务器的正门。默认配置是开着的门,任何人都可以尝试密码登录。加固 SSH 是服务器安全的第一步。
# 1. 生成 SSH 密钥(Ed25519,比 RSA 更安全更快)
ssh-keygen -t ed25519 -C "your-email@example.com"
# 2. 上传公钥到服务器
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@your-server
# 3. 修改 SSH 配置
# /etc/ssh/sshd_config
# 禁用 root 密码登录
PermitRootLogin prohibit-password
# 禁用密码登录(只允许密钥)
PasswordAuthentication no
PubkeyAuthentication yes
# 禁用空密码
PermitEmptyPasswords no
# 限制尝试次数
MaxAuthTries 3
# 登录超时
LoginGraceTime 30
# 限制用户(只允许特定用户登录)
AllowUsers deploy root
# 禁用不需要的认证方式
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no
# 使用强加密算法
KexAlgorithms curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# 日志
LogLevel VERBOSE
# 应用配置
systemctl restart sshd
# ⚠️ 重要:不要关闭当前 SSH 会话!
# 开一个新窗口测试能否用密钥登录
# 如果登不上,还有旧会话可以修复
SSH 跳板机配置
# ~/.ssh/config — 本地配置
# 通过跳板机连接生产服务器
Host bastion
HostName bastion.example.com
User deploy
IdentityFile ~/.ssh/id_ed25519
Host prod-*
ProxyJump bastion
User deploy
IdentityFile ~/.ssh/id_ed25519
Host prod-app
HostName 10.0.0.1
Host prod-db
HostName 10.0.0.2
# 一条命令直连
ssh prod-app # 自动通过 bastion 跳转
# SSH Agent Forwarding(不要开!安全风险)
# 如果需要在生产服务器上 git pull,用 Deploy Key 代替
🔥 UFW 防火墙
# UFW = Uncomplicated Firewall
# 默认策略:拒绝所有入站,允许所有出站
# 1. 基本规则
ufw default deny incoming
ufw default allow outgoing
# 2. 允许 SSH(⚠️ 先加这条,否则会锁死自己!)
ufw allow 22/tcp
# 如果改了 SSH 端口:
ufw allow 2222/tcp
# 3. 允许 HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# 4. 限制来源 IP(推荐)
# 只允许你的 IP 访问 SSH
ufw allow from YOUR_IP to any port 22
# 5. 允许特定端口(如 Docker 直接暴露)
ufw allow from 10.0.0.0/8 to any port 5432 # 内网 PostgreSQL
# 6. 启用
ufw enable
# 7. 查看规则
ufw status verbose
# 常用操作
ufw delete allow 80/tcp # 删除规则
ufw insert 1 allow 22/tcp # 在第一条插入
ufw reload # 重载配置
# Docker + UFW 的坑
# Docker 默认会绕过 UFW 直接修改 iptables
# 解决:在 /etc/docker/daemon.json 中禁用 iptables 操作
{
"iptables": false
}
# 然后用 Traefik/Nginx 作为唯一入口
🚫 Fail2ban
# Fail2ban = 自动封禁暴力破解 IP
# 安装
apt install fail2ban
# 配置
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600 # 封禁 1 小时
findtime = 600 # 10 分钟内
maxretry = 3 # 3 次失败即封禁
destemail = you@example.com
action = %(action_mwl)s # 封禁 + 发邮件
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
[nginx-http-auth]
enabled = true
port = 80,443
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
[nginx-badbots]
enabled = true
port = 80,443
filter = nginx-badbots
logpath = /var/log/nginx/access.log
# 自定义规则:保护 API
[api-rate-limit]
enabled = true
port = 80,443
filter = api-rate-limit
logpath = /var/log/nginx/access.log
maxretry = 100
findtime = 60
bantime = 600
# /etc/fail2ban/filter.d/api-rate-limit.conf
[Definition]
failregex = ^ .* "(GET|POST) /api/ .*" (429|503) .*$
ignoreregex =
# 常用命令
fail2ban-client status # 查看所有 jail
fail2ban-client status sshd # 查看 SSH jail 状态
fail2ban-client set sshd unbanip 1.2.3.4 # 解封 IP
fail2ban-client reload # 重载配置
🔄 自动安全更新
# Ubuntu 自动安全更新
apt install unattended-upgrades apt-listchanges
# /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESMInfrastructure:${distro_codename}-infrastructure-security";
};
# 自动清理旧内核
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false"; # 安全更新一般不需要重启
# 如果需要自动重启(内核更新后)
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
# /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
# 测试(dry-run)
unattended-upgrade --dry-run --debug
📋 最小权限原则
# 最小权限 = 只给必要的权限,不多给
# 1. 创建专用用户(不要用 root 跑应用)
useradd -m -s /bin/bash appuser
# 2. 限制 sudo 权限
# /etc/sudoers.d/deploy
deploy ALL=(ALL) /usr/bin/docker, /usr/bin/systemctl restart nginx
# 只允许 docker 和 nginx restart
# 3. 文件权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 .env # 环境变量文件
chmod 600 *.pem # SSL 密钥
# 4. Docker 安全
# 不要把 Docker socket 暴露给非特权用户
# 使用 Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# 5. 数据库权限
# 应用用只读用户连接只读库
# 管理操作用管理员用户
GRANT SELECT, INSERT, UPDATE ON app_tables TO app_user;
-- 不要给 app_user DELETE/DROP 权限
# 6. API Key 轮换
# 每 90 天轮换一次
# 使用 Vault 或 AWS Secrets Manager 存储
# 代码中不要硬编码密钥
✅ 安全检查清单
| 检查项 | 状态 | 命令 |
| SSH 禁用密码登录 | ☐ | grep PasswordAuthentication /etc/ssh/sshd_config |
| SSH 只允许密钥 | ☐ | grep PubkeyAuthentication /etc/ssh/sshd_config |
| UFW 启用 | ☐ | ufw status |
| Fail2ban 运行 | ☐ | systemctl status fail2ban |
| 自动安全更新 | ☐ | systemctl status unattended-upgrades |
| 非 root 用户运行应用 | ☐ | ps aux | grep node |
| 没有不必要的开放端口 | ☐ | ss -tlnp |
| 环境变量文件权限 600 | ☐ | ls -la .env |
| Docker socket 不暴露 | ☐ | find / -name "*.yml" -exec grep docker.sock {} \; |
| 定期备份运行 | ☐ | ls -la /backup/ |
💡 上线前 30 分钟安全清单
每次新 VPS 上线前做这些:① 创建 deploy 用户 + SSH 密钥 ② 禁用 root 密码登录 ③ 配置 UFW(只开 22/80/443)④ 安装 Fail2ban ⑤ 启用自动安全更新 ⑥ 安装 Docker ⑦ 配置日志轮转 ⑧ 设置备份 Cron。整个过程可以用 Ansible Playbook 自动化——一次配置,永远安全。
🔗 相关专题
⚙️ Ansible · 🔒 SSL & 域名 · 💾 备份恢复 · 🏗️ 返回首页