Server-Blog Ubuntu Server

Thema Iptables: Firewall und IPSet

Abschnitt 1: Installation und Grundeinrichtung

Nun kommt mein Lieblingsthema überhaupt. An dem habe ich viele Stunden und Nächte verbracht :)

Erstmal alles Nötige installieren:

apt install iptables-persistent iptables -y

Bei Iptables wird jedes Protokoll (**IPv4** und **IPv6**) separat behandelt. Die Regeln für IPv4 gelten also nie für IPv6 und umgekehrt.

Um das Ganze "angenehmer" zu machen, werden wir hier mit **Bash-Scripten** arbeiten, da diese die Sache vereinfachen. Dazu wechseln wir erstmal über SSH in den Ordner `iptables`:

cd /etc/iptables

Darin finden wir Folgendes:

-rw-r----- 1 root root    4364 Jun 29 11:00 rules.v4
-rw-r----- 1 root root     849 Jun 29 11:00 rules.v6

Dort werden die **persistenten Regeln** für jedes Protokoll gespeichert.

Schauen wir uns die vorhandenen Regeln einmal an mit `iptables -S` für IPv4 und `ip6tables -S` für IPv6.

Ausgabe für IPv4:

root@server01:~# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

Ausgabe für IPv6:

root@server01:~# ip6tables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

**Alle Türen sind sperrangelweit offen, das müssen wir ändern!**

Da jeder, der damit anfängt, sich schon einmal über die Firewall ausgesperrt hat, gebe ich Ihnen hier mal ein fertiges Script als Grundlage. Ihre Schnittstellen fragen Sie auf der Konsole mit `ip a` ab:

ip a
#!/bin/bash

IPT="/sbin/iptables"

WAN="ens18"
LAN="ens19"
WLAN="ens20"

#    Löschen aller Regeln
$IPT -F
$IPT -t nat --flush
$IPT -t mangle --flush

#    Policy setzen auf ALLES ablehnen!
$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP
$IPT -t nat --policy PREROUTING ACCEPT
$IPT -t nat --policy OUTPUT ACCEPT
$IPT -t nat --policy POSTROUTING ACCEPT
$IPT -t mangle --policy PREROUTING ACCEPT
$IPT -t mangle --policy OUTPUT ACCEPT

# LOOPBACK aktivieren, sonst geht nichts
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT


# bestehende Eingehende Verbindungen zulassen
$IPT -A INPUT -i $WAN -m state --state RELATED,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $LAN -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $WLAN -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

# Alle Verbindungen zulassen für Ausgehend
$IPT -A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

# Alle bestehenden Verbindungen zulassen für FORWARD (Weiterleitung)
$IPT -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT


#    LAN Verbindungen erlauben
$IPT -A FORWARD -i $LAN -o $WAN -m state --state NEW -j ACCEPT
$IPT -A FORWARD -i $WLAN -o $WAN -m state --state NEW -j ACCEPT

#    LAN und WLAN und umgekehrt erlauben
$IPT -A FORWARD -i $LAN -o $WLAN -m state --state NEW -j ACCEPT
$IPT -A FORWARD -i $WLAN -o $LAN -m state --state NEW -j ACCEPT

#    SSH zulassen um sich nicht auszusperren
$IPT -A INPUT -i $LAN -p tcp --dport 22 -m state --state NEW -j ACCEPT
$IPT -A INPUT -i $WLAN -p tcp --dport 22 -p tcp --dport 22 -m state --state NEW -j ACCEPT


#    PING zulassen aber für WAN begrenzen
$IPT -A INPUT -i $WAN -p icmp --icmp-type echo-request -m state --state NEW,ESTABLISHED,RELATED -m limit --limit 30/minute --limit-burst 60 -j ACCEPT
$IPT -A OUTPUT -o $WAN -p icmp --icmp-type echo-reply -m state --state NEW,ESTABLISHED,RELATED -m limit --limit 30/minute --limit-burst 60 -j ACCEPT

$IPT -A INPUT -i $LAN -p icmp --icmp-type echo-request -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT -o $LAN -p icmp --icmp-type echo-reply -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

$IPT -A INPUT -i $WLAN -p icmp --icmp-type echo-request -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT -o $WLAN -p icmp --icmp-type echo-reply -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT


#
# --- Grundlegende Sicherheit (Stealth-Scans, ungültige Pakete, Multicast) ---
#

# Stealth-Scans verhindern (kommentarlos verwerfen)
# 2. XMAS-Scan (alle Flags gesetzt)
$IPT -A INPUT -p tcp --tcp-flags ALL ALL -m conntrack ! --ctstate ESTABLISHED,RELATED -j DROP
# 3. Null-Scan (keine Flags gesetzt)
# Ein Paket ohne TCP-Flags ist im regulären Betrieb ebenfalls nicht vorgesehen.
$IPT -A INPUT -p tcp --tcp-flags ALL NONE -m conntrack ! --ctstate ESTABLISHED,RELATED -j DROP
# 4. SYN/FIN-Scan
$IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -m conntrack ! --ctstate ESTABLISHED,RELATED -j DROP
# 5. SYN/RST-Scan
$IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -m conntrack ! --ctstate ESTABLISHED,RELATED -j DROP

# Ungültige Pakete verwerfen
$IPT -A INPUT -p tcp -m state --state INVALID -j DROP
$IPT -A OUTPUT -p tcp -m state --state INVALID -j DROP # Optional
$IPT -A FORWARD -p tcp -m state --state INVALID -j DROP # Nur wenn Routing aktiv

# Multicast nur über UDP zulassen
$IPT -A INPUT ! -p udp -d $NETWORK_MULTICAST -j DROP
$IPT -A FORWARD ! -p udp -d $NETWORK_MULTICAST -j DROP # Nur wenn Routing aktiv
#
# Hier kommt die Blacklist Prüfung hin
#
#
#
#
#
#
#
#
# SNAT für den Internetzugang auch für die Clients
$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE
# Einstellungen speichern
netfilter-persistent save

Passen Sie Ihre Schnittstellen oben an, damit auch die richtigen zugewiesen werden. Kopieren Sie sich den Text und führen Sie dann auf der Konsole folgende Schritte aus:

Öffnen der Datei:

nano /etc/iptables/firewall.sh

Mit einem Rechtsklick auf die Konsole wird der Text dort hineinkopiert. Speichern und schließen Sie mit:

STRG + O
STRG + X

Nun müssen Sie das Script ausführbar machen und starten:

chmod a+x /etc/iptables/firewall.sh
/etc/iptables/firewall.sh

Als Ausgabe erhalten wir, wenn kein Fehler im Script ist, folgende beiden Zeilen:

klaus@server01:/etc/iptables# /etc/iptables/firewall.sh
run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save

Abschnitt 2: IPSet

Zu jeder Firewall gehört **IPSet**.

Damit können Sie sich ganz einfach Blacklists einrichten, und auch **Fail2ban** profitiert davon, wenn es dafür konfiguriert ist. Aber der Reihe nach.

Installation:

apt install ipset -y

Pflegehinweise für IPSet

# Set erstellen, sofern nicht vorhanden (-exist)
ipset create 'setname' hash:net -exist hashsize 16384 maxelem 65536

# Set löschen
ipset destroy 'setname'

# Speichert nur ein bestimmtes Set
ipset save 'setname' > ipset_backup.txt

# Stellt ein Set wieder her
ipset restore 'setname' < ipset_backup.txt

# Ein komplettes Set in eine Datei schreiben
ipset list 'setname' > ausgabe.txt

# Nur den Kopf anzeigen, was wichtig als Info ist.
ipset list -t 'setname'

# IP oder CIDR zum Set hinzufügen
ipset add 'setname' 1.2.3.4
ipset add 'setname' 1.2.3.0/24

# IP oder CIDR vom Set löschen
ipset del 'setname' 1.2.3.4
ipset del 'setname' 1.2.3.0/24

# Testen ob eine IP oder CIDR im Set vorhanden ist
ipset test 'setname' 1.2.3.4
ipset test 'setname' 1.2.3.0/24

Ausgabe der vorhandenen Listen

Folgend mal die Ausgabe bei mir, um zu sehen, welche Listen ich im System habe:

root@server01:~# ipset list -t
Name: blacklist
Type: hash:net
Revision: 7
Header: family inet hashsize 131072 maxelem 565536 bucketsize 12 initval 0x97b45a01
Size in memory: 4867344
References: 2
Number of entries: 144246

Name: dach
Type: hash:net
Revision: 7
Header: family inet hashsize 65536 maxelem 565536 bucketsize 12 initval 0xaaf3a9de
Size in memory: 685440
References: 1
Number of entries: 15808

Name: whitelist
Type: hash:net
Revision: 7
Header: family inet hashsize 65536 maxelem 565536 bucketsize 12 initval 0x62435b6c
Size in memory: 2640
References: 2
Number of entries: 14

Name: le-whitelist
Type: hash:net
Revision: 7
Header: family inet hashsize 65536 maxelem 100 bucketsize 12 initval 0x224e7fdd
Size in memory: 3168
References: 0
Number of entries: 25

Erstellen wir unser erstes Set. Die Angabe **`-exist`** legt es nur an, wenn es noch nicht vorhanden ist. **`hash:net`** ist für Netzwerkadressen, perfekt für eine Firewall. `hashsize` und `maxelem` geben die Größe des Sets an. Die Angaben hier sind mehr als ausreichend!

ipset create blacklist hash:net -exist hashsize 16384 maxelem 65536

Im Internet gibt es frei verfügbare **CIDR-Listen** von ganzen Ländern, zum Beispiel bei ipdeny.com. Mit diesen Listen lassen sich ganze Länder sperren, und wenn man die "richtigen" sperrt, ist der Spam auch gleich verschwunden :) Da das Ganze recht mühsam ist, jedes CIDR per Befehl in die Liste zu bekommen, kann man unter Linux wunderbar ein Bash- oder PHP-Script nutzen.

Hat man dann eine solche Blacklist erstellt, wird diese ganz einfach in Iptables eingebunden mit:

# Blacklist aus ipset
$IPT -A FORWARD -i $IFACE_WAN -p tcp --syn -m set --match-set blacklist src -j DROP
$IPT -A OUTPUT -o $IFACE_WAN -m set --match-set blacklist dst -j DROP
$IPT -A INPUT -i $IFACE_WAN -p tcp --syn -m set --match-set blacklist src -j DROP

Die Regeln sperren alles aus dem Set auf **INPUT**, **OUTPUT** und **FORWARD**. Wenn alle Ihre Server dieses IPSet nutzen, können Sie auf OUTPUT und FORWARD verzichten.

Beachten Sie bei INPUT und FORWARD die Angabe **`src`** (Quelle) und bei OUTPUT **`dst`** (Ziel). Zudem werden hier mit der Angabe **`--syn`** nur neue Verbindungen geblockt, was sinnvoll ist.

Das Ganze geht natürlich auch für **`ACCEPT`**, was den Zugriff unheimlich erleichtert. Mein E-Mail-Server z.B. blockt den ganzen Orient, inkl. Russland, China, Nord-Korea und viele afrikanische Länder, sowie Mexiko und Brasilien für eingehende E-Mails.

Für den Zugriff auf meine Postfächer nutze ich diese Regeln, die nur 15 IP-Adressen Zugriff gewährt, was sehr sicher ist. Dazu noch eine Begrenzung der Verbindungen, und DDoS fällt auch aus:

$IPT -t nat -A PREROUTING -i $IFACE_WAN -d $IP_WAN -p tcp -m multiport --dports $EMAIL_PORT -j DNAT --to-destination $IP_MAIL_SERVER
$IPT -A FORWARD -i $IFACE_WAN -o $IFACE_LAN -d $IP_MAIL_SERVER -p tcp -m multiport --dports $EMAIL_PORT -m state --state NEW -m limit --limit $RATE_LIMIT_CONNECTIONS/minute --limit-burst $RATE_LIMIT_BURST -m set --match-set whitelist src -j ACCEPT

Was soll ich sagen, seitdem ist es sehr ruhig geworden.