Welcome back to Linux Networking Mastery!
In previous parts we built a strong foundation:
- Part 1 – network stack basics and inspection tools
- Part 2 – interface and IP configuration (temporary + persistent via Netplan, nmcli, systemd-networkd)
- Part 3 – routing tables, static/policy routing, namespaces, simple router setup
- Part 4 – name resolution, systemd-resolved, per-link/global DNS, troubleshooting
Now we add a critical security layer: firewalls.
We'll focus on the modern nftables framework (the successor to iptables), look at higher-level front-ends (firewalld on RHEL/Fedora family and ufw on Ubuntu/Debian), write practical rules for common services, and understand stateful connection tracking.
By the end you'll be able to secure a server, allow only necessary traffic, and understand the transition away from legacy iptables.
From iptables to nftables – Why the Change?
iptables (and ip6tables, arptables, ebtables) served Linux for ~20 years but had serious limitations:
- Multiple separate tables/chains per protocol/family
- Difficult rule ordering and complex matching
- Performance issues with very large rulesets
- No atomic updates (rules applied one-by-one)
nftables (kernel 3.13+, userspace since ~2014) replaces all of them with:
- Single framework, unified syntax
- Tables → chains → rules
- Sets, maps, verdicts, concatenations, incremental updates
- Better performance and atomic rule-set replacement
- Native IPv4 + IPv6 + bridge + netdev support
Most distributions switched as default in 2019–2022:
- Fedora → nftables default since Fedora 32 (2020)
- Ubuntu → nftables backend default since 22.04 (2022), though ufw still uses iptables-nft compatibility layer by default
- Debian → nftables default since Debian 11 (bullseye, 2021) in many contexts
iptables-nft / ip6tables-nft provide a compatibility layer so old iptables commands still work (they translate to nft).
Working Directly with nftables
Basic Structure
table inet myfilter { # inet = IPv4 + IPv6
chain input {
type filter hook input priority 0; policy drop;
# rules here
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Common hooks:
input→ packets to local processesforward→ packets being routed through the machineoutput→ packets originating from local processesprerouting/postrouting→ used in nat table
Quick Practical Example – Minimal Secure Server
Allow established/related, loopback, SSH, HTTP/HTTPS, drop everything else:
#!/usr/bin/env bash
# Reset to clean state
sudo nft flush ruleset
sudo nft -f - <<EOF
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept comment "Allow return traffic"
ct state invalid drop
iifname "lo" accept comment "Loopback"
tcp dport 22 ct state new accept comment "SSH"
tcp dport { 80, 443 } ct state new accept comment "Web"
# Optional: allow ping (careful in production)
icmp type echo-request limit rate 5/second accept
# Log dropped packets (optional, noisy)
# log prefix "DROPPED: " drop
}
chain forward {
type filter hook forward priority 0; policy drop;
# If this is not a router → keep drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}
EOF
Apply: sudo chmod +x firewall.sh && sudo ./firewall.sh
List rules:
sudo nft list ruleset
Persistent nftables Rules
Debian/Ubuntu: save with
sudo nft list ruleset > /etc/nftables.conf
Enable:sudo systemctl enable nftablesFedora/RHEL:
/etc/sysconfig/nftables.confor use firewalld (recommended)
Higher-Level Front-Ends
firewalld (default on Fedora, RHEL, CentOS Stream, Rocky, AlmaLinux)
Zone-based firewall – very convenient for servers with changing roles.
Common zones: public, internal, trusted, drop, block, home, work, dmz, external
Quick examples:
# See active zone
sudo firewall-cmd --get-active-zones
# Allow SSH on public interface permanently
sudo firewall-cmd --permanent --zone=public --add-service=ssh
sudo firewall-cmd --reload
# Allow web server
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Rich rule example: allow SSH only from specific IP
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.42" service name="ssh" accept'
# Port forward 8080 → internal 80
sudo firewall-cmd --permanent --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.100.50
# Masquerade (NAT) for LAN clients
sudo firewall-cmd --permanent --zone=external --add-masquerade
List everything: sudo firewall-cmd --list-all-zones
ufw (Uncomplicated Firewall) – Ubuntu/Debian favorite
Very simple interface over iptables-nft (or pure nftables in newer versions).
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh # or 22/tcp
sudo ufw allow http
sudo ufw allow https
sudo ufw allow from 203.0.113.0/24 to any port 3306 proto tcp comment 'MySQL from office'
# Rate limiting
sudo ufw limit ssh
# See status with numbers
sudo ufw status numbered
# Delete rule by number
sudo ufw delete 3
# Enable (careful – make sure SSH is allowed first!)
sudo ufw enable
Connection Tracking and Stateful Filtering
Both nftables and firewalld/ufw use the conntrack (connection tracking) module.
Key states:
- NEW
- ESTABLISHED
- RELATED
- INVALID
Rule of thumb for servers:
- Allow ESTABLISHED,RELATED early
- Allow NEW only for services you want to expose
- Drop INVALID
Hands-On Exercises
nftables minimal firewall
- Flush current ruleset
- Apply the example script above (adjust ports)
- Test: can you still SSH? Can external hosts reach a web server? Try
curlfrom another machine.
firewalld zone switch (if on Fedora/RHEL family)
- Move interface to
dropzone → test connectivity - Move back to
public, add ssh/http services
- Move interface to
ufw rate-limit (Ubuntu/Debian)
- Enable ufw with SSH allowed
- Add rate limit on SSH
- Try many rapid SSH attempts from another host
Safety warning: Always allow SSH (or have console/rescue access) before enabling strict policies!
What's Next?
In Part 6 we will configure and harden common network services: SSH server (keys, no password, fail2ban basics), lightweight web servers (Nginx/Apache), file sharing (NFS and Samba), and a simple DHCP server.