Linux Firewall — iptables & nftables Complete Guide
In this tutorial, you'll learn about Linux Firewall. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
iptables and nftables are Linux kernel packet filtering frameworks that control network traffic, implement NAT, and protect servers from unauthorized access at the network layer.
What You'll Learn
How to write iptables and nftables rules for packet filtering, port forwarding, connection tracking, Rate Limiting, and logging, plus a practical Migration Strategy from iptables to nftables on modern distributions.
Why Firewalls Matter
Every public-facing server is scanned by bots within minutes. A properly configured firewall is the first line of defense — it drops unwanted traffic before it reaches your services. Understanding raw packet filtering also lets you build complex NAT configurations, VPN routing, and traffic shaping rules that GUI tools cannot express. Doda Browser's cloud infrastructure uses nftables for all edge firewall rules.
Learning Path
flowchart LR A[Network Commands] --> B[SSH & Remote Access] B --> C[iptables & nftables
You are here] C --> D[Network Bonding] C --> E[Server Hardening] style C fill:#f90,color:#fff
iptables Architecture
iptables organizes rules into tables (filter, nat, mangle, raw) and chains (INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING). Each packet traverses a specific path depending on its destination.
flowchart LR
subgraph Incoming
A[Packet In] --> B[PREROUTING]
B --> C{Routing Decision}
end
C -->|Local Process| D[INPUT]
C -->|Forward| E[FORWARD]
D --> F[Local Process]
F --> G[OUTPUT]
E --> H[POSTROUTING]
G --> H
H --> I[Packet Out]
Default Policies and Rule Chains
# Set default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Flush existing rules
sudo iptables -F
sudo iptables -X
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -j ACCEPT
# Log dropped packets
sudo iptables -A INPUT -j LOG --log-prefix "iptables-dropped: " --log-level 4
# Save rules
sudo iptables-save > /etc/iptables/rules.v4
Expected output of sudo iptables -L -v -n:
Chain INPUT (policy DROP 42 packets, 3120 bytes)
pkts bytes target prot opt in out source destination
42 3120 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
1234 987K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
12 720 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW
56 3360 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443 ctstate NEW
3 180 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix "iptables-dropped: "
NAT with iptables
# Source NAT (masquerade) — share one public IP
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Destination NAT (port forwarding) — forward port 8080 to internal server
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.0.1.100:80
# Allow forwarded traffic
sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate NEW -j ACCEPT
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Rate Limiting
# Limit SSH to 10 new connections per minute per IP
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m recent --update --seconds 60 --hitcount 10 --name SSH -j DROP
# Limit ICMP echo requests
sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/second -j ACCEPT
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
nftables — The Modern Replacement
nftables is the successor to iptables, integrated into the kernel since 2014. It uses a single nft command with a simpler syntax, atomic rule replacement, and better performance.
Basic nftables Configuration
# Flush all rules and create a basic firewall
sudo nft flush ruleset
sudo nft add table inet filter
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; }'
# Allow loopback
sudo nft add rule inet filter input iif lo accept
# Allow established connections
sudo nft add rule inet filter input ct state established,related accept
# Allow SSH
sudo nft add rule inet filter input tcp dport 22 ct state new accept
# Allow HTTP/HTTPS
sudo nft add rule inet filter input tcp dport '{ 80, 443 }' ct state new accept
# Allow ICMP
sudo nft add rule inet filter input icmp type '{ echo-request, echo-reply }' limit rate 5/second accept
# Log and drop
sudo nft add rule inet filter input log prefix 'nftables-drop ' drop
nftables Configuration File
sudo tee /etc/nftables.conf << 'EOF'
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
tcp dport 22 ct state new accept
tcp dport { 80, 443 } ct state new accept
icmp type { echo-request, echo-reply } limit rate 5/second accept
log prefix "nftables-drop " drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority -100;
tcp dport 8080 dnat to 10.0.1.100:80
}
chain postrouting {
type nat hook postrouting priority 100;
oif eth0 masquerade
}
}
EOF
sudo systemctl enable --now nftables
nftables Sets and Maps
Sets reduce rule duplication:
# Define a set of allowed ports
sudo nft add set inet filter allowed_ports '{ type inet_service; elements = { 22, 80, 443, 8443 }; }'
# Use the set in a rule
sudo nft add rule inet filter input tcp dport @allowed_ports ct state new accept
# Add to a set dynamically
sudo nft add element inet filter allowed_ports '{ 8080 }'
Atomic Rule Replacement
Unlike iptables, nftables replaces the entire ruleset atomically — no gap where the firewall is down:
# Update the config file, then apply atomically
sudo nft -f /etc/nftables.conf
Migrating from iptables to nftables
# Convert iptables rules to nftables syntax
sudo iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# nft add rule ip filter INPUT tcp dport 22 accept
# Translate entire ruleset
sudo iptables-save > /tmp/rules.txt
sudo iptables-restore-translate -f /tmp/rules.txt > /tmp/rules.nft
# Use the iptables-nft compatibility layer
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
Common Errors
1. Firewall Rules Applied Over SSH Drop Current Session
Adding a default-policy DROP on INPUT without first allowing established traffic disconnects you. Always add the conntrack rule first, or use at now + 5 minutes <<< "iptables -P INPUT ACCEPT" as a safety timer.
2. IPv6 Ignored
iptables and nftables rules for IPv4 do not apply to IPv6. Use ip6tables or add meta nfproto ipv6 matches in nftables.
3. Rules Lost After Reboot
iptables rules are ephemeral. Save with iptables-save > /etc/iptables/rules.v4 and install iptables-persistent. nftables config in /etc/nftables.conf loads automatically if the service is enabled.
4. Port Forwarding Not Working
Check three things: net.ipv4.ip_forward=1 must be set, the DNAT rule must be in the PREROUTING chain, and the FORWARD chain must allow the traffic. Enable forwarding: sudo sysctl -w net.ipv4.ip_forward=1.
5. Conflicting Rules from Docker
Docker manipulates iptables directly. If you restart the firewall, Docker networking breaks. Use nftables with the iptables-nft compat layer or configure Docker to use a dedicated bridge.
6. Rate Limiting Too Aggressive
Setting rate limits too low blocks legitimate traffic. Start with generous limits (10/second) and monitor logs before tightening.
7. Misordered Rules
iptables processes rules in order. A blanket REJECT before a specific ALLOW drops everything. List rules with iptables -L --line-numbers and insert with -I instead of -A.
Practice Questions
1. What is the difference between iptables filter and nat tables? The filter table handles packet filtering (allow/deny). The nat table modifies source or destination addresses for routing (MASQUERADE, DNAT).
2. How do you allow established SSH sessions to keep working after reloading the firewall?
Add a rule matching -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT before the default DROP policy.
3. What nftables feature replaces iptables chains for grouping rules?
nftables uses named chains within tables. Built-in chains have type filter hook declarations. User-defined chains can be called with goto or jump.
4. How do you make iptables rules persistent across reboots on Ubuntu?
Install iptables-persistent: sudo apt install iptables-persistent && sudo netfilter-persistent save.
5. What command translates iptables rules to nftables syntax?
iptables-translate for single rules, iptables-restore-translate for saved rulesets.
Challenge: Set up an nftables firewall on a dual-homed server (public IP on eth0, private LAN on eth1). Configure: (1) SSH access from anywhere, (2) web traffic to port 80/443, (3) masquerade for LAN clients, (4) port forwarding from host port 8443 to internal server 10.0.1.50:443, (5) rate-limited ICMP. Test with nft list ruleset and curl.
What's Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro