Skip to content
Codeloom
Linux

iptables and nftables Firewall Configuration on Linux

Learn how to configure Linux firewalls with iptables and nftables, including rules, chains, NAT, and migration strategies.

·7 min read · By Codeloom
Intermediate 14 min read

What you'll learn

  • How iptables tables, chains, and rules work
  • Writing firewall rules for real server scenarios
  • nftables syntax and migrating from iptables

Prerequisites

  • Basic Linux command line
  • Basic understanding of TCP/IP and ports

Linux Firewall Fundamentals

Every Linux system has a built-in firewall powered by the kernel’s Netfilter framework. iptables has been the standard interface to Netfilter for over two decades, while nftables is its modern replacement. Both control which network packets are allowed, denied, or modified as they pass through your system.

Understanding these tools is essential for securing any Linux server.

iptables Architecture

iptables organizes rules into tables and chains:

Tables define the type of operation:

  • filter — Default table for accepting or dropping packets
  • nat — Network address translation (port forwarding, masquerading)
  • mangle — Packet header modification
  • raw — Exemptions from connection tracking

Chains define when rules are evaluated:

  • INPUT — Packets destined for the local system
  • OUTPUT — Packets originating from the local system
  • FORWARD — Packets being routed through the system
  • PREROUTING — Packets before routing decisions (nat/mangle)
  • POSTROUTING — Packets after routing decisions (nat/mangle)

Viewing Current Rules

# List all rules in the filter table
sudo iptables -L -n -v

# List rules with line numbers
sudo iptables -L -n --line-numbers

# List NAT rules
sudo iptables -t nat -L -n -v

# Show rules as commands (useful for backup)
sudo iptables-save

The -n flag prevents DNS resolution, making the output much faster.

Writing iptables Rules

The basic syntax is:

sudo iptables -A <CHAIN> <match-criteria> -j <TARGET>

Common targets are ACCEPT, DROP (silently discard), REJECT (discard with error response), and LOG.

Allow SSH Access

# Allow incoming SSH connections
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow SSH only from a specific subnet
sudo iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 22 -j ACCEPT

Basic Web Server Rules

# Allow HTTP and HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow established and related connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT

# Drop everything else
sudo iptables -A INPUT -j DROP

Rate Limiting

# Rate limit SSH to prevent brute force (max 3 connections per minute)
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
    -m limit --limit 3/min --limit-burst 3 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j DROP

Logging

# Log dropped packets before dropping them
sudo iptables -A INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
sudo iptables -A INPUT -j DROP

A Complete Server Firewall Script

Here is a production-ready iptables script for a web server:

#!/bin/bash
# Flush existing rules
iptables -F
iptables -X
iptables -t nat -F

# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (rate limited)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
    -m limit --limit 5/min --limit-burst 5 -j ACCEPT

# Allow HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow ICMP (ping)
iptables -A INPUT -p icmp --icmp-type echo-request \
    -m limit --limit 1/s --limit-burst 4 -j ACCEPT

# Log and drop everything else
iptables -A INPUT -j LOG --log-prefix "IPT-DROPPED: " --log-level 4
iptables -A INPUT -j DROP

NAT and Port Forwarding

NAT rules are essential for routers, VPNs, and container hosts:

# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Masquerade outbound traffic (basic NAT for a router)
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# Port forward external port 8080 to internal host 192.168.1.100:80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \
    -j DNAT --to-destination 192.168.1.100:80
sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT

Persisting Rules

iptables rules are lost on reboot. To make them persistent:

# Debian/Ubuntu
sudo apt install iptables-persistent
sudo netfilter-persistent save

# RHEL/CentOS
sudo service iptables save
# Rules saved to /etc/sysconfig/iptables

# Manual backup and restore
sudo iptables-save > /etc/iptables.rules
sudo iptables-restore < /etc/iptables.rules

Migrating to nftables

nftables is the successor to iptables, offering a cleaner syntax, better performance, and unified handling of IPv4, IPv6, ARP, and bridging. Most modern distributions ship with nftables as the default.

nftables Basics

nftables uses tables, chains, and rules, but with a different syntax:

# List all rules
sudo nft list ruleset

# Create a table
sudo nft add table inet my_filter

# Create a chain with a base hook
sudo nft add chain inet my_filter input { type filter hook input priority 0 \; policy drop \; }

# Add rules
sudo nft add rule inet my_filter input iif lo accept
sudo nft add rule inet my_filter input ct state established,related accept
sudo nft add rule inet my_filter input tcp dport 22 accept
sudo nft add rule inet my_filter input tcp dport { 80, 443 } accept
sudo nft add rule inet my_filter input counter drop

nftables Configuration File

The preferred approach is to write a configuration file at /etc/nftables.conf:

#!/usr/sbin/nft -f

flush ruleset

table inet firewall {
    chain input {
        type filter hook input priority 0; policy drop;

        # Allow loopback
        iif lo accept

        # Allow established connections
        ct state established,related accept

        # Allow SSH (rate limited)
        tcp dport 22 ct state new limit rate 5/minute accept

        # Allow web traffic
        tcp dport { 80, 443 } accept

        # Allow ICMP
        ip protocol icmp limit rate 4/second accept
        ip6 nexthdr icmpv6 limit rate 4/second accept

        # Log and drop
        counter log prefix "NFT-DROP: " drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

Load and enable it:

sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables
sudo systemctl start nftables

nftables Advantages Over iptables

  • Sets for grouping IPs or ports: define allowed_ips = { 10.0.0.1, 10.0.0.2 }
  • Maps for verdict lookups: tcp dport vmap { 22 : accept, 80 : accept, 443 : accept }
  • Atomic rule replacement — the entire ruleset is applied at once
  • Single tool for IPv4 and IPv6 (the inet family)
  • Better performance with fewer kernel transitions

Translating iptables to nftables

If you have existing iptables rules, you can auto-translate them:

# Translate saved iptables rules to nftables format
iptables-save > /tmp/iptables-rules.txt
iptables-restore-translate -f /tmp/iptables-rules.txt > /etc/nftables.conf

Debugging Firewall Rules

# Watch packets hitting a chain in real time (iptables)
sudo iptables -A INPUT -j LOG --log-prefix "DEBUG: "
sudo journalctl -f | grep "DEBUG:"

# Count hits per rule (iptables)
sudo iptables -L -n -v

# Trace packet flow (nftables)
sudo nft add rule inet my_filter input meta nftrace set 1
sudo nft monitor trace

# Test connectivity
nc -zv hostname 80
curl -v http://hostname

Summary

Linux firewalls built on Netfilter give you complete control over network traffic. iptables remains widely used and understood, with its table/chain/rule structure. nftables is the modern replacement offering cleaner syntax, better performance, and unified IPv4/IPv6 handling. For new deployments, prefer nftables. For existing systems, you can translate your iptables rules automatically. The key principles remain the same: set default-deny policies, allow only what is needed, rate-limit sensitive services, and persist your rules across reboots.