Skip to content

Nuface Blog

隨意隨手記 Casual Notes

Menu
  • Home
  • About
  • Services
  • Blog
  • Contact
  • Privacy Policy
  • Login
Menu

Building a Custom Firewall Without Affecting Other Services’ Rules

Posted on 2025-11-042025-11-04 by Rico

— e.g. Docker, Fail2Ban, libvirt —

In real-world environments, we often maintain our own firewall scripts — for example, to handle NAT, DNAT, outbound access control, or time-based restrictions.
However, servers frequently run services such as Docker, Fail2Ban, or libvirt, which also manage their own iptables rules.

If you flush all existing rules using something like iptables -F or iptables -t nat -F, you may accidentally remove rules that these services rely on — breaking containers, SSH, or automatic banning entirely.

This post describes a safe and structured way to manage your own firewall rules —

by creating custom chains that coexist peacefully with other system-managed rules.


🧩 Why Do They Interfere?

Services like Docker or Fail2Ban automatically modify the firewall when they start.
For example:

  • Docker adds its own chains: DOCKER, DOCKER-USER, DOCKER-ISOLATION-STAGE-1
  • Fail2Ban creates chains like f2b-sshd, f2b-postfix, etc.
  • Many services internally execute iptables -F or iptables -X when reloading.

If your script also clears entire tables (iptables -t filter -F, iptables -t nat -F), it wipes those rules too — causing:

  • Docker containers to lose external connectivity
  • Fail2Ban to stop blocking malicious IPs
  • All NAT or DNAT rules to disappear

✅ The Principle: Manage Your Own Chains, Don’t Flush Everything

Instead of rewriting built-in chains like INPUT, FORWARD, and OUTPUT,
you only insert a single jump to your own custom chain, such as:

filter:INPUT       → FW-INPUT
filter:DOCKER-USER → FW-FORWARD
nat:PREROUTING     → FW-PREROUTING
nat:POSTROUTING    → FW-POSTROUTING
mangle:FORWARD     → FW-FORWARD (for MSS clamp)

That way:

  • You can reload your own rules safely — just flush FW-* chains.
  • Docker and Fail2Ban rules remain untouched.
  • Re-running your script won’t break system-generated chains.

🔧 Implementation Example

Create Custom Chains (Flush Only Yours)

# Create chains (ignore errors if they already exist)
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

# Flush only your own chains
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

# Insert jumps only once (if not already present)
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

🔍 Clear Role Separation by Table

TablePurposeCustom Chain
filterACLs (accept/deny traffic)FW-INPUT, FW-FORWARD (includes whitelist and time rules)
natAddress translationFW-PREROUTING (DNAT), FW-POSTROUTING (MASQ / hairpin NAT)
manglePacket modificationFW-FORWARD (TCP MSS clamp)

Each table handles its own logic — no overlap or conflicts.


⚙️ Example: MSS Clamp in mangle/FORWARD

iptables -t mangle -A FW-FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1452

This avoids packet fragmentation on PPPoE or low-MTU links.


⚙️ Example: Internet Whitelist + Time Control (filter/FORWARD)

We can manage the “who can access the Internet” list using ipset,
then enforce the rule inside filter/FORWARD.

# Create the IP set
ipset create internet_allowed hash:ip -exist
ipset add internet_allowed 192.168.100.2
ipset add internet_allowed 192.168.100.21

# Allow these IPs to go online at any time
iptables -A FW-FORWARD -m set --match-set internet_allowed src -o ppp0 -j ACCEPT

# Allow the entire subnet during specific time windows (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
# ...and other windows as needed

# Drop everything else
iptables -A FW-FORWARD -s 192.168.100.0/24 -o ppp0 -j DROP

These changes take effect immediately — no need to reload iptables.

Update your whitelist dynamically:

ipset add internet_allowed 192.168.100.50
ipset del internet_allowed 192.168.100.21

⚙️ Example: DNAT + Hairpin NAT (nat)

# External 80/443 → internal 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: allow internal users to access via public IP
iptables -t nat -A FW-POSTROUTING -s 192.168.100.0/24 -d 192.168.100.2 -o br.eno1.5 -j MASQUERADE

⚙️ Why Docker and Fail2Ban Stay Safe

  • Docker’s chains (DOCKER, DOCKER-USER, etc.) remain untouched — we only jump into them.
  • Fail2Ban’s f2b-* chains stay intact under filter/INPUT.
  • The script flushes only FW-*, so all system-generated chains continue working normally.

🧰 ipset Quick Reference

ActionCommand
Create a setipset create internet_allowed hash:ip
Add IPipset add internet_allowed 192.168.100.21
Remove IPipset del internet_allowed 192.168.100.21
Flush all elementsipset flush internet_allowed
Destroy the setipset destroy internet_allowed
Save / Restoreipset save > /etc/firewall/ipset.rules / ipset restore < /etc/firewall/ipset.rules

Instant effect: ipset works at the kernel level.
iptables simply references the set name — any changes (add/del) apply immediately without reloading rules.


🧠 Why This Design Works Better

Traditional ApproachCustom Chain Approach
Flushes entire tablesFlushes only custom chains
System services lose rulesDocker / Fail2Ban unaffected
Dozens of whitelist rulesOne ipset match — O(1) lookup
Need to rerun full script to apply changesDynamic add/remove instantly effective

⚙️ Systemd Integration (Auto-load on Boot)

[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

✅ Summary

The key mindset is simple:

Manage only your own namespace — don’t take over the entire iptables.

By isolating your rules into dedicated custom chains:

  • System-managed rules (Docker, Fail2Ban, etc.) remain intact
  • Your firewall logic stays modular and predictable
  • Troubleshooting, updates, and re-deployment become much safer

📎 Further Reading

  • Netfilter Hook Order (nftables.org)
  • Docker and iptables — Official Docs
  • Fail2Ban Integration with Firewalls
  • man ipset, man iptables

Recent Posts

  • Postfix + Let’s Encrypt + BIND9 + DANE Fully Automated TLSA Update Guide
  • Postfix + Let’s Encrypt + BIND9 + DANE TLSA 指紋自動更新完整教學
  • Deploying DANE in Postfix
  • 如何在 Postfix 中部署 DANE
  • DANE: DNSSEC-Based TLS Protection

Recent Comments

  1. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on High Availability Architecture, Failover, GeoDNS, Monitoring, and Email Abuse Automation (SOAR)
  2. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on MariaDB + PostfixAdmin: The Core of Virtual Domain & Mailbox Management
  3. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Daily Operations, Monitoring, and Performance Tuning for an Enterprise Mail System
  4. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Final Chapter: Complete Troubleshooting Guide & Frequently Asked Questions (FAQ)
  5. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Network Architecture, DNS Configuration, TLS Design, and Postfix/Dovecot SNI Explained

Archives

  • December 2025
  • November 2025
  • October 2025

Categories

  • AI
  • Apache
  • Cybersecurity
  • Database
  • DNS
  • Docker
  • Fail2Ban
  • FileSystem
  • Firewall
  • Linux
  • LLM
  • Mail
  • N8N
  • OpenLdap
  • OPNsense
  • PHP
  • QoS
  • Samba
  • Switch
  • Virtualization
  • VPN
  • WordPress
© 2025 Nuface Blog | Powered by Superbs Personal Blog theme