iptables Deep Knowledge – Chains, NAT, nftables, RZ-Regelwerk

Quellen

  • RFC 793 – TCP
  • RFC 768 – UDP
  • Linux Kernel Netfilter Documentation
  • nftables Wiki: https://wiki.nftables.org
  • iptables man pages

Teil 1: Netfilter – Das Fundament

iptables ist nur das Userspace-Werkzeug. Die eigentliche Arbeit macht
Netfilter im Linux-Kernel. Netfilter ist ein Framework von Hooks
in den Netzwerk-Stack des Kernels.

Netfilter Hooks im Kernel:
                                          ROUTING
                                         DECISION
                                             β”‚
NETWORK  ──► PREROUTING ──► (local?) ──►   β”œβ”€β”€β–Ί FORWARD ──► POSTROUTING ──► NETWORK
INTERFACE                        β”‚          β”‚                                INTERFACE
                                 β–Ό          β–Ό
                              INPUT      OUTPUT
                                 β”‚          β”‚
                                 β–Ό          β–Ό
                            LOCAL PROCESS (z.B. sshd, named)

Die fΓΌnf Netfilter-Hooks:

Hook Wann Typischer Einsatz
PREROUTING Paket kommt rein, vor Routing DNAT, Redirect
INPUT Paket fΓΌr lokalen Prozess Eingehende Firewall-Regeln
FORWARD Paket wird weitergeleitet (kein lokaler Prozess) Router-Firewall
OUTPUT Paket von lokalem Prozess Ausgehende Regeln
POSTROUTING Paket verlΓ€sst System, nach Routing SNAT, Masquerade

Teil 2: Tables und Chains

iptables organisiert Regeln in Tables (Tabellen) und Chains (Ketten).

Die vier Tabellen

filter   ← Standard-Tabelle, Pakete erlauben/verweigern
nat      ← Network Address Translation (SNAT, DNAT, Masquerade)
mangle   ← Pakete verΓ€ndern (TTL, TOS, Marking)
raw      ← Vor Connection Tracking (sehr frΓΌh, sehr schnell)

Welche Chains in welcher Tabelle

filter:   INPUT, FORWARD, OUTPUT
nat:      PREROUTING, OUTPUT, POSTROUTING
mangle:   PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
raw:      PREROUTING, OUTPUT

Verarbeitungsreihenfolge (eingehendes Paket)

Netzwerk-Interface
      β”‚
      β–Ό
raw:PREROUTING        ← Connection Tracking Bypass mΓΆglich
      β”‚
      β–Ό
mangle:PREROUTING     ← Paket-Marking
      β”‚
      β–Ό
nat:PREROUTING        ← DNAT hier! (Ziel-IP Γ€ndern)
      β”‚
      β–Ό
   ROUTING DECISION
      β”‚
      β”œβ”€β”€ Lokaler Prozess?
      β”‚         β”‚
      β”‚         β–Ό
      β”‚   mangle:INPUT
      β”‚         β”‚
      β”‚         β–Ό
      β”‚   filter:INPUT   ← Haupt-Firewall fΓΌr eingehende Pakete
      β”‚         β”‚
      β”‚         β–Ό
      β”‚   Lokaler Prozess (sshd, named, nginx...)
      β”‚
      └── Weiterleitung?
                β”‚
                β–Ό
          mangle:FORWARD
                β”‚
                β–Ό
          filter:FORWARD  ← Router-Firewall
                β”‚
                β–Ό
          mangle:POSTROUTING
                β”‚
                β–Ό
          nat:POSTROUTING  ← SNAT/Masquerade hier!
                β”‚
                β–Ό
          Netzwerk-Interface

Teil 3: Regeln, Targets und Policy

Anatomie einer iptables-Regel

iptables  -t filter  -A INPUT  -p tcp  --dport 22  -s 10.0.0.0/19  -j ACCEPT
    β”‚         β”‚          β”‚        β”‚         β”‚             β”‚              β”‚
    β”‚       Tabelle    Chain   Protokoll  Ziel-Port    Quell-IP       Aktion
    β”‚      (default:            (tcp/udp/
    β”‚       filter)              icmp)
  Befehl
  -A = Append (anhΓ€ngen)
  -I = Insert (vorne einfΓΌgen)
  -D = Delete
  -L = List
  -F = Flush (alle Regeln leeren)
  -P = Policy (Standard-Aktion)
  -N = New chain
  -X = Delete chain

Targets (Aktionen)

ACCEPT    β†’ Paket durch, keine weiteren Regeln
DROP      β†’ Paket still verwerfen (Absender bemerkt nichts, Timeout)
REJECT    β†’ Paket verwerfen + ICMP-Fehler senden (Absender weiß es sofort)
LOG       β†’ Paket loggen, weiter zu nΓ€chster Regel
RETURN    β†’ ZurΓΌck zur aufrufenden Chain
DNAT      β†’ Ziel-IP Γ€ndern (nur nat:PREROUTING, nat:OUTPUT)
SNAT      β†’ Quell-IP Γ€ndern (nur nat:POSTROUTING)
MASQUERADE β†’ SNAT mit dynamischer Quell-IP (fΓΌr DHCP-Interfaces)
REDIRECT  β†’ Auf lokalen Port umleiten
MARK      β†’ Paket markieren (fΓΌr Routing-Entscheidungen)

Default Policy

# DROP vs REJECT als Policy:
iptables -P INPUT DROP     # Stilles Verwerfen – empfohlen fΓΌr RZ
iptables -P INPUT REJECT   # Mit ICMP-Fehler – einfacher zu debuggen

# ⚠️ VORSICHT: Policy setzen BEVOR Regeln gelâscht werden!
# Falsche Reihenfolge = sofortige Aussperrung!

# SICHERE Reihenfolge:
# 1. SSH-Regel sicherstellen
# 2. Policy setzen
# 3. Rest konfigurieren

Teil 4: Connection Tracking (conntrack)

Connection Tracking ist das GedΓ€chtnis von iptables.
Es merkt sich den Zustand jeder Verbindung.

Verbindungs-ZustΓ€nde:

NEW          β†’ Erstes Paket einer neuen Verbindung
ESTABLISHED  β†’ Antwortpakete einer bekannten Verbindung
RELATED      β†’ Verbindung die zu einer anderen gehΓΆrt (z.B. FTP-Daten)
INVALID      β†’ Passt zu keinem bekannten Zustand β†’ DROP!
# Die wichtigste Regel ΓΌberhaupt – IMMER ganz oben:
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Warum? 
# Ohne diese Regel blockiert DROP-Policy auch Antwortpakete!
# TCP-Handshake geht raus (OUTPUT ACCEPT) aber SYN-ACK kommt nicht rein

# UngΓΌltige Pakete verwerfen
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# Conntrack-Tabelle anzeigen
conntrack -L
conntrack -L | grep ESTABLISHED | wc -l   # Anzahl aktiver Verbindungen

# Conntrack-Limits (wichtig bei vielen Verbindungen!)
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count  # aktuell aktiv

Teil 5: NAT – Network Address Translation

DNAT – Destination NAT (eingehend)

# Eingehende Verbindungen auf Port 80 an internen Server weiterleiten
iptables -t nat -A PREROUTING \
  -p tcp --dport 80 \
  -j DNAT --to-destination 10.0.1.10:80

# Eingehend auf Port 2222 β†’ intern auf SSH Port 22
iptables -t nat -A PREROUTING \
  -p tcp --dport 2222 \
  -j DNAT --to-destination 10.0.1.50:22

# ⚠️ DNAT allein reicht nicht!
# FORWARD-Regel muss auch erlauben:
iptables -A FORWARD \
  -p tcp --dport 80 \
  -d 10.0.1.10 \
  -m conntrack --ctstate NEW \
  -j ACCEPT

SNAT – Source NAT (ausgehend, feste IP)

# Internes Netz hinter fester externer IP verstecken
iptables -t nat -A POSTROUTING \
  -s 10.0.0.0/19 \
  -o eth0 \
  -j SNAT --to-source 203.0.113.10

MASQUERADE – SNAT mit dynamischer IP

# FΓΌr Interfaces mit wechselnder IP (DSL, DHCP)
iptables -t nat -A POSTROUTING \
  -s 10.0.0.0/19 \
  -o eth0 \
  -j MASQUERADE

# Forwarding im Kernel aktivieren! (sonst funktioniert NAT nicht)
echo 1 > /proc/sys/net/ipv4/ip_forward
# Persistent in /etc/sysctl.conf:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

Teil 6: VollstΓ€ndiges RZ-Regelwerk

#!/bin/bash
# /etc/iptables/rz-rules.sh
# RZ-Server Basis-HΓ€rtung
# Anpassen: MGMT_NET, SSH_PORT, eigene Dienste

MGMT_NET="10.0.0.0/19"    # Management-Netz
SERVER_NET="10.0.1.0/24"  # Server-Netz
SSH_PORT="22"

IPT="iptables"
IP6T="ip6tables"

echo "[*] Lade iptables RZ-Regelwerk..."

# ── Alles leeren ──────────────────────────────────────────────
$IPT -F
$IPT -X
$IPT -t nat -F;    $IPT -t nat -X
$IPT -t mangle -F; $IPT -t mangle -X
$IPT -t raw -F;    $IPT -t raw -X

# ── IPv6 komplett sperren (wenn nicht genutzt) ────────────────
$IP6T -F
$IP6T -P INPUT DROP
$IP6T -P FORWARD DROP
$IP6T -P OUTPUT DROP

# ── Default Policy ERST NACH Basis-Regeln setzen! ─────────────
# (sonst Aussperrung bei Fehler)

# ── Loopback ──────────────────────────────────────────────────
$IPT -A INPUT  -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

# ── Connection Tracking – IMMER ZUERST ───────────────────────
$IPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPT -A INPUT -m conntrack --ctstate INVALID -j DROP

# ── ICMP ──────────────────────────────────────────────────────
$IPT -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
$IPT -A INPUT -p icmp --icmp-type echo-reply   -j ACCEPT
# ICMP Typ 3 (Unreachable) und Typ 11 (TTL exceeded) fΓΌr Traceroute:
$IPT -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
$IPT -A INPUT -p icmp --icmp-type time-exceeded           -j ACCEPT

# ── SSH – nur aus Management-Netz ─────────────────────────────
$IPT -A INPUT -p tcp --dport $SSH_PORT \
  -s $MGMT_NET \
  -m conntrack --ctstate NEW \
  -j ACCEPT

# ── DNS (falls dieser Server DNS-Server ist) ──────────────────
# $IPT -A INPUT -p udp --dport 53 -s $MGMT_NET -j ACCEPT
# $IPT -A INPUT -p tcp --dport 53 -s $MGMT_NET -j ACCEPT

# ── Monitoring (NRPE/Icinga2) ─────────────────────────────────
# $IPT -A INPUT -p tcp --dport 5666 -s $MGMT_NET -j ACCEPT
# $IPT -A INPUT -p tcp --dport 5665 -s $MGMT_NET -j ACCEPT

# ── IPMI/BMC Interface (separates Interface) ──────────────────
# Falls IPMI auf eigenem Interface:
# $IPT -A INPUT -i ipmi0 -s $MGMT_NET -j ACCEPT

# ── Logging vor DROP ──────────────────────────────────────────
$IPT -A INPUT -j LOG \
  --log-prefix "iptables-DROP-INPUT: " \
  --log-level 4 \
  -m limit --limit 5/min --limit-burst 10

# ── Default Policy setzen ─────────────────────────────────────
$IPT -P INPUT   DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT  ACCEPT

echo "[+] Regelwerk geladen."
echo "[*] Aktuelle Regeln:"
$IPT -L -n -v --line-numbers

Regeln persistent machen

# Debian / Ubuntu
apt install iptables-persistent
iptables-save  > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
systemctl enable netfilter-persistent

# RHEL / Rocky / AlmaLinux
dnf install iptables-services
iptables-save > /etc/sysconfig/iptables
systemctl enable iptables
systemctl start iptables

# Arch Linux
iptables-save > /etc/iptables/iptables.rules
systemctl enable iptables
systemctl start iptables

# FreeBSD β†’ KEIN iptables! β†’ pf (siehe unten)

Teil 7: Diagnose und Debugging

# Regeln anzeigen
iptables -L -n -v             # alle filter-Chains, mit ZΓ€hlern
iptables -L INPUT -n -v --line-numbers  # mit Zeilennummern
iptables -t nat -L -n -v      # NAT-Tabelle

# Treffer-ZΓ€hler zurΓΌcksetzen
iptables -Z                   # alle ZΓ€hler
iptables -Z INPUT             # nur INPUT-Chain

# Einzelne Regel lΓΆschen
iptables -D INPUT 5           # Regel Nr. 5 in INPUT lΓΆschen
iptables -D INPUT -p tcp --dport 22 -j ACCEPT  # nach Inhalt lΓΆschen

# Live-Logging beobachten
journalctl -f | grep iptables-DROP
tail -f /var/log/kern.log | grep iptables  # Debian ohne systemd-journal

# Pakete tracen (raw table)
iptables -t raw -A PREROUTING \
  -s 10.0.1.50 \
  -j TRACE

# Connection Tracking anzeigen
conntrack -L
conntrack -L --proto tcp | grep ESTABLISHED
conntrack -E                  # Events live beobachten

# HΓ€ufige Probleme:

# Problem: SSH-Verbindung bricht ab nach RegelΓ€nderung
# Ursache: ESTABLISHED-Regel fehlt oder falsche Reihenfolge
iptables -L INPUT -n --line-numbers | grep ESTABLISHED

# Problem: NAT funktioniert nicht
# Ursache: ip_forward nicht aktiviert
cat /proc/sys/net/ipv4/ip_forward  # muss 1 sein
# Ursache: FORWARD-Chain blockiert
iptables -L FORWARD -n -v

# Problem: Port ist offen aber Verbindung kommt nicht an
# Diagnose:
iptables -L INPUT -n -v       # Regel vorhanden?
ss -tlnp | grep :PORT         # Dienst lauscht?
conntrack -L | grep PORT      # Connection Tracking?

Teil 8: nftables – Der moderne Nachfolger

Warum nftables?

iptables:   Seit 1998, separate Tools pro Protokoll
            (iptables, ip6tables, arptables, ebtables)
            Jede Regel = Kernel-Syscall
            Kein atomisches Regelwerk-Update

nftables:   Seit Linux 3.13 (2014), ein Tool fΓΌr alles
            Regelwerke werden als Batch geladen (atomar)
            Bessere Performance
            Seit Debian 10 / RHEL 8 Standard-Backend

iptables vs nftables Syntax

# iptables:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# nftables equivalent:
nft add rule inet filter input tcp dport 22 accept

nftables Grundstruktur

Tables β†’ Families β†’ Chains β†’ Rules

Family:  ip (IPv4), ip6 (IPv6), inet (beide), arp, bridge
# VollstΓ€ndiges nftables RZ-Regelwerk
# /etc/nftables.conf

#!/usr/sbin/nft -f

flush ruleset

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

        # Loopback
        iif lo accept

        # Connection Tracking
        ct state established,related accept
        ct state invalid drop

        # ICMP
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # SSH aus Management-Netz
        ip saddr 10.0.0.0/19 tcp dport 22 ct state new accept

        # Logging
        log prefix "nft-DROP: " flags all counter drop
    }

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

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

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100;
        # DNAT Beispiel:
        # tcp dport 80 dnat to 10.0.1.10:80
    }

    chain postrouting {
        type nat hook postrouting priority 100;
        # MASQUERADE Beispiel:
        # ip saddr 10.0.0.0/19 oif eth0 masquerade
    }
}
# nftables Befehle
nft list ruleset              # alles anzeigen
nft list table inet filter    # eine Tabelle
nft -f /etc/nftables.conf     # Datei laden
nft flush ruleset             # alles leeren

# Service
systemctl enable nftables
systemctl start nftables
systemctl restart nftables    # lΓ€dt /etc/nftables.conf neu

iptables-nft – KompatibilitΓ€ts-Backend

# Modernes Debian/Ubuntu/RHEL nutzt iptables-nft:
# iptables-Syntax, aber nftables-Backend im Kernel

iptables --version
# iptables v1.8.x (nf_tables)  ← nftables Backend β†’ gut
# iptables v1.8.x (legacy)     ← legacy iptables  β†’ alt

# Backend prΓΌfen und setzen (Debian)
update-alternatives --query iptables
update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

# ⚠️ NIEMALS mischen!
# iptables-legacy + iptables-nft gleichzeitig = Chaos
# iptables + nftables direkt gleichzeitig = doppelte Regeln

Teil 9: FreeBSD – pf statt iptables

FreeBSD hat kein iptables. Das Γ„quivalent ist pf (Packet Filter),
ursprΓΌnglich aus OpenBSD.

# /etc/pf.conf – Basis RZ-Regelwerk

# Interfaces definieren
ext_if = "em0"        # externes Interface
mgmt_if = "em1"       # Management-Interface
mgmt_net = "10.0.0.0/19"

# Standard: alles blockieren
set block-policy drop
block all

# Loopback
pass quick on lo0

# Eingehend: nur Etabliertes + SSH aus Management
pass in  quick on $mgmt_if inet proto tcp \
  from $mgmt_net to any port 22 keep state

pass in  quick inet proto icmp all keep state

pass out quick all keep state
# pf aktivieren
# /etc/rc.conf:
pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"

# Befehle
pfctl -e              # pf aktivieren
pfctl -d              # pf deaktivieren
pfctl -f /etc/pf.conf # Regeln laden
pfctl -sr             # Regeln anzeigen
pfctl -si             # Statistiken
pfctl -ss             # Verbindungs-States anzeigen

Teil 10: Distro-Unterschiede

Aspekt Debian/Ubuntu RHEL/Rocky Arch FreeBSD
Default Firewall-Tool iptables-nft / ufw firewalld / iptables iptables pf
Persistenz-Paket iptables-persistent iptables-services systemd unit rc.conf
Config-Datei /etc/iptables/rules.v4 /etc/sysconfig/iptables /etc/iptables/iptables.rules /etc/pf.conf
nftables Config /etc/nftables.conf /etc/nftables.conf /etc/nftables.conf n/a
ufw vorinstalliert (inaktiv) nicht vorhanden optional n/a
firewalld nicht Standard Standard optional n/a
Backend nf_tables nf_tables nf_tables pf

Ubuntu: ufw deaktivieren bei manuellem iptables

# ⚠️ ufw UND manuelles iptables = Konflikt!
ufw disable
systemctl disable ufw
systemctl mask ufw
# Dann prΓΌfen ob keine ufw-Chains mehr in iptables:
iptables -L | grep ufw   # sollte leer sein

RHEL: firewalld deaktivieren bei manuellem iptables

# ⚠️ firewalld UND manuelles iptables = Konflikt!
systemctl disable firewalld
systemctl mask firewalld
systemctl enable iptables
systemctl start iptables

Teil 11: Wichtige Einzeiler fΓΌr den RZ-Alltag

# Alle aktiven Regeln mit Treffern anzeigen
iptables -L -n -v | grep -v "0     0"

# Welche Ports lauschen UND sind durch Firewall erlaubt?
ss -tlnp; echo "---"; iptables -L INPUT -n | grep ACCEPT

# IP sofort sperren (Angreifer!)
iptables -I INPUT 1 -s 1.2.3.4 -j DROP

# IP-Sperre wieder aufheben
iptables -D INPUT -s 1.2.3.4 -j DROP

# Alle Verbindungen von IP zΓ€hlen
conntrack -L | grep 1.2.3.4 | wc -l

# Port-Scan erkennen (viele NEW-States von einer IP)
conntrack -L | grep NEW | awk '{print $6}' | \
  grep src | sort | uniq -c | sort -rn | head

# SYN-Flood Schutz
iptables -A INPUT -p tcp --syn \
  -m limit --limit 1/s --limit-burst 3 \
  -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

# Alle Regeln auf einmal sichern
iptables-save > /root/iptables-backup-$(date +%Y%m%d).rules

# Schnell wiederherstellen
iptables-restore < /root/iptables-backup-20260327.rules

Wichtige Dateipfade

Pfad System Inhalt
/etc/iptables/rules.v4 Debian/Ubuntu IPv4-Regeln persistent
/etc/iptables/rules.v6 Debian/Ubuntu IPv6-Regeln persistent
/etc/sysconfig/iptables RHEL/Rocky IPv4-Regeln persistent
/etc/iptables/iptables.rules Arch IPv4-Regeln persistent
/etc/nftables.conf Alle Linux nftables-Regelwerk
/etc/pf.conf FreeBSD pf-Regelwerk
/proc/sys/net/ipv4/ip_forward Linux NAT/Forwarding aktiv?
/proc/net/nf_conntrack Linux Connection-Tracking-Tabelle
journalctl -k | grep iptables systemd Kernel-Log Firewall-Drops

Quellen / WeiterfΓΌhrend

  • man iptables, man iptables-extensions
  • man nft
  • man pf.conf (FreeBSD)
  • Netfilter Project: https://www.netfilter.org
  • nftables Wiki: https://wiki.nftables.org
  • Arch Wiki: https://wiki.archlinux.org/title/iptables
  • RHEL Security Guide: Firewall kapitel