香港服务器上如何利用 Ubuntu 18.04 的 firewalld 优化自定义网络策略,提高跨境访问速度并增强安全性?

凌晨 1:40,我站在机房 19U 的机柜前,手心还残着酒精湿巾的凉。白天华南业务部刚把“跨境访问慢”的锅甩了过来:广州与深圳用户打开我们在香港的 API 时延波动大、偶发 504,运维台上 SYN -> SYN/ACK 的握手就像堵在一条看不见的隧道里。
我做过太多“加带宽”“上 CDN”的直觉尝试,效果都一般。那晚我决定换个思路:用 firewalld 做一层“会思考的边界”,把无效流量挡在外面,把有效流量引到正确的链路上;再配合内核与路由调优,既要稳也要快。
下面是我那一夜到第二天清晨的完整实操与复盘。过程不神秘,但每一步都踩在真实场景的坑沿上。
1. 现场环境与目标
1.1 硬件与网络拓扑
| 项目 | 规格 |
|---|---|
| 机型 | Dell R740(示例) |
| CPU | Intel Xeon Silver 4210 × 2 |
| 内存 | 128 GB |
| 系统盘 | 2 × 960 GB SSD(RAID1) |
| 数据盘 | 2 × 1.92 TB NVMe(RAID1) |
| 网卡 | 2 × 10GbE(eno1:国际 BGP/CMI,eno2:优化回国/CN2) |
| 操作系统 | Ubuntu 18.04.5 LTS(内核 4.15.0-*) |
| 核心组件 | firewalld 0.5.x、conntrack、iproute2、Docker(部分节点) |
说明:Ubuntu 18.04 的默认 iptables 为 legacy 后端,firewalld 在这个版本工作稳定。BBR 从 4.9 起支持,4.15 可直接启用。
1.2 业务与端口
| 模块 | 协议/端口 | 说明 |
|---|---|---|
| Web/API | 80/tcp, 443/tcp | Nginx 反代、多域名 |
| 管理 | 22/tcp | 仅办公出口与堡垒机可访问 |
| 业务 TCP | 7000-7020/tcp | 内部 RPC(跨可用区) |
| 健康检查 | 9100/tcp, 9090/tcp | Exporter/Prometheus |
1.3 基线数据(调优前)
数据为当天 3 组可复现实验的中位数,仅供方法论参考。
| 指标 | 广州移动 → HK(CMI) | 深圳电信 → HK(CN2) |
|---|---|---|
| ICMP RTT(ms) | 39~45 | 33~38 |
| TLS 建连 P95(ms) | 420 | 280 |
| 1 并发下载(MiB/s) | 2.1 | 4.3 |
| 20 并发下载(MiB/s) | 6.8 | 12.5 |
| 504/502 比例 | 0.7% | 0.2% |
目标:在不改业务架构的前提下,通过 firewalld + 内核/路由策略把稳定性先拉满(降错误率、控突发),再把有效吞吐拉起来(降低握手重试与 PMTU 相关的损耗),最终让华南用户的 P95 建连与下载速度肉眼可见地提升。
2. 方案设计:把“边界”做聪明
Zone 分层:
- external:面对公网(eno1/eno2),默认拒绝,仅放行业务端口。
- trusted:容器网桥/内部回环(docker0、lo、内网 VLAN)。
- management(自建 zone):只给堡垒机与办公网开放 SSH、Prometheus。
ipset + rich rules:
- 将允许运维访问与高风险段分别放入 ipset,规则一键引用、易维护。
MSS/PMTU 与 TCP:
- mangle 链 MSS Clamping,减少跨境链路上 MTU 不一致造成的分片与重传。
- 启用 BBR、适度 TFO/keepalive、调 somaxconn 与 conntrack。
双上联的策略路由(可选):
- 按目的网段或SNI/端口特征做 MARK,通过 ip rule 走对应上联(CMI/CN2)。
速率与连接保护:
- hashlimit/connlimit 阻断异常并发,SYN flood 的速率阈值控制。
Docker 共存:
- docker0 入 trusted,公网 zone 做 --add-masquerade,避免链冲突。
3. 实操步骤
3.1 准备系统
# 禁用并移除 UFW,避免和 firewalld 抢规则
sudo systemctl stop ufw && sudo systemctl disable ufw
sudo apt purge -y ufw
# 安装 firewalld 与常用工具
sudo apt update
sudo apt install -y firewalld ipset conntrack iproute2
# 启动并开机自启
sudo systemctl enable --now firewalld
sudo firewall-cmd --state # 预期输出:running
坑 1:如果之前装过 iptables-persistent,它的还原脚本可能在重启时覆盖 firewalld 设定。建议卸载或清空其规则文件。
3.2 Zone 规划与接口绑定
# 新建 management zone
sudo firewall-cmd --permanent --new-zone=management
sudo firewall-cmd --permanent --zone=management --set-target=DROP
# 绑定接口到 zone(根据实际网卡名)
sudo firewall-cmd --permanent --zone=external --add-interface=eno1
sudo firewall-cmd --permanent --zone=external --add-interface=eno2
sudo firewall-cmd --permanent --zone=trusted --add-interface=docker0
# 设置默认 zone 为 external(更安全的默认拒绝)
sudo firewall-cmd --set-default-zone=external
# 放行业务端口(external)
sudo firewall-cmd --permanent --zone=external --add-service=http
sudo firewall-cmd --permanent --zone=external --add-service=https
sudo firewall-cmd --permanent --zone=external --add-port=7000-7020/tcp
# 管理面(management)仅允 SSH 与监控
sudo firewall-cmd --permanent --zone=management --add-service=ssh
sudo firewall-cmd --permanent --zone=management --add-port=9090/tcp
sudo firewall-cmd --permanent --zone=management --add-port=9100/tcp
# 允许来自办公出口与堡垒机的管理访问(示例网段)
sudo firewall-cmd --permanent --zone=management \
--add-source=203.0.113.0/24
sudo firewall-cmd --permanent --zone=management \
--add-source=198.51.100.10/32
# 公网 SNAT/容器出口
sudo firewall-cmd --permanent --zone=external --add-masquerade
sudo firewall-cmd --reload
sudo firewall-cmd --get-active-zones
小技巧:用 --add-source 把可信来源“拉进”到更高权限的 zone,比在 external 中逐项放行更直观。
3.3 ipset 与 rich rules(白/黑名单)
# 允许的管理来源(白名单)
sudo firewall-cmd --permanent --new-ipset mgmt_allow --type=hash:ip
sudo firewall-cmd --permanent --ipset=mgmt_allow --add-entry=203.0.113.0/24
sudo firewall-cmd --permanent --ipset=mgmt_allow --add-entry=198.51.100.10
# 高风险段(示例)
sudo firewall-cmd --permanent --new-ipset risk_drop --type=hash:net
sudo firewall-cmd --permanent --ipset=risk_drop --add-entry=45.155.0.0/16
# 使用 rich rules 引用 ipset
sudo firewall-cmd --permanent --add-rich-rule='rule source ipset=risk_drop drop'
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source ipset=mgmt_allow service name=ssh accept'
sudo firewall-cmd --reload
批量导入国家网段:
- 可从 IP 区段公开源下载(如 *.zone),批量写入 ipset 后用 rich-rule 引用;注意 每日差异更新,避免规则膨胀。示例导入(思路):
while read ip; do
sudo firewall-cmd --permanent --ipset=risk_drop --add-entry=$ip
done < cn.zone
sudo firewall-cmd --reload
3.4 传输层优化(BBR、TFO、连接跟踪等)
# 启用 BBR、TCP Fast Open(Server),优化 listen/backlog
cat <<'EOF' | sudo tee /etc/sysctl.d/99-crossborder.conf
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
net.ipv4.tcp_fastopen=3
net.core.somaxconn=4096
net.ipv4.tcp_max_syn_backlog=4096
net.ipv4.ip_local_port_range=10240 60999
net.netfilter.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_tcp_timeout_established=1200
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_fin_timeout=15
EOF
sudo sysctl --system
坑 2:tcp_fastopen 需要应用层(如 Nginx)也开启 fastopen 才能显效;此外个别中间设备会对 TFO 不友好,如异常可先关掉验证。
3.5 MSS Clamping(修复跨境 PMTU 问题)
跨境链路经常存在 MTU 低于 1500 的段(如 1460/1492),容易触发分片与重传。我们通过 direct 规则 在 mangle 表对 SYN 做 MSS 自动钳制:
# 对本机对外连接
sudo firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 0 \
-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# 对转发流量(如作为网关时)
sudo firewall-cmd --permanent --direct --add-rule ipv4 mangle FORWARD 0 \
-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo firewall-cmd --reload
坑 3:有的教程把规则加在 POSTROUTING,但对入站连接不生效;放在 FORWARD/OUTPUT 更稳。使用 0 优先级确保位于链首。
3.6 SYN flood/异常并发保护(hashlimit/connlimit)
# SSH 并发上限(超过直接复位)
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-p tcp --dport 22 -m connlimit --connlimit-above 5 -j REJECT --reject-with tcp-reset
# 针对 443 的 SYN 速率限制(每源 50/s,突发 200)
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
-p tcp --dport 443 -m state --state NEW -m hashlimit \
--hashlimit-name https_syn --hashlimit-mode srcip --hashlimit-upto 50/second \
--hashlimit-burst 200 -j ACCEPT
# 超出直接丢弃
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2 \
-p tcp --dport 443 -m state --state NEW -j DROP
sudo firewall-cmd --reload
说明:firewalld 的 rich 语言对“速率限制”主要用于日志;精细限速建议走 direct 规则,调用 iptables 匹配扩展更可靠。
3.7(可选)双上联策略路由:按目的地走“对的线”
场景:eno1 走 CMI(对移动友好),eno2 走 CN2(对电信友好)。按目的网段给报文打标记,走不同路由表:
# 1) 为不同上联建独立路由表(/etc/iproute2/rt_tables)
echo "100 cmi" | sudo tee -a /etc/iproute2/rt_tables
echo "200 cn2" | sudo tee -a /etc/iproute2/rt_tables
# 2) 各自默认路由(按实际网关)
sudo ip route add default via 10.0.0.1 dev eno1 table cmi
sudo ip route add default via 10.0.1.1 dev eno2 table cn2
# 3) 打标 + 策略规则
# - 目的为 223.120.0.0/14(示例,移动常见归属)走 CMI
# - 目的为 101.0.0.0/8(示例,电信常见段)走 CN2
# mangle OUTPUT 标记(本机发起)
sudo firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 0 \
-d 223.120.0.0/14 -j MARK --set-mark 0x64
sudo firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 1 \
-d 101.0.0.0/8 -j MARK --set-mark 0xc8
# 应用策略路由
sudo ip rule add fwmark 0x64 table cmi
sudo ip rule add fwmark 0xc8 table cn2
sudo firewall-cmd --reload
坑 4:策略路由容易产生出入向不对称。务必在对应 egress zone 开启 MASQUERADE 或在边界路由器上处理 SNAT,保持回程可达。
3.8 端口转发与健康检查
# 将外部 443 转发到内网 10.0.10.10:443(四层直转)
sudo firewall-cmd --permanent --zone=external \
--add-forward-port=port=443:proto=tcp:toaddr=10.0.10.10:toport=443
# 放行健康检查到 exporter
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 \
source address=198.51.100.200/32 port port=9100 protocol=tcp accept'
sudo firewall-cmd --reload
4. 验证与观测
4.1 快速体检命令
firewall-cmd --list-all --zone=external
firewall-cmd --list-rich-rules
sudo conntrack -S
ss -s
ip rule show; ip route show table cmi; ip route show table cn2
sudo tcpdump -ni eno1 tcp[tcpflags] & 0x02 != 0 -c 50 # 观察 SYN
4.2 优化后数据(同样 3 组中位数)
| 指标 | 广州移动 → HK(CMI) | 深圳电信 → HK(CN2) |
|---|---|---|
| ICMP RTT(ms) | 39~45(无变化) | 33~38(无变化) |
| TLS 建连 P95(ms) | 310(-110) | 220(-60) |
| 1 并发下载(MiB/s) | 3.3(+57%) | 5.6(+30%) |
| 20 并发下载(MiB/s) | 10.2(+50%) | 15.8(+26%) |
| 504/502 比例 | 0.18% | 0.08% |
这些提升来自 无效重试与重传的减少(MSS/BBR/conntrack),以及把有效流量送上更匹配的回国线(策略路由)。
5. 与 Docker 的和平相处
Docker 会改 iptables,常见的两个点:
把 docker0 加入 trusted 并允许 SNAT(前文已做)。
在 external zone 打开 相关/已建立连接即可;若有复杂转发,优先用 firewalld 的 forward-port,避免和 Docker 自建 NAT 乱序。
# 允许既有连接与 ICMP(默认有,确认)
sudo firewall-cmd --permanent --zone=external --add-service=ssh
sudo firewall-cmd --permanent --zone=external --add-icmp-block-inversion=no
sudo firewall-cmd --reload
坑 5:firewalld --reload 不会丢失 --permanent 的 direct 规则,但只写了运行态的 direct 规则会丢;务必加 --permanent 再 reload。
6. 运维常用“回滚按钮”
出问题时先保命:
# 暂时放宽(时间窗内操作完再收紧)
sudo firewall-cmd --zone=external --add-service=ssh
sudo firewall-cmd --zone=external --add-port=443/tcp
# 完整重载(同步磁盘配置)
sudo firewall-cmd --complete-reload
# 彻底停用 firewalld(慎用,建议有带外/KVM)
sudo systemctl stop firewalld
我会在 /root/ 留一份最小可用的 iptables-restore 文本,紧急时一键恢复最小白名单与 443 放行,避免把自己锁在门外。
7. FAQ 与排障手记
握手还是慢?
看 tcpdump 是否有 Frag needed but DF set(PMTU 黑洞),若有,确认 MSS Clamping 是否生效、优先级是否在链首。
BBR 不生效?
sysctl net.ipv4.tcp_congestion_control 应为 bbr;若是容器内监听,宿主侧 BBR 仍生效,但应用层限速/反向代理配置也会影响。
策略路由无效?
检查 MARK 是否命中(iptables -t mangle -S | grep MARK),以及 ip rule 顺序(策略规则按序匹配)。
连接耗尽?
查看 conntrack -S,适度提升 nf_conntrack_max,并用 hashlimit/connlimit 削峰。
DSCP 标记有用吗?
跨境多数运营商会清零 DSCP,不保证生效,生产中不以此为核心手段。
8. 一次性部署脚本(节选)
供参考,建议按需拆分成 Ansible 角色或 cloud-init。
#!/usr/bin/env bash
set -euo pipefail
apt update && apt install -y firewalld ipset conntrack iproute2
systemctl enable --now firewalld
firewall-cmd --permanent --new-zone=management
firewall-cmd --permanent --zone=management --set-target=DROP
firewall-cmd --permanent --zone=external --add-interface=eno1
firewall-cmd --permanent --zone=external --add-interface=eno2
firewall-cmd --permanent --zone=trusted --add-interface=docker0
firewall-cmd --set-default-zone=external
for svc in http https; do
firewall-cmd --permanent --zone=external --add-service=$svc
done
firewall-cmd --permanent --zone=external --add-port=7000-7020/tcp
firewall-cmd --permanent --zone=external --add-masquerade
firewall-cmd --permanent --zone=management --add-service=ssh
firewall-cmd --permanent --zone=management --add-port=9090/tcp
firewall-cmd --permanent --zone=management --add-port=9100/tcp
firewall-cmd --permanent --zone=management --add-source=203.0.113.0/24
firewall-cmd --permanent --zone=management --add-source=198.51.100.10/32
firewall-cmd --permanent --new-ipset mgmt_allow --type=hash:ip
firewall-cmd --permanent --ipset=mgmt_allow --add-entry=203.0.113.0/24
firewall-cmd --permanent --ipset=mgmt_allow --add-entry=198.51.100.10
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source ipset=mgmt_allow service name=ssh accept'
firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 0 \
-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
firewall-cmd --permanent --direct --add-rule ipv4 mangle FORWARD 0 \
-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-p tcp --dport 22 -m connlimit --connlimit-above 5 -j REJECT --reject-with tcp-reset
sysctl -w net.core.default_qdisc=fq
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_fastopen=3
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=1200
firewall-cmd --reload
9. 清晨 6:20 的那条消息
天色发白时,Prometheus 的告警板从橙色褪成了绿色。广州移动侧的 P95 TLS 建连压到了 300ms 出头,API 错误率掉到千分之二以下。业务负责人在群里丢来一句:“这次真稳住了。”
我把那晚做的每一步都记在变更单里:不是为了证明“firewalld 能让网络变快”,而是要证明把边界做聪明,有效流量自然就快了——这在跨境场景尤其成立。
10. 清单复核(给未来的我/你)
- UFW 已禁用,firewalld 运行且为唯一规则管理者
- Zone/接口/源地址划分清晰,默认拒绝,显式放行
- ipset 管理白/黑名单,差异更新可自动化
- BBR/TFO/conntrack/sysctl 生效且观测指标正常
- MSS Clamping 在 OUTPUT/FORWARD 生效,确认优先级
- (可选)策略路由出入向对称,回程可达
- Docker 与 firewalld 规则边界明确,避免链冲突
- 预案:最小可用规则与紧急回滚脚本随时可用
如果你也在香港机房扛着“跨境访问慢”的锅,以上流程基本可以复用。先让边界会思考,再让链路去奔跑——这是那夜留给我的最朴素的经验。