In diesem Artikel werde ich erklären wie man die Firewall iptables auf einem Debian Linux einrichten um nur Zugriffe auf die erwarteten Dienste (HTTP, HTTPS und SSH) zu erlauben.
Firewall Grundlagen
Iptables bestimmt wie die meisten Firewalls ob ein Packet durch darf oder nicht indem es das eingerichtete Regelwerk von oben noch unten durchgeht. Sobald die erste Regel gefunden wird, die auf das Packet passt, wird diese Angewendet. Wenn unser Regelwerk also wie folgt aussehen würde, dann würden nur SSH Verbindungen zustande kommen und keine HTTP Verbindungen:
- Erlaube alle SSH Verbindungen
- Verbiete alle Verbindungen
- Erlaube HTTP Verbindungen
Es ist also wichtig auf die Reihenfolge der Regeln zu achten und am Schluss sollte immer eine Regel stehen “Verbiete alle Verbindungen” (Deny All).
IPv4 vs IPv6
Die meisten Webserver sind heute sowohl über eine IPv4 wie auch eine neue IPv6 Adresse erreichbar. Die neue Protokollversion wird jedoch oft sträflich vernachlässigt obwohl diese auf den Systemen aktiv ist. Dies ist bei Firewalls im besonderen ein Problem. Wenn wir bei dem Beispiel oben bleiben und in der Standardkonfiguration von IPtables nun via IPv6 eine HTTP Verbindung öffnen würden, so würden wir bis zu dem Webserver kommen. Da IPv6 die Zukunft ist empfehle ich daher explizit nicht einfach alle solchen Verbindungen zu verbieten sondern die Regeln auch für dieses Protokoll einzurichten. Bei iptables ist dies besonders einfach, da die meisten Befehle in beiden Versionen funktionieren und das einzige was man ändern muss ist der Programmname (von iptables zu ip6tables). In meinem Beispielen führe ich daher jeweils beide Befehlssätze (IPv4 & IPv6) auf.
System Vorbereiten
Viel ist nicht nötig um mit der Konfiguration anfangen zu können. Wir müssen im wesentlichen nur ein Packet installieren (welches häufig schon vorhanden ist):
apt-get install iptables iptables-persistent
Filterregeln erstellen
Es gibt bei iptables im wesentlichen zwei Möglichkeiten Regeln zu erstellen. Man kann diese entweder in eine Datei schreiben und diese vom System automatisch laden lassen oder man schreibt die Regeln in den (flüchtigen) Speicher von iptables und lässt iptables am Ende das File schreiben. Ich bevorzuge und verwende hier letzteren Weg, da man hier bei einem Fehler das System nur neustarten muss um einen Weitere Versuch zu erhalten.
Derzeitiges Regelwerk anschauen
Um sich das aktuell vorhandene Regelwerk anzuschauen genügt ein einfacher Befehl. Im Auslieferungszustand sollte das Ergebnis wie folgt aussehen:
IPv4
# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
IPv6
# ip6tables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Regel für SSH Zugang und den Loopback Verkehr einrichten
Die erste Regel sollte immer für den Loopback Verkehr und dann für den Remotezugang verwendet werden. So wird verhindert, dass man sich zu einem späteren Zeitpunkt durch eine falsche Regel aussperrt bzw das System zu viel Zeit verbraucht um z. B. Verbindungen zum lokalen Datenbankserver aufzubauen.
IPv4
# iptables -I INPUT -p tcp --dport 22 -j ACCEPT # iptables -I INPUT -i lo -j ACCEPT
IPv6
# ip6tables -I INPUT -p tcp --dport 22 -j ACCEPT # ip6tables -I INPUT -i lo -j ACCEPT
Alle nicht explizit erlaubten Verbindungen ignorieren
Nachdem die SSH Regel gesetzt wurde können wir iptables scharf schalten und in der base policy einstellen, dass nur explizit erlaubte Verbindungen an unseren Server heran kommen dürfen.
IPv4
# iptables -P INPUT DROP
IPv6
# ip6tables -P INPUT DROP
Regeln für den Webserver anhängen
Für unseren Webserver erlauben wir nun noch HTTP und HTTPS Verbindungen auf den Ports 80 und 443:
IPv4
# iptables -A INPUT -p tcp --dport 80 -j ACCEPT # iptables -A INPUT -p tcp --dport 443 -j ACCEPT
IPv6
# ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT # ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
# iptables -L -n Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
Im Gegensatz zu dem Befehl den wir zum Anlegen der SSH Regel verwendet haben, haben wir hier den Parameter –I durch den Parameter –A ersetzt. Dies bewirkt, dass die Regel an das Regelwerk angehängt (append) wird.
Etablierte Verbindungen zulassen
Mit dem bestehenden Regelwerk würde der Webserver und der SSH Server wie erwartet funktionieren. Allerdings würdet Ihr vermutlich recht schnell über Fehlermeldungen z. B. von apt fallen, da hier verschiedene Verbindungen nicht durch gehen würden. Es ist daher ratsam noch folgende Regel hinzuzufügen:
IPv4
# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
IPv6
# ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
Filterregeln permanent speichern
Alle Regeln die wir bis jetzt eingerichtet haben wären nach einer Reboot wieder weg. Um diese zu speichern ist ein kurzer Befehl notwendig:
# iptables-save > /etc/iptables/rules.v4 # ip6tables-save > /etc/iptables/rules.v6
Nach einem Reboot werden die Regeln nun automatisch wieder gelesen und geladen. Zur Sicherheit sollte man das System neustarten und prüfen ob dies auch der Fall ist.
Alles in einem Script für Schreibfaule
Um das setzen der Regeln zu vereinfachen habe ich alle Befehle in eine bash Datei geworfen. Ihr müsste diese also nur auf euer System herunter laden, die Datei ausführbar machen (chmod +x [name]) und danach ausführen. Den Schritt um die Regeln zu persistieren habe ich nicht mit aufgenommen um Fehlern vorzubeugen.