The Linux kernel’s Netfilter framework forms the underlying packet processing and stateful inspection architecture for virtually all Linux network security appliances, container virtual switches, and standalone server firewalls. For over two decades, systems engineers interfaced with Netfilter through the iptables command suite. However, contemporary enterprise distributions (including Debian, Ubuntu 22.04/24.04 LTS, and RHEL 9) have transitioned standard packet filtering infrastructure to nftables (invoked via the nft utility).
While iptables relied on separate binaries for IPv4 (iptables), IPv6 (ip6tables), and ARP (arptables) with fixed table chains, nftables consolidates multi-protocol packet inspection into a single runtime environment with configurable tables, atomic rule updates, and significantly higher packet evaluation throughput. This technical guide covers packet filtering fundamentals, compares syntax differences between legacy iptables and modern nftables, and provides production-grade firewall configuration templates.
Architectural Comparison: iptables vs. nftables
Understanding how the two command suites interface with the Linux kernel Netfilter hooks clarifies why modern Linux distributions default to nftables:
| Feature / Attribute | iptables |
nftables (nft) |
|---|---|---|
| Protocol Handling | Fragmented across separate binaries (iptables, ip6tables, arptables, ebtables). |
Unified single binary handling IPv4, IPv6, ARP, and bridge frames under the inet family. |
| Table Structure | Fixed, hardcoded kernel tables (filter, nat, mangle, raw). |
Completely customizable tables and chains created by the administrator. |
| Rule Updating | Rule replacements flush and reload entire monolithic tables. | Atomic rule addition, modification, and deletion without table re-initialization. |
| Performance | Linear rule scanning causing latency degradation with large rule counts. | Internal bytecode compilation using fast dictionary sets and interval lookups. |
Essential Packet Filtering with Legacy iptables
Even in modern systems, backward-compatible translation layers (iptables-nft) keep legacy syntax relevant for legacy shell scripts and container daemons like Docker.
Inspecting Active Firewall Rules
To list all active rules across the standard filter table with packet and byte counters:
sudo iptables -L -n -v --line-numbers
Establishing Default Policies (Deny-by-Default)
A resilient firewall drops unsolicited ingress packets while allowing outbound client connections:
# Set default chain policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Allow established and related return traffic
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow local loopback interface traffic
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow inbound SSH on port 22
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
Persisting iptables Across Reboots
Rules created via iptables reside purely in volatile kernel memory. On Debian/Ubuntu systems, install and invoke the persistence package:
sudo apt install iptables-persistent
sudo netfilter-persistent save
Modern Packet Filtering with nftables (nft)
The nft command provides a cleaner syntax that eliminates redundant protocol flags and allows multi-port ranges within a single statement.
Listing the Complete nftables Ruleset
To view all active tables, chains, and rules currently evaluated by the kernel:
sudo nft list ruleset
Creating a Production Firewall with nftables
Create a secure baseline configuration that blocks incoming probes, authorizes loopback communications, and allows inbound web and SSH traffic:
# 1. Create a unified IPv4/IPv6 table
sudo nft add table inet filter
# 2. Add standard base chains linked to Netfilter kernel hooks
sudo nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
sudo nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
sudo nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
# 3. Allow loopback traffic
sudo nft add rule inet filter input iifname "lo" accept
# 4. Accept established and related sessions
sudo nft add rule inet filter input ct state established,related accept
# 5. Drop invalid packets
sudo nft add rule inet filter input ct state invalid drop
# 6. Permit inbound SSH (22), HTTP (80), and HTTPS (443) using a set
sudo nft add rule inet filter input tcp dport { 22, 80, 443 } ct state new accept
# 7. Permit ICMP ping with rate limiting
sudo nft add rule inet filter input ip protocol icmp icmp type echo-request limit rate 5/second accept
Translating Legacy iptables Rules to nftables
Administrators migrating legacy bash scripts can convert complex rule definitions using the native iptables-translate utility:
# Translate an iptables command to nft syntax
iptables-translate -A INPUT -p tcp --dport 8080 -j ACCEPT
The console prints the equivalent native nft statement:
nft add rule ip filter INPUT tcp dport 8080 counter accept
Persisting and Loading Configuration Files
In modern distributions, nftables loads its ruleset on boot from /etc/nftables.conf:
- Export the active, working ruleset to the configuration file:
sudo nft list ruleset | sudo tee /etc/nftables.conf - Enable the
systemdservice to ensure rules are injected before network interfaces initialize during system startup:sudo systemctl enable --now nftables - To test syntax validity when authoring rules manually inside
/etc/nftables.confbefore loading:sudo nft -c -f /etc/nftables.conf