Linux Networking Mastery Series Part 5: Firewalls and Packet Filtering

Words
562
Reading
3 min
Listen
Play
8M

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 processes
  • forward → packets being routed through the machine
  • output → packets originating from local processes
  • prerouting / 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 nftables

  • Fedora/RHEL: /etc/sysconfig/nftables.conf or 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

  1. 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 curl from another machine.
  2. firewalld zone switch (if on Fedora/RHEL family)

    • Move interface to drop zone → test connectivity
    • Move back to public, add ssh/http services
  3. 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.

Linux Networking Mastery Series Part 5: Firewalls and Packet Filter... | Ecency