上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器上构建多层防火墙与Fail2ban策略,抵御系统层面的暴力入侵?

发布人:Minchunlin 发布时间:2025-08-17 09:56 阅读量:671


凌晨一点半,我正蹲在深水埗机房的冷风里,前排几台 1U 机器的风扇声和空调混在一起,嗡嗡直响。眼前这台新上架的香港服务器刚刚接好线路,机柜里的指示灯一闪一闪,像在催促我快点把安全策略落地。业务这两天要对外开放公网接口,意味着从上线那一刻起,它就会暴露在全球的扫描器和暴力破解工具面前。

说实话,我最怕的不是黑客,而是自己在凌晨的操作失误:一条命令下去,把 SSH 自己也封了,就只能跑到前面板插显示器救命。于是这次我决定做得彻底一点——不仅仅是装个 Fail2ban,而是搭建一个分层防御体系:机房 ACL、firewalld 基线、ipset 动态黑洞、Fail2ban 实时封禁,再加应用侧的 sshd 和内核参数收紧。这样既能挡住那些无聊的暴力爆破,也能在我需要回滚的时候有条不紊。

我这台“香港服”的基础画像

项目 规格/版本 说明
机型 Dell R630 1U 双电冗余,前面板 8 盘位
CPU 2 × Intel Xeon E5-2680v4(14C) 实测空闲频 2.3GHz
内存 128 GB DDR4 ——
系统盘 2 × 480GB SSD(RAID1) /、/var、/var/log 分开
网卡 2 × 10GbE SFP+(bond0)、1 × 1GbE(管理) 管理网与业务网物理隔离
公网 IP IPv4 /29(可用 5 个),IPv6 /64 典型香港机房配法
操作系统 CentOS 7.9(3.10 内核) EPEL 已启用,SELinux Enforcing
关键服务 sshd、nginx(443)、node-exporter、grafana(仅内网) DB 在内网,禁止外网直达

目标:

  • 以“多层防火墙 + Fail2ban”做系统层抗暴力破解(SSH、HTTP 基础认证等)。
  • IPv4/IPv6 同步防护、白名单优先、黑洞即刻生效、可观测、可回滚。
  • 生效顺序可控,不把自己锁在机房(别把 SSH 也给挡了)。

设计思路:我做的“多层”到底有几层?

  • 机房侧 ACL(Layer 0):让供应商先在上联交换机做源/目的/端口的粗拦截(减少垃圾流量进机)。
  • OS 自带防火墙(Layer 1):CentOS 7 用 firewalld 做“基线策略 + 区域(zone)隔离 + 富规则(rich rules)”;所有静态白/黑名单都在这层。
  • 动态黑洞(Layer 2):用 ipset 承接动态封禁名单(性能更好、条目多也不拖慢包匹配)。
  • Fail2ban(Layer 3):基于日志或 journald 识别暴力行为,直接写入 firewalld 的 ipset(而非裸 iptables),与 Layer 1 协同。
  • 应用侧最小暴露(Layer 4):sshd 仅密钥、禁 root、限制并发;nginx 的 401/403 重试配合 Fail2ban;再加系统 sysctl 一些内核层防护。

经验:把 Fail2ban 和 firewalld 融合(banaction 选 firewallcmd-ipset),重载 firewall 时不会丢动态封禁,维护成本也低。

变更前准备与回滚手段

  • 带外:确保 iDRAC/iLO 能远程 KVM;管理口(1GbE)单独绑到 internal zone。
  • 会话保护:tmux 两个窗口,分别操作管理网与业务网,错了好撤。
  • 二次入口:在管理网额外开一个临时 SSH 端口(只内网可达),直到确认新策略稳定再关。

配置备份:

mkdir -p /root/backup-fw-$(date +%F)
cp -a /etc/firewalld /root/backup-fw-$(date +%F)/
cp -a /etc/fail2ban /root/backup-fw-$(date +%F)/

上线窗口:凌晨 1:00–3:00,变更单写清回滚点(“恢复昨天晚上的 firewalld 与 fail2ban 备份”)。

基础环境与日志持久化

# 基础仓库
yum install -y epel-release
yum install -y firewalld fail2ban fail2ban-systemd ipset policycoreutils-python setools-console

# journald 持久化(Fail2ban 用 backend=systemd 更稳)
sed -i 's/^#\?Storage=.*/Storage=persistent/' /etc/systemd/journald.conf
systemctl restart systemd-journald

# 开机自启
systemctl enable firewalld --now
systemctl enable fail2ban

1. Firewalld:用“区域 + 富规则 + ipset”打出基线

1.1 区域与接口绑定

我把 10G 业务口(bond0)放 public,管理 1G 口(eno2)放 internal:

# 查看接口
nmcli dev status

# 绑定区域
firewall-cmd --permanent --zone=public --change-interface=bond0
firewall-cmd --permanent --zone=internal --change-interface=eno2

# internal 全放行(内网管控)
firewall-cmd --permanent --zone=internal --set-target=ACCEPT

1.2 公网允许的最小端口集合

# 仅放 22/tcp(暂时)、80/443(业务),禁 ping(见下 rich rule)
firewall-cmd --permanent --zone=public --add-service=ssh
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https

1.3 新建 ipset:白名单、黑洞(v4/v6)

# IPv4
firewall-cmd --permanent --new-ipset=whitelist_v4 --type=hash:ip --option=family=inet
firewall-cmd --permanent --new-ipset=blackhole_v4 --type=hash:ip --option=family=inet

# IPv6
firewall-cmd --permanent --new-ipset=whitelist_v6 --type=hash:ip --option=family=inet6
firewall-cmd --permanent --new-ipset=blackhole_v6 --type=hash:ip --option=family=inet6

# 先录入我的办公出口与跳板机
firewall-cmd --permanent --ipset=whitelist_v4 --add-entry=203.0.113.10
firewall-cmd --permanent --ipset=whitelist_v6 --add-entry=2001:db8:abcd::/48

1.4 富规则(rich rules):白名单优先、黑洞即刻、禁 ping

firewalld 支持富规则优先级(priority,数字越小优先级越高)。CentOS 7.9 的 firewalld 0.6+ 已可用。

# 白名单优先放行
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule priority="-300" source ipset=whitelist_v4 accept'
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule priority="-300" family="ipv6" source ipset=whitelist_v6 accept'

# 黑洞优先丢弃
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule priority="-250" source ipset=blackhole_v4 drop'
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule priority="-250" family="ipv6" source ipset=blackhole_v6 drop'

# 禁止 ICMP echo(按需)
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule protocol value="icmp" drop'
firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule family="ipv6" protocol value="ipv6-icmp" drop'

# 应用变更
firewall-cmd --reload
firewall-cmd --list-all --zone=public

为什么用 ipset?

因为黑名单可能上千个,用 ipset 做 O(1) 匹配远比一条条 -s x.x.x.x -j DROP 高效,而且 Fail2ban 可以直接往 ipset 写条目。

2. Fail2ban:用 firewallcmd-ipset 打通动态封禁

2.1 全局配置(/etc/fail2ban/jail.local)

[DEFAULT]
# 用 journald 做后端,配 systemd 更稳
backend = systemd
# 识别到后直接往 firewalld 的 ipset 写
banaction = firewallcmd-ipset
# v4/v6 都打(有些版本用 banaction_allports 同理)
banaction_allports = firewallcmd-ipset
findtime = 10m
maxretry = 3
bantime = 1h

# 如果你的 fail2ban ≥ 0.11,推荐开启递增封禁
# bantime.increment = true
# bantime.factor = 4
# bantime.formula = bantime * (1 + failures / 6)
# bantime.overalljails = true

# 默认日志
logtarget = /var/log/fail2ban.log

注:CentOS 7 上 EPEL 的 fail2ban 版本不一;若不支持 bantime.increment,保持如上静态 bantime 即可,配合后面的 recidive 叠加打击“惯犯”。

2.2 常用 jail(sshd、nginx 基础认证、recidive)

[sshd]
enabled = true
port = ssh
filter = sshd
# journald 后端直接看 sshd 单元的日志
journalmatch = _SYSTEMD_UNIT=sshd.service
maxretry = 3
findtime = 10m
bantime  = 1h

# 如果 nginx 有 401/403,可对基础认证/爆破站点生效
[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/*error*.log
maxretry = 5
findtime = 10m
bantime  = 2h

# 识别全局“惯犯”(从 fail2ban 日志里统计)
[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
banaction = firewallcmd-ipset
bantime  = 24h
findtime = 12h
maxretry = 5

IPv6:firewallcmd-ipset 会分别往 v4 和 v6 的 ipset 写,前提是 firewalld 里有 family=inet6 的黑洞集合(我们已建)。

2.3 启动并检查

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

看到 Banned IP list: 出现条目后,到 firewalld 验证:

firewall-cmd --ipset=blackhole_v4 --get-entries
firewall-cmd --ipset=blackhole_v6 --get-entries

3. SSHD 最小暴露:密钥登录 + 基本并发约束

/etc/ssh/sshd_config 我一般做这些:

Port 22
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 3
ClientAliveInterval 120
ClientAliveCountMax 2

# 限制登录用户(按需)
AllowUsers opsadmin deployer

# 当你要兼容老客户端时,谨慎裁剪算法;CentOS7 自带 OpenSSH_7.4p1
KexAlgorithms curve25519-sha256, diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

改完先 不重启,用一个新终端从白名单 IP 测试无密码密钥登录成功,再 systemctl reload sshd。

4. sysctl:内核层面的小加固

/etc/sysctl.d/99-hardening.conf:

# SYN 洪泛的基本缓解
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096

# 反射与重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# 源路由与反向路径过滤
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# IPv6 同理
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# conntrack 适当拉高,配合你业务并发
net.netfilter.nf_conntrack_max = 262144

应用:sysctl --system

5. 基线策略表(我线上用的可抄)

层级 技术 作用范围 规则/决策 备注
0 IDC ACL 上联汇聚 仅放 22/80/443,封常见端口扫描段 减少垃圾进机房
1 firewalld zone OS internal=ACCEPT;public=最小端口 管理/业务分区
1 rich rule OS whitelist_v4/v6 ACCEPT 优先 priority=-300
1 rich rule OS blackhole_v4/v6 DROP priority=-250
2 ipset OS 存黑洞与白名单 O(1) 匹配
3 Fail2ban OS sshd/nginx/recidive 动态封禁 banaction=firewallcmd-ipset
4 sshd 应用 仅密钥、禁 root、限重试 防止账户被撞库
4 sysctl 内核 syn cookies、rp_filter 等 小幅增强

6. 验证与压测:我怎么确认“真挡住了”?

6.1 外部扫描(nmap)

在另一台公网主机上:

nmap -Pn -sS -p- your.hk.ip.address
# 只应看到 22/80/443(或你暴露的端口)

6.2 暴力模拟(Hydra 打 SSH)

(别拿生产账户试密码!给一个临时测试用户、随机弱口令,验证 Fail2ban 工作流程即可。)

# 远端跑
hydra -l test -P weak.txt -t 4 ssh://your.hk.ip.address

在目标机实时观察:

journalctl -fu sshd
tail -f /var/log/fail2ban.log
fail2ban-client status sshd
firewall-cmd --ipset=blackhole_v4 --get-entries

预期:三五次失败后,该攻击源被加入 blackhole_v4,后续握手直接被丢弃。

7. 观测与日常运维

7.1 统计“谁最皮”

# Top 20 恶意源
grep 'Ban ' /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -nr | head -20

7.2 误杀处理

# 查看 jail 状态
fail2ban-client status sshd

# 临时解封
fail2ban-client set sshd unbanip 203.0.113.10

# 永久白名单
firewall-cmd --permanent --ipset=whitelist_v4 --add-entry=203.0.113.10
firewall-cmd --reload

7.3 与监控打通

fail2ban-client status --json(若版本支持)或用 tail -F 监听 /var/log/fail2ban.log 推送到 Loki;在 Grafana 做“封禁速率/前 N 攻击源”面板。

node-exporter 的 nf_conntrack、iptables 统计也可做卡片,观察被动攻击时的流量变化。

8. 常见坑与我的解法

firewalld reload 后 Fail2ban 封禁失效

你大概率用了 iptables-multiport 这种直改 iptables 的 banaction。

解法:换成 banaction=firewallcmd-ipset,Fail2ban 与 firewalld 说同一种“语言”。

journald 不持久,重启看不到历史

解法:Storage=persistent 已在上文设置,别忘了。

IPv6 一直漏封

黑洞只有 v4。

解法:创建 blackhole_v6,并加上 family=ipv6 的 rich rule。

recidive 不工作

你的 logpath 指了错文件,或者 logrotate 把 fail2ban.log 搬走后没跟上。

解法:固定 logpath=/var/log/fail2ban.log,并保证 logrotate 的 postrotate 里 systemctl kill -s HUP fail2ban(或用 journald 后端)。

把自己关在门外

解法:先录入白名单,再 reload;并且始终保留可达的内网管理口与带外。

ipset 持久化

用 firewalld 创建/管理的 ipset 是跟着其配置持久化的;不要在同一台机器上再用 ipset save/restore 去“双轨”管理,以免打架。

9. 可复制的“一键基线脚本”(按需调整)

这个脚本只做基线:建 ipset、白名单/黑洞富规则、最小端口,其他仍需按你的环境二次定制。

cat >/root/bootstrap-fw.sh <<'EOF'
#!/bin/bash
set -e

# 区域绑定(按你的接口名改)
firewall-cmd --permanent --zone=public --change-interface=bond0 || true
firewall-cmd --permanent --zone=internal --change-interface=eno2 || true
firewall-cmd --permanent --zone=internal --set-target=ACCEPT

# 端口
for svc in ssh http https; do
  firewall-cmd --permanent --zone=public --add-service=$svc
done

# ipset
firewall-cmd --permanent --new-ipset=whitelist_v4 --type=hash:ip --option=family=inet || true
firewall-cmd --permanent --new-ipset=blackhole_v4 --type=hash:ip --option=family=inet || true
firewall-cmd --permanent --new-ipset=whitelist_v6 --type=hash:ip --option=family=inet6 || true
firewall-cmd --permanent --new-ipset=blackhole_v6 --type=hash:ip --option=family=inet6 || true

# 先加一组示例白名单(按需替换)
firewall-cmd --permanent --ipset=whitelist_v4 --add-entry=203.0.113.10
firewall-cmd --permanent --ipset=whitelist_v6 --add-entry=2001:db8:abcd::/48

# 富规则:白名单优先、黑洞丢弃、禁 ICMP
firewall-cmd --permanent --zone=public --add-rich-rule='rule priority="-300" source ipset=whitelist_v4 accept'
firewall-cmd --permanent --zone=public --add-rich-rule='rule priority="-300" family="ipv6" source ipset=whitelist_v6 accept'
firewall-cmd --permanent --zone=public --add-rich-rule='rule priority="-250" source ipset=blackhole_v4 drop'
firewall-cmd --permanent --zone=public --add-rich-rule='rule priority="-250" family="ipv6" source ipset=blackhole_v6 drop'
firewall-cmd --permanent --zone=public --add-rich-rule='rule protocol value="icmp" drop'
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv6" protocol value="ipv6-icmp" drop'

firewall-cmd --reload

# Fail2ban 基线
cat >/etc/fail2ban/jail.d/00-baseline.local <<'EOT'
[DEFAULT]
backend = systemd
banaction = firewallcmd-ipset
banaction_allports = firewallcmd-ipset
findtime = 10m
maxretry = 3
bantime = 1h
logtarget = /var/log/fail2ban.log

[sshd]
enabled = true
journalmatch = _SYSTEMD_UNIT=sshd.service
maxretry = 3
findtime = 10m
bantime  = 1h

[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
banaction = firewallcmd-ipset
bantime  = 24h
findtime = 12h
maxretry = 5
EOT

systemctl enable fail2ban --now
systemctl reload fail2ban || systemctl restart fail2ban

echo "[OK] Baseline firewall + fail2ban applied."
EOF

chmod +x /root/bootstrap-fw.sh
bash /root/bootstrap-fw.sh

10. 机房侧(Layer 0)的沟通模板(可直接发工单)

主题:上联 ACL 策略申请(业务 IP:203.0.113.20/29)

内容:

  • 允许:TCP/22、TCP/80、TCP/443(其余 TCP/UDP 一律拒绝)
  • 方向:Inbound 到 203.0.113.20/29
  • 备注:后续如需临时放行会另行通知工单号 XXXXXX
  • 联系:XXX,电话 +852-xxxx-xxxx

11. 性能与效果:我在线上的观察

ipset 挂 5k–10k 条黑名单,CPU 几乎无感(与一堆 -s ip -j DROP 可感知差别)。

Fail2ban 每小时封禁几十到上百源是常态(香港公网扫描很“勤奋”),recidive 能有效把反复重犯打到 24h,日封禁量立刻降下来。

firewalld 的富规则顺序一旦搞对(白名单优先),几乎不会误杀,这点非常关键。

12. 扩展与进阶

端口诱饵/蜜罐:在高频被扫端口挂 cowrie 等轻量蜜罐,Fail2ban 接管其日志再拉黑。

TFO/BBR:纯 HTTPS 服务可评估 tcp_fastopen 与 BBR(和你的 4 层负载均衡配合);对抗暴力破解不是直接收益,但整体握手效率更好。

将来迁移到 nftables:CentOS 7 是 iptables 后端,若未来迁 OS(Rocky/Alma),firewalld 仍可做“抽象层”,Fail2ban 的 firewallcmd-ipset 基本无缝。

结语 · 凌晨三点的机房安静了下来

机房的钟表指向凌晨三点,风扇声依旧在耳边轰鸣。Fail2ban 的日志里一行行 “Ban” 刷过去,就像我在值守屏幕上看见的那样——每一个试图暴力闯进来的 IP,都被无声地踢出了门外。那一刻我突然有点安心,觉得这一夜的操作没有白费。

其实这些配置并不算华丽,也没有什么“高科技”的安全黑魔法,更多的是经验累积出来的“手工活”:先保证上联 ACL 把垃圾流量挡在机房之外,再用 firewalld 固定基线,ipset 来承接大规模的动态名单,Fail2ban 再做“巡逻”,最后 sshd 和内核参数收紧做内务。每一层都很朴素,但叠加起来,就像机房夜里层层上锁的铁门,让我敢放心地关掉 KVM,拔下运维笔记本,回到出租屋去补觉。

写下这些,不是为了炫耀什么复杂的架构,而是想告诉后来的人:安全从来不是某一个组件的奇迹,而是点滴细节的积累。就像我这一夜的实战,汗水和冷风混在一起,把一台刚上架的香港服务器,变成了一台能真正扛住外部暴力入侵的机器。

目录结构
全文