DIY Debian Router - Part 6: Secure firewall with nftables
Introduction
This is Part 6 of the DIY Debian Router series. See Part 1 for the series introduction and links to all parts.
The firewall enforces the security boundary between untrusted WAN (Internet) and trusted LAN (internal network). This part covers the implementation of stateful packet filtering using nftables, the modern Linux firewall framework.
We'll cover:
- Default-deny ingress on WAN (only established/related traffic permitted)
- Stateful connection tracking for IPv4 and IPv6
- Anti-spoofing via bogon filtering and reverse path filtering
- ICMP/ICMPv6 rate limiting for DoS mitigation
- IPv4 NAT/masquerading for outbound Internet access
- Connection tracking optimization for performance
Firewall architecture overview
The ruleset is organized into multiple tables and chains:
Tables
inet raw(priority: raw): Connection tracking bypass for performanceinet filter(priority: filter): Stateful packet filtering (forward, input, output chains)ip nat(priority: srcnat/dstnat): IPv4 Network Address Translation
Chains
forward: Filters traffic traversing the router (LAN ↔ WAN)input: Filters traffic destined for the router itself (management, DNS, DHCP)output: Filters traffic originating from the router (minimal restrictions)postrouting(NAT): Source NAT (masquerading) for IPv4 LAN trafficprerouting(NAT): Destination NAT (port forwarding, not configured by default)
Complete firewall configuration
Create /etc/nftables.conf:
#!/usr/sbin/nft -f
# Home router firewall configuration
################## Configuration variables ##################
# Bogon/martian addresses that should never appear from WAN
################## Connection tracking optimization ##################
################## Main firewall table ##################
################## NAT Table (IPv4 only) ##################
################## Management Notes ##################
#
# Apply this configuration:
# nft -f /etc/nftables.conf
#
# Save current ruleset:
# nft list ruleset > /etc/nftables.conf
#
# Monitor live traffic:
# nft monitor
#
# List ruleset with handles:
# nft list ruleset -a
#
# Delete specific rule:
# nft delete rule inet filter input handle <number>
#
# Enable at boot (systemd):
# systemctl enable --now nftables.service
#
# Rate limit configuration notes:
# - ICMP rate limit: 5/second (prevents ping floods)
# - SYN rate limit: 100/second burst 200 (prevents SYN floods)
# - Adjust these values based on your WAN bandwidth and threat model
#
################## End Configuration ##################
Key configuration aspects:
- Default-deny forward policy: Transit traffic blocked unless explicitly permitted; only established/related connections and LAN-initiated outbound traffic allowed
- WAN anti-spoofing protection: Bogon filtering drops packets from WAN with internal private addresses, loopback, link-local, or LAN source addresses
- Stateful connection tracking: All chains prioritize established/related traffic first, with invalid states dropped immediately to block malformed packets
- LAN trust boundary: Traffic from
br0fully trusted for router services (DNS, DHCP, SSH), based on assumption that LAN is secure - ICMP rate limiting: WAN ping requests limited to 5/second; essential types (destination-unreachable, time-exceeded) and PMTU Discovery always permitted
- ICMPv6 Neighbor Discovery: Link-local NDP messages (router-advert, neighbor-solicit) and packet-too-big allowed to prevent IPv6 breakage
- SYN flood mitigation: TCP SYN packets from WAN limited to 100/second with 200-packet burst to prevent connection table exhaustion
- Connection tracking bypass: Raw table optimization uses
notrackfor established connections, reducing CPU usage on high-throughput links - IPv4 masquerading: NAT postrouting translates LAN private addresses to WAN IP; IPv6 operates without NAT using stateful filtering only
- Port forwarding framework: DNAT prerouting chain configured for selective service exposure (disabled by default; requires corresponding forward chain rules)
Enable and start nftables:
Verify ruleset is loaded:
Test firewall functionality:
# From LAN client, test Internet access:
# From router, test DNS:
# From external host, test WAN is blocked:
Should prove that LAN and router have Internet access, but external ports are blocked (shows all ports filtered).
Testing and verification
Connection tracking table
View active connections using conntrack:
This displays all tracked connections (TCP, UDP, ICMP). Example output:
tcp 6 431999 ESTABLISHED src=192.168.0.10 dst=1.1.1.1 sport=54321 dport=443 ...
ICMP rate limiting test
From an external host, flood the router with pings:
Monitor firewall logs (if logging enabled):
|
Initial pings should succeed (within rate limit), subsequent pings should be dropped.
Troubleshooting common issues
All traffic blocked after applying firewall
Symptom: LAN clients lose Internet access.
Diagnosis:
-
Verify nftables loaded correctly:
-
Check forward chain rules exist:
-
Temporarily flush ruleset for testing:
If Internet works with ruleset flushed, the configuration has errors.
Common issues:
- Incorrect interface names in
definevariables - Missing
ct state established,related acceptrule - Incorrect chain priorities
DNS queries fail from router
Symptom: dig fails on router, but works on LAN clients.
Cause: Output chain blocks DNS responses.
Fix: Ensure output chain has policy accept and allows established connections.
IPv6 connectivity broken
Symptom: IPv6 works until firewall is applied.
Cause: ICMPv6 Neighbor Discovery is blocked.
Fix: Ensure ICMPv6 rules (ND, packet-too-big) are present in input chain.
Connection tracking table full
Symptom: New connections fail with "nf_conntrack: table full" in dmesg.
Cause: Connection tracking table exhausted (default: 65536 entries).
Fix: Increase nf_conntrack_max, see Part 7.
Additional firewall techniques
Logging dropped packets
To log dropped packets for forensics:
# In input chain, before final drop:
View logs:
|
Warning: Logging drops can generate massive log files under attack. Always rate-limit logging.
Port knocking
Implement SSH access control via port knocking (knock on specific ports in sequence to open SSH):
# Define a set to track knocking state
# Knock sequence: TCP 1234, then SSH allowed for 30s
This is a simplified example; secure port knocking requires more complex state tracking.
GeoIP blocking
Block traffic from specific countries using nftables sets and GeoIP databases:
# Create set of IP ranges for specific country
# Drop traffic from blocked country
GeoIP databases (e.g., MaxMind GeoLite2) require regular updates.
Performance considerations
Connection tracking overhead
Connection tracking (conntrack) is CPU-intensive. For routers handling >1 Gbps throughput with >10,000 concurrent connections, consider:
- Increasing conntrack table size (covered in Part 7)
- Using raw table notrack optimization (already implemented)
- Offloading to hardware (if NIC supports conntrack offload)
Ruleset optimization
nftables evaluates rules sequentially. Place most common rules first:
# Most traffic is established/related:
# Less common:
Use sets for large lists (e.g., bogons) instead of individual rules for O(1) lookup performance.
Security best practices
Principle of Least Privilege
Only permit necessary traffic. The reference configuration:
- WAN input: Only DHCP client and essential ICMP
- LAN input: Trusted (all traffic allowed)
- Forward: LAN-to-WAN and established/related only
For higher security, apply per-service rules even on LAN (e.g., only permit DNS, DHCP).
Regular ruleset audits
Periodically review firewall rules to remove unnecessary exceptions:
|
Attack surface minimization
The router exposes no services on WAN by default (no SSH, HTTP, etc.). To further reduce attack surface:
- Disable unused network protocols (if not using IPv6, disable it entirely)
- Run services as unprivileged users (DNS, DHCP)
- Apply AppArmor/SELinux policies (not covered in this series)
Next steps
With the firewall working, the router enforces security policies for all traffic. Part 7 continues the series by taking a look at kernel tuning via sysctl, covering IP forwarding, TCP stack hardening, DoS mitigation, and performance optimization.