Introduction
Linux firewalls form the cornerstone of network security for systems ranging from personal computers to enterprise infrastructure. For decades, iptables has been the go-to tool for packet filtering and network address translation on Linux. However, the landscape is evolving - nftables represents the modern successor with improved performance, cleaner syntax, and enhanced capabilities. Understanding both tools prepares you for both legacy systems and contemporary deployments.
This comprehensive guide covers packet filtering fundamentals, NAT configuration, rule management, and the transition from iptables to nftables. Whether you’re securing a single server or managing complex network infrastructure, these skills are essential for Linux professionals in 2026.
Understanding Packet Filtering
How Packet Filtering Works
Linux firewalls operate at various layers of the network stack, examining packets as they flow through the kernel. When a packet arrives, the firewall evaluates it against a set of rules organized into chains. Each rule specifies matching criteria (source IP, destination IP, port, protocol, etc.) and an action (accept, drop, reject, or modify).
The firewall processes rules sequentially, applying the first matching rule’s action. If no rules match, the chain’s default policy determines the outcome. This deterministic processing enables precise control over network traffic.
Tables and Chains
Linux firewalls use tables to organize rules by function, with each table containing specific chains:
filter Table: The primary table for packet filtering
- INPUT: Packets destined for local socket
- OUTPUT: Locally generated packets
- FORWARD: Packets being routed through the system
nat Table: Network Address Translation
- PREROUTING: Packets before routing decision
- OUTPUT: Locally generated packets
- POSTROUTING: Packets after routing decision
mangle Table: Packet modification
- PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
raw Table: Connection tracking exemptions
- PREROUTING, OUTPUT
Understanding this hierarchy is crucial - rules in different tables process packets at different stages, affecting how modifications and filtering interact.
iptables: The Classic Approach
Basic iptables Syntax
The iptables command follows this pattern:
iptables -A CHAIN -p PROTOCOL --dport PORT -j ACTION
Key flags:
-A: Append rule to chain-I: Insert rule (at position)-D: Delete rule-L: List rules-p: Protocol (tcp, udp, icmp)--dport: Destination port--sport: Source port-s: Source address-d: Destination address-j: Jump to target (ACCEPT, DROP, REJECT)
Building a Basic Firewall
Let’s construct a comprehensive firewall:
#!/bin/bash
# basic-firewall.sh - Basic iptables firewall script
# Flush existing rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# Set default policies - deny incoming, allow outgoing
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Allow loopback traffic
iptables -A INPUT -i lo -j ACCEPT
# Allow established and related connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH (be careful - change port if non-standard)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# Allow HTTP and HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow ping (ICMP) - optional, often disabled
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Log dropped packets
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-dropped: " --log-level 4
# Save rules
iptables-save > /etc/iptables/rules.v4
Connection Tracking
The connection tracking module (conntrack) is fundamental to stateful firewalls:
# Allow all established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow new connections only on specific ports
iptables -A INPUT -p tcp -m conntrack --ctstate NEW --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m conntrack --ctstate NEW --dport 443 -j ACCEPT
# Check connection tracking table
conntrack -L
conntrack -L -p tcp --dport 80
# Clear connection tracking (use with caution)
conntrack -F
Connection tracking maintains state for TCP connections and UDP “connections,” allowing the firewall to intelligently handle return traffic without explicit rules.
Port Scanning Protection
Detect and block port scans:
# Limit new connections per source
iptables -A INPUT -p tcp --tcp-flags ALL NONE -m limit --limit 5/min -j LOG --log-prefix "NULL scan: "
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL ALL -m limit --5/min -j LOG --log-prefix "XMAS scan: "
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
# SYN flood protection
iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -m recent --set
iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 10 -j DROP
# Rate limiting
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Network Address Translation (NAT)
NAT allows multiple devices to share a single public IP address:
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# Masquerading (NAT for outbound traffic)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Port forwarding (forward port 8080 to internal host)
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# Redirect port (redirect incoming HTTP to local proxy)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3128
# Source NAT (change source of outbound traffic)
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.10
Managing iptables Rules
# List rules with line numbers
iptables -L -n -v --line-numbers
# Delete specific rule
iptables -D INPUT 5
# Replace rule
iptables -R INPUT 3 -p tcp --dport 22 -j ACCEPT
# Save and restore
iptables-save > /etc/iptables/rules.v4
iptables-restore < /etc/iptables/rules.v4
# Persistent rules (Debian/Ubuntu)
apt install iptables-persistent
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
nftables: The Modern Successor
Why nftables?
nftables addresses several iptables limitations:
- Single framework for IPv4, IPv6, ARP, and bridge
- Faster packet classification with improved performance
- Cleaner, more consistent syntax
- Built-in atomic rule updates
- Better support for sets and maps
- Reduced kernel memory footprint
nftables Basics
The nft command structure differs from iptables:
# Basic syntax
nft add rule ip filter INPUT tcp dport 22 accept
nft add chain ip filter INPUT { policy drop \; }
Tables and Chains in nftables
Create tables and chains:
# Create filter table
nft add table ip filter
# Add chains with policies
nft add chain ip filter input '{ policy drop; }'
nft add chain ip filter forward '{ policy drop; }'
nft add chain ip filter output '{ policy accept; }'
# List all rules
nft list ruleset
# List specific table
nft list table ip filter
Building Rules with nftables
Construct comprehensive firewall rules:
#!/bin/bash
# nftables-basic.sh
# Flush existing rules
nft flush ruleset
# Create tables
nft add table ip filter
nft add table ip nat
nft add table ip6 filter
# Filter table chains
nft add chain ip filter input '{ policy drop; }'
nft add chain ip filter forward '{ policy drop; }'
nft add chain ip filter output '{ policy accept; }'
# NAT table chains
nft add chain ip nat prerouting '{ type nat hook prerouting priority dstnat; }'
nft add chain ip nat postrouting '{ type nat hook postrouting priority srcnat; }'
# Loopback
nft add rule ip filter input iif lo accept
# Established connections
nft add rule ip filter input ct state established,related accept
# SSH
nft add rule ip filter input tcp dport 22 ct state new accept
# HTTP/HTTPS
nft add rule ip filter input tcp dport { 80, 443 } ct state new accept
# Logging dropped packets
nft add rule ip filter input counter drop
# Save rules
nft list ruleset > /etc/nftables.conf
Using Sets and Maps
nftables powerful features include sets and maps:
# Create set of blocked IPs
nft add set ip filter blocklist '{ type ipv4_addr; flags dynamic; }'
# Add IP to blocklist
nft add element ip filter blocklist { 192.0.2.100 }
# Create map for port forwarding
nft add map ip nat portmap '{ type ipv4_addr . inet_service : ipv4_addr . inet_service; }'
# Use map in rule
nft add rule ip nat prerouting dnat to ip saddr . tcp dport map @portmap
nftables for NAT
# Masquerading
nft add rule ip nat postrouting oifname "eth0" masquerade
# Port forwarding
nft add rule ip nat prerouting iifname "eth0" tcp dport 8080 dnat to 192.168.1.100:80
# Source NAT
nft add rule ip nat postrouting saddr 192.168.1.0/24 oifname "eth0" snat to 203.0.113.10
Dynamic Blocklist Example
Implement dynamic blocking:
# Create dynamic set
nft add set ip filter attackers '{ type ipv4_addr; flags dynamic, timeout; }'
# Block repeated offenders
nft add rule ip filter input tcp dport 22 ct state new meter ssh-meter { ip saddr limit rate 10/minute } add @attackers { ip saddr timeout 1h } drop
# Allow legitimate traffic through blocklist
nft add rule ip filter input ip saddr @attackers drop
Comparison: iptables vs nftables
Syntax Comparison
| Task | iptables | nftables |
|---|---|---|
| List rules | iptables -L |
nft list ruleset |
| Accept TCP | -p tcp -j ACCEPT |
tcp dport 80 accept |
| Source NAT | -t nat -A POSTROUTING -j SNAT |
nat postrouting snat |
| Block IP | -A INPUT -s 1.2.3.4 -j DROP |
add rule ip filter input ip saddr 1.2.3.4 drop |
Migration from iptables
Debian and RHEL-based systems provide migration tools:
# Debian/Ubuntu - convert iptables rules to nftables
iptables-restore-translate -n < rules.v4 > rules.nft
# RHEL - use firewalld which supports nftables
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload
# Check backend
firewall-cmd --info-zone public
Advanced Techniques
IPv6 Firewall
Protect IPv6 with matching rules:
# Basic IPv6 protection
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# Or use nftables with ip6 tables
nft add table ip6 filter
nft add chain ip6 filter input '{ policy drop; }'
nft add rule ip6 filter input iif lo accept
nft add rule ip6 filter input ct state established,related accept
Container Networking
Firewall rules for Docker:
# Allow Docker bridge networking
iptables -A FORWARD -i docker0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Docker's internal rules (prevent interference)
# Add to /etc/docker/daemon.json
{
"iptables": true,
"ip-forward": false,
"userland-proxy": false
}
Fail2ban Integration
Fail2ban works with both frameworks:
# Check fail2ban status
fail2ban-client status
fail2ban-client status sshd
# Manual IP unban
fail2ban-client set sshd unbanip 192.168.1.100
# Custom jail for nftables in /etc/fail2ban/jail.local
[nginx-noscript]
enabled = true
filter = nginx-noscript
action = nftables[name=nginx, port=http, chain=input]
logpath = /var/log/nginx/access.log
maxretry = 10
Troubleshooting Firewall Issues
Debugging Commands
# Check what rules exist
nft list ruleset
iptables -L -n -v --line-numbers
# Monitor packets in real-time
nft monitor
tcpdump -i eth0 port 80
# Check connection states
conntrack -L
conntrack -L -p tcp --dport 80
# Test connectivity
nc -zv example.com 80
telnet example.com 80
# Check listening ports
ss -tulpn
netstat -tulpn
Common Issues
Can’t connect after firewall enable
# Check if SSH is allowed
iptables -L INPUT -n | grep 22
nft list chain ip filter input
# Temporarily allow SSH
iptables -I INPUT -p tcp --dport 22 -j ACCEPT
# Check if service is running
systemctl status sshd
ss -tlnp | grep :22
Port forwarding not working
# Enable IP forwarding
cat /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/ip_forward
# Make permanent
# /etc/sysctl.conf
net.ipv4.ip_forward = 1
# Check NAT rules
nft list table ip nat
iptables -t nat -L -n -v
Best Practices
Rule Organization
- Use separate chains for logical groups
- Place most specific rules first
- Default deny policy
- Log before dropping
- Document all rules
Performance
- Use connection tracking wisely
- Avoid unnecessary logging
- Use hashlimit for rate limiting
- Consider hardware offload
Security
- Regular rule audits
- Monitor logs for attacks
- Implement IDS/IPS
- Keep firewall updated
- Test changes before deployment
Maintenance
- Version control firewall rules
- Document rule changes
- Regular backups
- Automated deployment
- Disaster recovery plan
Conclusion
Mastering Linux firewalls requires understanding both iptables and nftables, as legacy systems continue running iptables while new deployments increasingly adopt nftables. The fundamental concepts - stateful inspection, packet filtering, NAT, and connection tracking - transfer between both tools.
Start with simple rules and gradually add complexity. Always maintain console access when testing firewall changes. Use scripts to manage rules and implement version control. With practice, you’ll be able to implement sophisticated network security policies that protect your infrastructure while enabling legitimate traffic.
Whether you choose iptables for compatibility or nftables for modern features, the principles remain the same: default deny, allow explicitly, log suspicious activity, and continuously monitor and refine your rules.
Comments