Mastering Linux Firewalls: Core Netfilter Concepts and Practical iptables/firewalld Configuration
This guide walks Linux operators through the fundamentals of Netfilter, explains iptables tables, chains and rule‑matching order, and provides step‑by‑step commands for configuring, saving, and troubleshooting iptables, firewalld and ufw in production environments.
Problem Background
Linux firewalls protect servers in data centers and cloud environments. Misconfiguration can cause closed ports, exposed services, rule‑order errors, or accidental lock‑out of remote access.
Netfilter Fundamentals
Netfilter is a kernel module that processes packets at five hook points: PREROUTING, FORWARD, INPUT, OUTPUT, and POSTROUTING. Front‑ends such as iptables, firewalld and ufw configure Netfilter rules.
PREROUTING → routing decision before DNAT
FORWARD → forwarded traffic (not local)
INPUT → packets destined for the host
OUTPUT → packets generated by the host
POSTROUTING → routing decision after SNATiptables Tables and Chains
iptables organizes rules into tables and chains: filter: default table for ACCEPT/DROP. nat: DNAT/SNAT. mangle: modify TOS, TTL, etc. raw: bypass connection tracking.
filter table: INPUT, FORWARD, OUTPUT
nat table: PREROUTING, INPUT, OUTPUT, POSTROUTING
mangle table: PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
raw table: PREROUTING, OUTPUTRule Matching Order
iptables evaluates rules sequentially; the first matching rule determines the packet fate. If no rule matches, the chain’s default policy is applied. Therefore an “allow SSH” rule must appear before any reject rules.
iptables Basic Operations
2.1 View Current Rules
# List filter table rules
sudo iptables -L -n -v
# Show a specific chain
sudo iptables -L INPUT -n -v
# Show rule numbers for deletion
sudo iptables -L INPUT -n --line-numbers2.2 Basic Syntax
# Append rule
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Delete rule by specification
sudo iptables -D INPUT -p tcp --dport 22 -j ACCEPT
# Insert rule at the top
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
# Flush a chain
sudo iptables -F INPUT
# Set default policy
sudo iptables -P INPUT DROP2.3 Matching Conditions
# Source IP
sudo iptables -A INPUT -s 192.168.1.100 -j ACCEPT
# Destination port (protocol must be specified first)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# Multi‑port
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT
# Connection state
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# ICMP echo request
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT2.4 Rule Operations
# Append, insert, delete, replace, flush, set policy (examples shown above)2.5 Save and Restore
# CentOS/RHEL
sudo yum install -y iptables-services
sudo iptables-save > /etc/sysconfig/iptables
sudo iptables-restore < /etc/sysconfig/iptables
sudo systemctl enable iptables
# Ubuntu
sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save
sudo iptables-save > /etc/iptables/rules.v4Production‑Level iptables Templates
3.1 Minimal Safe Rules
#!/bin/bash
# WARNING: Ensure console access before running!
sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
# Default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Loopback
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
# Established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# SSH (replace with your IPs)
ALLOWED_SSH_IPS="192.168.1.100"
for ip in $ALLOWED_SSH_IPS; do
sudo iptables -A INPUT -p tcp -s $ip --dport 22 -j ACCEPT
done
# HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Optional ICMP
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Save rules
sudo iptables-save > /etc/sysconfig/iptables3.2 Database Port Access
# MySQL from app servers
sudo iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 3306 -j ACCEPT
# Redis (no password by default)
sudo iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 6379 -j ACCEPT
# PostgreSQL
sudo iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 5432 -j ACCEPT
# MongoDB
sudo iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 27017 -j ACCEPT3.3 Restrict SSH Sources
# Allow a single IP
sudo iptables -A INPUT -p tcp -s 192.168.1.100 --dport 22 -j ACCEPT
# Allow a subnet
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT3.4 SYN Flood Mitigation
# Enable SYN cookies
sudo sysctl -w net.ipv4.tcp_syncookies=1
# Limit SYN backlog
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# Rate‑limit new connections on port 80
sudo iptables -A INPUT -p tcp --dport 80 -m state --state NEW -m recent --set --name SYN_FLOOD
sudo iptables -A INPUT -p tcp --dport 80 -m state --state NEW -m recent --update --seconds 10 --count 100 --rttl --name SYN_FLOOD -j DROP
# Limit concurrent connections per IP
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP
sudo iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j DROP3.5 NAT and Port Forwarding
# Enable IP forwarding (temporary)
sudo sysctl -w net.ipv4.ip_forward=1
# Permanent
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# SNAT (static public IP)
sudo iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.10
# MASQUERADE (dynamic IP)
sudo iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o eth0 -j MASQUERADE
# DNAT: public 8080 → internal 10.0.1.100:80
sudo iptables -t nat -A PREROUTING -p tcp -d 203.0.113.10 --dport 8080 -j DNAT --to-destination 10.0.1.100:80
sudo iptables -A FORWARD -d 10.0.1.100 -p tcp --dport 80 -j ACCEPT
# Save NAT rules
sudo iptables-save > /etc/sysconfig/iptablesfirewalld Basics (CentOS/RHEL 7+)
4.1 Concepts
firewalld introduces Zones (trust levels) and Services (pre‑defined port/protocol groups).
drop # drop all traffic
block # reject with ICMP
public # untrusted network, limited ports
external # NAT/MASQUERADE
dmz # SSH only
work # higher trust
home # home network
internal # internal network
trusted # allow everything ssh → 22/TCP
http → 80/TCP
https → 443/TCP
mysql → 3306/TCP4.2 Common Commands
# Check status
sudo firewall-cmd --state
# List default zone
sudo firewall-cmd --get-default-zone
# List active zones
sudo firewall-cmd --get-active-zones
# Show rules for a zone
sudo firewall-cmd --zone=public --list-all
# List services and icmp types
sudo firewall-cmd --get-services
sudo firewall-cmd --get-icmptypes4.3 Adding Rules
# Temporary port open (lost after reboot)
sudo firewall-cmd --add-port=8080/tcp
# Permanent port open
sudo firewall-cmd --permanent --add-port=8080/tcp
# Add a service
sudo firewall-cmd --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Rich rule – allow specific IP on SSH
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port port="22" protocol="tcp" accept'
# Reload to apply permanent changes
sudo firewall-cmd --reload4.4 Zone Configuration Example
# Assign interface to trusted zone
sudo firewall-cmd --zone=trusted --change-interface=eth0
# Set default zone to trusted
sudo firewall-cmd --set-default-zone=trusted
# Add services to work zone
sudo firewall-cmd --zone=work --add-service=ssh
sudo firewall-cmd --zone=work --add-service=https
# Set public zone default target to DROP
sudo firewall-cmd --zone=public --set-target=DROP
sudo firewall-cmd --zone=public --add-service=ssh
sudo firewall-cmd --zone=public --add-service=http4.5 firewalld NAT
# Enable masquerade on external zone
sudo firewall-cmd --permanent --zone=external --add-masquerade
# Forward external port 8080 to internal 10.0.1.100:80
sudo firewall-cmd --permanent --zone=external --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=10.0.1.100
# Reload and verify
sudo firewall-cmd --reload
sudo firewall-cmd --zone=external --list-allufw Basics (Ubuntu)
5.1 Commands
# Show status
sudo ufw status verbose
# Enable / disable
sudo ufw enable
sudo ufw disable
# List numbered rules
sudo ufw status numbered
# Delete a rule
sudo ufw delete <rule_number>5.2 Rule Management
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (or specific IP)
sudo ufw allow ssh
sudo ufw allow from 192.168.1.100 to any port 22
# Allow HTTP/HTTPS
sudo ufw allow http
sudo ufw allow https
# Deny an IP
sudo ufw deny from 1.2.3.4
# Rate‑limit SSH (5 attempts per 5 minutes)
sudo ufw limit 22/tcp
# Reset all rules
sudo ufw resetCommon Troubleshooting
6.1 Locked Out of SSH
# List INPUT rules with line numbers
sudo iptables -L INPUT -n --line-numbers
# Add a temporary SSH rule via console
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
# For firewalld, add SSH service to a trusted zone
sudo firewall-cmd --zone=trusted --add-interface=eth0
sudo firewall-cmd --zone=public --add-service=ssh6.2 Rule Order Errors
# Example of a REJECT rule placed before ACCEPT
# Fix by deleting the REJECT and inserting ACCEPT earlier
sudo iptables -D INPUT 1
sudo iptables -I INPUT 2 -p tcp --dport 22 -j ACCEPT
# For firewalld, remove the offending rich rule
sudo firewall-cmd --list-rich-rules
sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="0.0.0.0/0" reject'
sudo firewall-cmd --reload6.3 DNS Breakage
# Allow DNS traffic (UDP 53)
sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 53 -j ACCEPT6.4 Port Still Inaccessible
# Verify rule exists
sudo iptables -L INPUT -n | grep <port>
# Check service is listening
sudo ss -tunapl | grep <port>
# Verify default chain policy
sudo iptables -L | grep policy
# Check packet counters (pkts column)
sudo iptables -L INPUT -n -v
# Capture packets
sudo tcpdump -i eth0 port <port>Production‑Level Firewall Management Best Practices
Set default‑deny policies for INPUT and FORWARD chains.
Open only required ports (least privilege).
Restrict source IPs for privileged services such as SSH.
Place allow rules before deny rules.
Allow ESTABLISHED,RELATED connections to reduce rule count.
Log dropped traffic for audit and attack detection.
Backup rules before changes, verify after applying, and roll back if needed.
Tool Selection
CentOS 7/8, RHEL 7/8 – use firewalld.
Ubuntu – use ufw.
Debian, older CentOS – use raw iptables.
Conclusion
Understanding Netfilter’s tables, chains, and rule order is essential for reliable firewall configuration. By following the step‑by‑step commands, applying a default‑deny stance with minimal open ports, and using systematic backup and audit scripts, operators can secure Linux servers while avoiding common pitfalls such as accidental lock‑out or unintended traffic exposure.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Community
A leading IT operations community where professionals share and grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
