← News
/PLAYBOOK · 2026-07-29

iptables Firewall Rules Hands-on Configuration Guide: Linux Server Hardening

iptables is the Linux kernel native packet-filter firewall. This guide covers default policies, port allowlists, SSH source restrictions, port-scan defense, connection limits, persistence, and recovery.

iptables is the Linux kernel native packet-filter firewall. This guide covers default-deny policy, port allowlists, SSH source restrictions, port-scan defense, connection limits, persistence, and lockout recovery.

Applicable Scenarios

For admins who need iptables on a Linux server. Web, database, or API gateway—iptables is in-kernel, zero extra deps, fine-grained traffic control. Typical uses: restrict SSH sources, open specific ports, defend port scans, log anomalous traffic.

Prerequisites

Check iptables:

iptables --version  # should print something like iptables v1.8.7

How It Works

iptables is the userspace tool for kernel Netfilter. Core concepts:

Core strategy: default deny, allow as needed—set INPUT policy to DROP, then add allow rules. Minimizes attack surface.

Procedure

Step 1: List current rules

iptables -L -n -v --line-numbers

Flags: -L list, -n numeric (no DNS), -v counters (packets/bytes), --line-numbers for later insert/delete.

Step 2: Flush and set default policy

⚠️ Dangerous: these commands flush all rules. Remote SSH with a bad policy drops your session immediately.

# Flush all rules
iptables -F
iptables -X
iptables -Z

# Default policy: DROP INPUT and FORWARD, ACCEPT OUTPUT
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

Step 3: Allow loopback and established connections

# Allow loopback (many services depend on lo)
iptables -A INPUT -i lo -j ACCEPT

# Allow ESTABLISHED and RELATED
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

The second rule is critical—it lets replies for outbound connections (including your SSH session) through instead of hitting DROP.

Step 4: Open SSH

Under DROP you must explicitly allow SSH. Replace 22 with your actual SSH port:

# Restrict SSH to a specific subnet (safer)
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT

# No fixed IP: allow all sources (not recommended in production)
# iptables -A INPUT -p tcp --dport 22 -j ACCEPT

Always use `-s` to shrink the SSH attack surface. If you use a bastion, allow only the bastion IP.

Step 5: Open web ports

# HTTP (80)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# HTTPS (443)
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Nginx proxying other services may need extra ports
# iptables -A INPUT -p tcp --dport 8080 -s 127.0.0.1 -j ACCEPT

Step 6: Rate-limit ICMP (ping)

# Rate-limit ICMP to mitigate ping flood
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/second -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

First rule allows 1 ping/sec; excess hits the second rule and is dropped. Keeps reachability checks, blocks ICMP flood.

Step 7: Defend against port scans

# Log and drop SYN scans (half-open)
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j LOG --log-prefix "NULL_SCAN: "
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

# Log and drop Xmas scans
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j LOG --log-prefix "XMAS_SCAN: "
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

# Cap concurrent connections per IP to prevent exhaustion
iptables -A INPUT -p tcp --syn -m connlimit --connlimit-above 100 --connlimit-mask 32 -j LOG --log-prefix "CONN_LIMIT: "
iptables -A INPUT -p tcp --syn -m connlimit --connlimit-above 100 --connlimit-mask 32 -j DROP

connlimit caps concurrent connections per IP (100 in the example). connlimit-mask 32 counts per IPv4 address (/32). For a /24, set 24.

Step 8: Log dropped traffic

# Log DROP hits to syslog (post-incident audit)
iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4

# Place this after all ACCEPT rules and before the default policy
# It only runs when no ACCEPT rule matched

Logs go to `/var/log/messages` or `/var/log/syslog`. Inspect with `grep "IPTABLES-DROP" /var/log/syslog`.

Step 9: Persist rules

# CentOS/RHEL
yum install -y iptables-services
service iptables save
systemctl enable iptables

# Ubuntu/Debian
apt-get install -y iptables-persistent
netfilter-persistent save

Verification

# Full rule list
iptables -L -n -v --line-numbers

# Port reachability (local or remote host)
curl -I http://SERVER_IP     # should return an HTTP response
ssh user@SERVER_IP           # should log in
ping -c 3 SERVER_IP          # should get replies (rate-limited 1/s)

# Closed ports should be denied
curl -I http://SERVER_IP:3306  # should time out or refuse
nc -zv SERVER_IP 3306          # should show connect failure

FAQ

Q1: SSH dropped after applying rules?

If remote SSH died because of the new rules:

Q2: DROP vs REJECT?

DROP silently discards; the client waits until timeout (safer; no port disclosure). REJECT sends an error (e.g. ICMP port-unreachable); the client fails immediately. Prefer DROP in production so scanners cannot infer port state from REJECT replies.

Q3: Does rule order matter?

Yes. iptables matches top to bottom and stops on first hit. Put ACCEPT rules first, DROP/REJECT later. An ACCEPT after a matching DROP never runs.

Summary

iptables is the baseline for Linux server hardening. Default deny plus source IP restrictions, connection limits, and drop logs shrinks the attack surface and surfaces scans. Pair with fail2ban for automated IP bans and defense in depth.