— 以 Docker、Fail2Ban 為例 —
在實際維運環境中,我們常會自己寫一份防火牆腳本來控管流量,例如 NAT、DNAT、上網白名單、時間限制等。
但如果主機同時運行 Docker、Fail2Ban、libvirt 等服務,這些服務也會在 iptables 中自動產生規則。
若不小心「清空整個防火牆」,就會把它們的規則一併刪除,導致容器、SSH、甚至 fail2ban 全部失效。
本文介紹一個簡潔安全的方式:
建立自有鏈 (custom chains),在不影響其他服務的前提下,自行維護防火牆邏輯。
🧩 為什麼會互相影響?
因為許多服務(像 Docker、Fail2Ban)啟動時都會自動執行:
iptables -t filter -F
或建立自己的鏈:
- Docker:
DOCKER,DOCKER-USER,DOCKER-ISOLATION-STAGE-1 - Fail2Ban:
f2b-sshd,f2b-postfix, …
如果我們的腳本中也執行 iptables -F、iptables -X,那就會清空這些鏈的規則,導致:
- Docker 容器出不了網或無法接收外部連線
- Fail2Ban 無法阻擋攻擊
- 甚至 NAT 轉址全部失效
✅ 解決原則:用「自有鏈」管理,不清整表
我們不再動系統原生的 INPUT、FORWARD、OUTPUT 等預設鏈,
而是在它們裡面「插入一次跳轉」到我們自己的鏈,例如:
filter:INPUT → FW-INPUT
filter:DOCKER-USER → FW-FORWARD
nat:PREROUTING → FW-PREROUTING
nat:POSTROUTING → FW-POSTROUTING
mangle:FORWARD → FW-FORWARD (MSS clamp)
這樣:
- 我們可以重載自己的規則,只要
-F FW-*就好。 - Docker、Fail2Ban 的鏈都不會被清掉。
- 每次重啟腳本都能安全更新設定。
🔧 實作架構範例
建立自有鏈(只清自己的鏈)
# 建立鏈(若不存在)
for T in filter nat mangle; do
iptables -t $T -N FW-INPUT 2>/dev/null || true
iptables -t $T -N FW-FORWARD 2>/dev/null || true
iptables -t $T -N FW-PREROUTING 2>/dev/null || true
iptables -t $T -N FW-POSTROUTING 2>/dev/null || true
done
# 清空自己的鏈,不動系統鏈
iptables -t filter -F FW-INPUT
iptables -t filter -F FW-FORWARD
iptables -t nat -F FW-PREROUTING
iptables -t nat -F FW-POSTROUTING
iptables -t mangle -F FW-FORWARD
# 插入跳轉(若已存在就跳過)
iptables -C INPUT -j FW-INPUT 2>/dev/null || iptables -I INPUT -j FW-INPUT
iptables -N DOCKER-USER 2>/dev/null || true
iptables -F DOCKER-USER
iptables -A DOCKER-USER -j FW-FORWARD
iptables -t nat -C PREROUTING -j FW-PREROUTING 2>/dev/null || iptables -t nat -I PREROUTING 1 -j FW-PREROUTING
iptables -t nat -C POSTROUTING -j FW-POSTROUTING 2>/dev/null || iptables -t nat -A POSTROUTING -j FW-POSTROUTING
🔍 各表的職責清楚分工
| Table | 主要職責 | 我們自有鏈功能 |
|---|---|---|
| filter | ACL(放行、封鎖) | FW-INPUT, FW-FORWARD(包含上網白名單、時間控管) |
| nat | 位址轉換 | FW-PREROUTING(DNAT)、FW-POSTROUTING(MASQ / Hairpin) |
| mangle | 封包修改 | FW-FORWARD(MSS Clamp for PPPoE) |
這樣各自分工,不會互相干擾。
⚙️ 例:MSS Clamp 移到 mangle/FORWARD
iptables -t mangle -A FW-FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1452
適用 PPPoE 或 MTU 限制環境,防止 Fragmentation 問題。
⚙️ 例:上網白名單 + 時間控制(filter/FORWARD)
我們用 ipset 來管理「允許上網的 IP」清單,
然後在 filter/FORWARD 實施控制。
# 建立 ipset 清單
ipset create internet_allowed hash:ip -exist
ipset add internet_allowed 192.168.100.2
ipset add internet_allowed 192.168.100.21
# 規則:白名單 IP 隨時可上網
iptables -A FW-FORWARD -m set --match-set internet_allowed src -o ppp0 -j ACCEPT
# 規則:特定時段(UTC)
iptables -A FW-FORWARD -s 192.168.100.0/24 -o ppp0 -m time --timestart 00:00 --timestop 00:30 -j ACCEPT
iptables -A FW-FORWARD -s 192.168.100.0/24 -o ppp0 -m time --timestart 01:00 --timestop 01:30 -j ACCEPT
# ...其他時段略
# 其餘時間一律封鎖
iptables -A FW-FORWARD -s 192.168.100.0/24 -o ppp0 -j DROP
這樣即時生效,不需重啟 iptables。
新增 / 刪除 IP 只要操作 ipset:
ipset add internet_allowed 192.168.100.50
ipset del internet_allowed 192.168.100.21
⚙️ 例:DNAT + Hairpin NAT(nat)
# 外部 80/443 轉內部 Web Server
iptables -t nat -A FW-PREROUTING -p tcp -d 122.116.109.114 --dport 80 -j DNAT --to-destination 192.168.100.2:80
iptables -t nat -A FW-PREROUTING -p tcp -d 122.116.109.114 --dport 443 -j DNAT --to-destination 192.168.100.2:443
# Hairpin NAT:內網用公網 IP 也能存取
iptables -t nat -A FW-POSTROUTING -s 192.168.100.0/24 -d 192.168.100.2 -o br.eno1.5 -j MASQUERADE
⚙️ Fail2Ban / Docker 為何不再被清空?
因為:
- Docker 的
DOCKER,DOCKER-USER鏈從未被清除;我們只掛一個跳轉進去。 - Fail2Ban 自建的
f2b-*鏈在filter/INPUT仍然存在。 - 我們的腳本只 flush
FW-*,所以兩者完全不受影響。
🧰 ipset 操作速查
| 動作 | 指令 |
|---|---|
| 建立集合 | ipset create internet_allowed hash:ip |
| 加入 IP | ipset add internet_allowed 192.168.100.21 |
| 刪除 IP | ipset del internet_allowed 192.168.100.21 |
| 清空集合 | ipset flush internet_allowed |
| 刪除集合 | ipset destroy internet_allowed |
| 存檔 / 還原 | ipset save > /etc/firewall/ipset.rules / ipset restore < /etc/firewall/ipset.rules |
即時生效:ipset 是核心層資料結構,iptables 只引用 set 名稱,
因此你新增或刪除 IP 不需重新載入防火牆,規則會立即生效。
🧠 為何這種設計比較好?
| 傳統做法 | 改進後的自有鏈設計 |
|---|---|
每次 iptables -F 清空全表 | 只清自有鏈,其他服務規則不受影響 |
| 服務重啟會覆蓋規則 | 只要在 DOCKER-USER、PREROUTING 插入一次即可 |
| 上網白名單要寫多條規則 | ipset 一條搞定,效能更好 |
| 改設定要重跑整份腳本 | 即時 ipset add/del 即可生效 |
⚙️ 開機自動載入
透過 systemd 管理開機順序:
[Unit]
Description=Firewall rules loader (after Docker)
Wants=docker.service
After=network-online.target docker.service
Requires=network-online.target
[Service]
Type=oneshot
ExecStart=/etc/firewall/firewall.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
✅ 結語
這套架構的關鍵思維是:
讓自建防火牆「只管理自己的區域」,而不是接管整個 iptables。
這樣:
- Docker、Fail2Ban 等服務可以自由生成自己的規則;
- 你的自有鏈只專注在網路架構與存取控制;
- 未來要改、要除錯、要搬遷,都能安全地進行。
📎 延伸閱讀
- iptables packet flow diagram (netfilter.org)
- Docker and iptables official doc
- Fail2Ban firewall integration
man ipset,man iptables