Firewalling und NAT für die private Cloud

23.05.2013

Der Artikel Virtualisierung mit KVM hat gezeigt, wie man eine virtuelle Maschine auf dem eigenen Heimserver einrichtet. Nun stellt sich die Frage: Wie kommt die VM ins Netz und wie kann man aus dem Internet auf die VM zugreifen?

Netzwerkdiagramm für die Integration der virtuellen Maschinen und der Clients im LAN. Um den Internetzugang für die VMs und alle anderen Clients im Netzwerk zu betrachten, erweitern wir erst einmal das Netzwerkbild aus dem letzten Artikel um das lokale Netzwerk (LAN). Wie die Grafik zeigt, sind nun über das Interface eth0 weitere Systeme mit dem Host verbunden. Diese Systeme sollen den Internetzugang ebenso nutzen können, wie die virtuellen Maschinen. Zusätzlich sollen natürlich alle Endgeräte (egal ob virtuell oder physikalisch) hinter der IP-Adresse des Hostsystems versteckt werden (Hiding-NAT).

Hierfür soll die IP-Tables-Firewall auf dem Hostsystem genutzt werden. Bevor dies allerdings möglich ist, müssen iptables und das Packet iptables-persist installiert werden.

Debian Squeeze

Wie schon im letzten Artikel angemerkt, beschreibe ich das Vorgehen hier für Debian Squeeze. Für Debian Derivate wie Ubuntu ist das Vorgehen daher wahrscheinlich sehr ähnlich, bei anderen Distributionen können größere Anpassungen notwendig sein. Im Prinzip sollte es aber unter jedem Linux so funktionieren.

Wichtig: Auch dieser Artikel liest sich wieder wie eine “Schritt für Schritt”-Anleitung, soll jedoch keine sein. Einfach nur alles aus diesem Posting heraus in eine Konsole zu kopieren wird eher nicht funktionieren. Wie immer gilt die Regel: Erst verstehen, dann nachmachen.

Vorbereitungen

Um IP-Tables zu installieren genügt es als root-Benutzer das entsprechende Paket zu installieren:

sudo apt-get install iptables

Leider speichert IP-Tables die angelegten Firewallregeln normalerweise nicht ab. Viele Tutorials empfehlen daher, die Regeln in rc.local manuell wieder in die Firewall zu importieren. Unter Debian gibt es jedoch einen deutlich eleganteren Weg, welcher über das Paket “iptables-persistent” führt. Dieses Paket erlaubt es die aktuelle Konfiguration der IP-Tables-Firewall zu speichern und aus zwei Konfigurationsdateien (für IPv4 und IPv6) wieder zu restaurieren. Ob man nun die Regeln im laufenden Betrieb über das iptables-Kommando editiert und anschließend über service iptables-persistent save speichert oder ob man die Konfigurationsdatei anpasst und anschließend nur über service iptables-persistent start neu lädt ist Geschmackssache. Wichtig hingegen ist, dass man das Paket überhaupt installiert um sicher zu stellen, dass der eigene Host nach einem Reboot nicht völlig ohne Firewallregelsatz da steht:

sudo apt-get install iptables-persistent

IP-Adressen

Für diesen Artikel nehme ich (wie schon im letzten) an, dass das lokale Netzwerk Adressen aus dem private Subnetz 192.168.0.0/16 stammt. Im Detail habe ich folgende Adressaufteilung vorgenommen:

Adresse/NetzmaskeBeschreibung
192.168.1.0/241Adressbereich der internen Clients und des Servers
192.168.100.0/24Adressbereich der virtuellen Maschinen

Grundregelsatz

Normalerweise sollte jeder, der sich mit der Virtualisierung und einer eigenen Cloud beschäftigt, schon einen Grundregelsatz auf dem Host eingerichtet haben. Daher gehe ich hier nur kurz auf eine rudimentäre Firewallkonfiguration ein um klare Verhältnisse für die nächsten Schritte zu schaffen:

Gedächtnisprotokoll

Natürlich verwendet man heutzutage für das Firewalling meistens das Connection-Tracking-Modul des Linux-Kernel. Hierdurch werden einmal über die Firewall aufgebaute Verbindungen erkannt und Datenpakete, welche zu diesen Verbindungen gehören, können (korrekte Regeln vorausgesetzt) unbehelligt passieren. Hierdurch ist es nicht mehr nötig, für jede ausgehende Firewallregel eine passende Antwort-Regel zu definieren.

Prinzipiell möchte man ja, dass der Host, alle VMs und Clientsysteme ins Internet können, dass aber aus dem Internet niemand ungefragt auf die internen Systeme zugreifen kann. Zwar erledigt hier das Hiding-NAT schon einen Teil der Arbeit, da alle internen Systeme hinter der IP des Host-System versteckt werden und somit der “Angreifer” aus dem Internet keinen einzelnen Client ansprechen kann. Nichts desto trotz, müssen die Firewallregeln den Zugriff aus dem Internet zuverlässig blockieren.

Interface oder IP

Beim Erstellen der Firewallregeln stellt sich meist die Frage, ob man diese lieber auf die einzelnen Interfaces2 oder auf die Quell-/Ziel-IP-Adressen3 beziehen möchte. Die Nutzung von Interfaces wird häufig als sicherer angesehen, da sie IP-Spoofing verhindert. Allerdings bietet Linux mit dem Reverse-Path-Forwarding4 (RPF) einen guten Spoofingschutz. RPF leitet nur Pakete weiter, bei denen ein hypothetisches Antwortpaket das System auf dem gleichen Interface verlassen würde. Ein Angreifer kann daher keine IP-Adresse spoofen, welche nicht über sein Interface weitergeleitet würde. Ist dieser Schutz aktiv und strikt eingestellt (Wert 1 in net.ipv4.conf.all.rp_filter) kann durchaus auch die Quell-IP-Adresse zu Filterung herangezogen werden. Dies entkoppelt die Firewallregeln von der tatsächlichen Zuordnung und Benennung der Netzwerkinterfaces. Natürlich hat auch RPF einige Nachteile. Da diese aber primär bei komplexen Routingszenarien eine Rollen spielen, werde ich für dieses Beispiel RPF verwenden, da es mehr Freiraum bei der tatsächliche Benennung der Interfaces lässt und somit das Beispiel universell einsetzbar macht.

Basisregeln

Zuerst müssen Basisregeln geschaffen werden, welche alle ausgehenden Pakete akzeptieren und eingehende Pakete nur dann weiterleiten, wenn diese zu einer bereits bestehenden Verbindung gehören:

# Loopback-Verbindungen immer erlauben
iptables -A INPUT -s 127.0.0.0/8 -j ACCEPT


# Ausgehende Verbindungen und ihre Antworten erlauben.
# Eingehende Verbindungen nur aus dem internen Netzwerk (nicht aus dem VM-Netzwerk) erlauben.
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -m comment --comment "Verbindungen vom internen Netzwerk auf den Host erlauben." -j ACCEPT
iptables -A INPUT -j DROP -m comment --comment "Cleanup-Regel"

# Schon bestehende Verbindungen immer Weiterleiten.
# Neue Verbindungen dürfen nur aus dem internen Netzwerk aufgebaut werden.
# Das Hosting-Netzwerk darf ins Internet aber nicht in das interne Netzwerk.
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT
iptables -A FORWARD -s 192.168.100.0/24 ! -d 192.168.1.0/24 -j ACCEPT
iptables -A FORWARD -j DROP -m comment --comment "Cleanup-Regel"

# Hiding-NAT für alles, was kein internes System anspricht.
# Hier wird das gesamte 192.168.0.0/16-Subnetz versteckt, so dass bei Erweiterungen nicht extra an diese Regel gedacht werden muss.
iptables -t nat -A POSTROUTING ! -d 192.168.0.0/16 -j MASQUERADE

Diese Grundkonfiguration schützt nun die internen Hosts vor dem Zugriff aus dem Internet und erlaubt alle ausgehenden Verbindungen. Sie sollte nun über service iptables-persistent save abgespeichert werden, damit sie auch bei nächsten Systemstart wieder aktiv ist.

Scharf schalten

Da nun die Firewall korrekt eingerichtet ist, kann das IP-Forwarding auf dem Host-System aktiviert werden. Erst danach werden die Datenpakete auch zwischen den einzelnen Netzwerkinterfaces weitergeleitet. Um die nötigen Einträge in /proc/sys/net persistent zu machen, legt man am besten unter /etc/sysctl.d/ eine neue Datei mit dem Namen ipforward.conf an. In diese Datei fügt man nun die folgenden Zeilen ein:

net.ipv4.ip_forward=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.log_martians=1
net.ipv4.conf.all.log_martians=1

Diese aktivieren das Routing von Paketen, das RPF und protokollieren Pakete, welche durch RPF verworfen werden. Anschließend können die Einstellungen über sysctl -p oder einen Neustart des Hostsystems aktiviert werden.

Nachsendeauftrag

Dies war nun eine ungewöhnlich lange Vorrede für eine wirklich kleine Veränderung an den Firewallregeln. Damit ein VM-Gast einen Dienst im Internet bereitstellen kann, muss der Host Anfragen auf einem bestimmten TCP-Port an das Gastsystem weiterleiten. Auch hierzu ist IP-Tables über eine DNAT-Regel5 fähig. Das einzige - kleine - Problem dabei ist, dass sich die IP-Adresse des lokalen Interfaces bei DSL-Anschlüssen gerne mal ändert. Die IP-Adresse in der Firewallkonfiguration zu verewigen ist daher keine besonders praktikable Lösung. Leider ist die IP aber nötig, damit nur Pakete umgeschrieben werden, welche sich direkt an den VM-Host richten. Glücklicherweise kennt IP-Tables hierfür den addrtype-Match. Hierdurch lassen sich alle Datenpakete aussortieren, welche an eine der IP-Adressen des lokalen Hosts gerichtet sind. Die folgende Regel leitet also alle Anfragen, welche den VM-Host auf Port 80 erreichen, direkt an den Gast mit der IP-Adresse 192.168.100.100 weiter:

iptables -A PREROUTING ! -d 192.168.0.0/20 -p tcp --dport 80 -m addrtype --dst-type LOCAL -j DNAT --to 192.168.100.100

Die Einschränkung auf Pakete, welche nicht an 192.168.0.0/20 gerichtet sind, ist notwendig, da auch diese lokalen Interfaces den Adresstyp LOCAL besitzen. Die Einschränkung kann entfallen, wenn auch Anfragen an Port 80 von den privaten IP-Adressen des Servers an die VM weitergeleitet werden sollen.

Mehere Ports

Manchmal sollen mehrere Ports je Server weitergeleitet werden. Natürlich kann für jeden Port eine eigene Zeile in der PREROUTING-Tabelle angelegt werden, jedoch ist das nicht unbedingt übersichtlich. Besser geht das mit dem multiport-Match, welchen IP-Tables ebenfalls anbietet. Die folgende Zeile leitet die Ports 80 und 8080 an die VM auf 192.168.100.100 weiter:

iptables -A PREROUTING ! -d 192.168.0.0/20 -p tcp -m multiport --dports 80,8080 -m addrtype --dst-type LOCAL -j DNAT --to 192.168.100.100

Wiederfinden des Hosts

Natürlich ist es gerade bei DSL-Anschlüssen mit wechselnder IP-Adresse notwendig, dem eigenen System im Internet einen Namen zu geben. Hierfür bieten sich eine Vielzahl von Diensten an, welche dynamisches DNS bereitstellen. Eine Liste solcher Anbieter findet sich hier. Persönlich kann ich noch den deutschen Anbieter twodns empfehlen. Die Schritte zum Einrichten eines solchen Dienstes sind im Internet an vielen Stellen beschrieben, daher gehe ich hier nicht weiter darauf ein.

Wie weiter?

Da nun auch der Internetzugang über die VMs funktioniert, kann es nun im dritten Teil “Installation von Debian in der VM” um die Installation eines Debian Gastsystems gehen.


  1. Der Server hat in diesem Beispiel die IP-Adresse 192.168.1.100/24. ↩︎

  2. iptables-Option -i ↩︎

  3. iptables-Option -s (Source) oder -d (Destination) ↩︎

  4. Häufig (allerdings nicht RFC-Konform) als Reverse-Path-Filtering bezeichnet. ↩︎

  5. DNAT = Destination-NAT ↩︎