Another session with Claude AI had me examining most of my IPFire configuration. That was not done lightly and a lot of testing over a number of days was done, including just leaving it to sift a while and looking for any oddities among my devices or connectivity, be that local or remote. Another motive for this was my recent decision to rely more on IPFire to protect my network, I have cancelled other services for economical reasons.
While reviewing my firewall configuration I started looking at what the firewall itself is allowed to send outbound. There are also a couple of forum posts related to this and the official docs: www.ipfire.org - Additional Security Configuration
The answer by default is: everything. Which caught me a bit by surprise. No restrictions, no logging. This post documents how to change that, why it matters, and the practical snags encountered along the way.
What was done
Implemented a whitelist-based outbound policy restricting IPFire’s own generated traffic using the POLICYOUT chain via /etc/sysconfig/firewall.local. Only explicitly needed services are allowed out. Everything else is rate-limited, logged and dropped.
In this we took great care to check what services I actually use, what plugins are installed:
firmware-update-20210107-2
flashrom-1.2-2
guardian-2.0.2-27
perl-Net-IP-1.26-4
perl-common-sense-3.74-4
perl-inotify2-1.22-4
tcpdump-4.99.5-17
watchdog-5.16-6
wio-1.3.2-18
You may have other plugins, then you would have to check those when doing anything similar.
Why it matters
If IPFire is ever compromised through a vulnerability, weak credentials, or a malicious addon, an attacker’s first move is probably beaconing outbound to a Command & Control server for instructions. With default POLICYOUT that traffic walks straight out undetected on any port.
With a whitelist in place, any unexpected outbound connection attempt is immediately logged and blocked. The log entry itself becomes your intrusion detection alert.
We tested this live:
for port in 1337 4444 6666 8888; do
nc -zv 8.8.8.8 $port -w 2 2>&1
done
Every attempt timed out. dmesg showed:
DROP_POLICYOUT OUT=red0 SRC=[EDITED MY EXT IP] DST=8.8.8.8 DPT=1337
DROP_POLICYOUT OUT=red0 SRC=[EDITED MY EXT IP] DST=8.8.8.8 DPT=4444
DROP_POLICYOUT OUT=red0 SRC=[EDITED MY EXT IP] DST=8.8.8.8 DPT=6666
DROP_POLICYOUT OUT=red0 SRC=[EDITED MY EXT IP] DST=8.8.8.8 DPT=8888
Classic malware beaconing ports — caught, logged, killed.
Without POLICYOUT hardening those packets leave silently with zero trace.
The configuration
Make a backup of edited files first and foremost.
Edit /etc/sysconfig/firewall.local:
#!/bin/sh
# Used for private firewall rules
# Edited by [me and Claude AI] feb 23 2026 - backup of original file is in the same folder named *.original
# See how we were called.
case "$1" in
start)
## Flush first to prevent duplicates on reload
iptables -F POLICYOUT
## POLICYOUT - Restrict firewall's own outbound traffic
iptables -I POLICYOUT 1 -j DROP
iptables -I POLICYOUT 1 -m limit --limit 5/min --limit-burst 10 -j LOG --log-prefix "DROP_POLICYOUT " --log-level 4
iptables -I POLICYOUT 1 -p icmp -j ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 443 -j ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 80 -j ACCEPT
iptables -I POLICYOUT 1 -p udp --dport 123 -j ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 53 -j ACCEPT
iptables -I POLICYOUT 1 -p udp --dport 53 -j ACCEPT
# Allow DHCP on RED interface for lease renewal
iptables -I POLICYOUT 1 -o red0 -p udp --sport 68 --dport 67 -j ACCEPT
# Allow WIO (Who Is Online) addon - internal subnets only
iptables -I POLICYOUT 1 -p tcp --dport 7 -d 192.168.10.0/24 -j ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 7 -d 192.168.20.0/24 -j ACCEPT
;;
stop)
iptables -F POLICYOUT
;;
reload)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|reload}"
;;
esac
As you see my ip ranges are 192.168.10.0/24 for Green and 192.168.20.0/24 for Blue. You will have to change that WIO to match. I have no Orange or other network for now. If you don’t run the WIO addon those two rules can be omitted entirely.
Apply and verify:
/etc/sysconfig/firewall.local stop
/etc/sysconfig/firewall.local start
iptables -L POLICYOUT -n -v --line-numbers
Expected result — 11 clean rules, DROP counter at zero:
num pkts target prot destination
1 2 ACCEPT tcp 192.168.10.0/24 dpt:7
2 0 ACCEPT tcp 192.168.20.0/24 dpt:7
3 0 ACCEPT udp red0 spt:68 dpt:67
4 24 ACCEPT udp dpt:53
5 0 ACCEPT tcp dpt:53
6 0 ACCEPT udp dpt:123
7 0 ACCEPT tcp dpt:80
8 0 ACCEPT tcp dpt:443
9 2 ACCEPT icmp
10 0 LOG all limit: avg 5/min burst 10 prefix "DROP_POLICYOUT"
11 0 DROP all
Monitor for unexpected drops:
dmesg | grep DROP_POLICYOUT
Snags encountered
1) Rules appended with -A landed below IPFire’s ACCEPT, never evaluated
IPFire’s /usr/sbin/firewall-policy injects a blanket ACCEPT all into POLICYOUT automatically. Using -A appended our rules below that — they were never reached.
Fix: Use -I POLICYOUT 1 to insert rules at position 1, above IPFire’s ACCEPT.
2) Flush order is critical
Initial attempt placed new rules before the flush line:
# WRONG — rules get flushed immediately after insertion
iptables -I POLICYOUT 1 -o red0 ... ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 7 ... ACCEPT
iptables -F POLICYOUT ← silently wipes everything above
Fix: Always flush first, then add all rules.
3) DHCP renewal on RED was being blocked
DROP_POLICYOUT logs revealed OUT=red0 PROTO=UDP SPT=68 DPT=67 — the firewall’s own DHCP client traffic was blocked. Left unnoticed this would cause RED to lose its IP on lease expiry, taking down internet connectivity entirely.
Fix: Explicit DHCP ACCEPT scoped to -o red0 only.
4) WIO addon generates POLICYOUT traffic
The Who Is Online addon uses TCP echo (port 7) to probe hosts on GREEN and BLUE. This originates from the firewall itself and hits POLICYOUT. Completely legitimate — but unexpected if you haven’t seen it before.
Initial fix allowed port 7 to any destination including internet. Correct fix scopes it to internal subnets only:
iptables -I POLICYOUT 1 -p tcp --dport 7 -d 192.168.10.0/24 -j ACCEPT
iptables -I POLICYOUT 1 -p tcp --dport 7 -d 192.168.20.0/24 -j ACCEPT
5) LOG rate limiting requires -m limit before -j LOG
This fails:
-j LOG --log-prefix "DROP_POLICYOUT " --log-level 4 -m --limit 5/min
This works:
-m limit --limit 5/min --limit-burst 10 -j LOG --log-prefix "DROP_POLICYOUT " --log-level 4
The module declaration must precede the target.
6) Logs appear in dmesg, not /var/log/messages
Kernel iptables LOG at level 4 may not route to /var/log/messages depending on syslog configuration. Use dmesg | grep DROP_POLICYOUT for reliable access.
Notes
- Rules survive reboots as
firewall.localis called by IPFire on startup - POLICYOUT rules are invisible in the IPFire web GUI — SSH verification is the only way to confirm the chain
- Geographic blocking in IPFire is inbound only and does not interfere with firewall outbound traffic
- IPv6 is disabled by default in IPFire and was not enabled on this system — ip6tables POLICYOUT is therefore out of scope here. If you have IPv6 enabled treat this as a separate exercise
- Two different source IPs appear in the logs — the firewall uses its zone gateway IP (e.g. 192.168.10.1) for traffic toward internal zones and its RED IP for internet-bound traffic. Both are normal
- Guardian runs with zero issues, as far as I have been able to make out.
- If you run other addons that generate firewall-originated traffic, check dmesg after applying and whitelist as needed.
- Log entries are most reliably accessed via SSH using
dmesg | grep DROP_POLICYOUT. The IPFire web GUI Logs → System Logs → Firewall section may show these entries depending on your syslog configuration, but did not in testing. That configuration was not altered. SSH access is recommended for verifying POLICYOUT activity.
Tested on IPFire 2.29 (x86_64) Core-Update 199, three-zone GREEN/BLUE/RED, DHCP on RED, WIO addon installed.
ATTENTION!
This is an experiment. I have done what I can to verify that it works, but I am not a developer nor a network technician, having said that, I test and question - a lot. This was not done lightly, nor should anyone approach this with a careless copy-paste method.
I am sure some thing could be done differently and perhaps others are even wrong. That is why I post here. Open for debate and review.
Another thing: this will not be visible in the WUI. I tried to make rules in the Firewall UI to match this, but there seems to be no way to do so.






