Aus Linux-Magazin 12/2005

Hochverfügbare Linux-Firewall mit Zustandssynchronisation der Cluster-Knoten

Linux beherrscht Firewalling meisterlich, nur für den Einsatz in geschäftskritischen Umgebungen fehlte lange etwas. Nun rüstet Ct_sync die für Hochverfügbarkeit vermisste Synchronisation nach.

Hochverfügbarkeit ist eine übliche Anforderung an geschäftskritische Systeme. Oft gilt das auch für die Internetanbindung, weil beim Ausfall kein Kunde die Firmen-Website erreicht und wichtige E-Mails hängen bleiben. Da sämtlicher Verkehr durch eine Firewall läuft, wird diese zum neuralgischen Punkt: Sie darf nicht ausfallen. Linux hat sich längst als hervorragende Firewall-Plattform etabliert. Der Aufbau einer hochverfügbaren Firewall mit Netfilter/IPtables war in der Vergangenheit jedoch ein Problem.

Reserverechner

Hochverfügbare Lösungen sind meist als Cluster mit mindestens zwei Knoten implementiert. Im Regelbetrieb erledigt der Master die anstehenden Aufgaben, während der passive Knoten (Slave, Hot-Standby) lediglich seinen Master überwacht und bei dessen Ausfall die Funktion übernimmt. Außenstehende sollen davon möglichst nichts bemerken; für sie bleibt der Cluster ständig verfügbar.

Die schwierigste Aufgabe bei jeder Art Hochverfügbarkeit ist die Synchronisation. Beide Knoten müssen ihrer Anwendung den identischen Datenstand vorlegen. Bei einem Webserver, der statische Seiten ausliefert, ist das trivial. Bei einem Forum oder Gästebuch muss der Administrator aber sicherstellen, dass neue Beiträge auf beiden Systemen landen und die Datenbestände konsistent bleiben.

Ob Hochverfügbarkeit einfach oder schwierig zu implementieren ist, hängt primär von der Synchronisation ab. Zusätzlich ist Software nötig, die den Ausfall eines Knoten erkennt und einen Failover auf den Hot-Standby durchführt – Linux-Lösungen dafür gibt es längst in verschiedenen Ausprägungen.

Bei den Firewallregeln gelingt die Synchronisation noch recht einfach, sie ändern sich nur, wenn der Admin eine neue Policy einspielt. IPtables-Firewalls ziehen aber auch dynamische Daten zu Rate, um über das Schicksal eines Pakets zu entscheiden: In einer Zustandstabelle (Connection Tracking Table) speichern sie Informationen über bestehende Verbindungen. Ob ein Paket die Firewall passieren darf, hängt von den statischen Regeln und der dynamischen Zustandstabelle ab.

Dank dieser Tabelle kann der Administrator wesentlich bessere und sicherere Paketfilter konfigurieren als bei rein statischen Filtern. Listing 1 zeigt ein einfaches Firewallskript. Zeile 10 legt bei jeder neuen Verbindung, die vom internen Netz ins externe geht, einen Eintrag in der Zustandstabelle an und gestattet den Transfer. Die Regel in Zeile 11 passt zu allen Folgepaketen innerhalb dieser Verbindung. Um die Zugehörigkeit einzelner Pakete zu bestimmen, konsultiert Netfilter die Zustandstabelle.

Doppelt hält besser

Eine hochverfügbare Firewall besteht aus (mindestens) zwei identischen Systemen. Damit ein System den Ausfall des anderen erkennt, muss der Administrator eine geeignete Software installieren. Hier bietet sich Keepalived an [1]. Dieser Userspace-Daemon implementiert das Virtual-Router-Redundancy-Protokoll (VRRP, [2]), mit dem sich hochverfügbare Router und Firewalls realisieren lassen. Alternativ zu VRRP würde sich UCARP eignen ([3] und der BSD-Firewall-Artikel in diesem Heft).

VRRP verwaltet eine virtuelle Router-IP-Adresse und garantiert, dass sie verfügbar bleibt, solange mindestens ein Knoten im Cluster funktioniert. Im Normalbetrieb hört der aktive Knoten auf sie. Bei einem Ausfall übernimmt der Hot-Standby die IP-Adressen und damit auch die Funktion des virtuellen Routers. Die Rechner in den angeschlossenen Netzen bemerken davon nichts, Gratious-ARP bringt die ARP-Caches der Nachbarn auf den Stand der neuen MAC-Adressen.

Leben erhalten

Viele Linux-Distributionen enthalten das Keepalived-Paket bereits. Wo es fehlt, ist es bei Bedarf schnell aus den Quellen installiert. Alle Cluster-Knoten brauchen eindeutige eigene IP-Adressen, bei Firewalls also je eine interne und eine externe IP. Zusätzlich vergibt der Admin eine interne und eine externe virtuelle Firewall-IP-Adresse für den Cluster. Diese Daten trägt er in die Konfigurationsdatei des Keepalive-Daemon ein. Listing 2 eignet sich als Ausgangsbasis.

Keepalived verwaltet in diesem Setup zwei Netzwerkkarten (Instanzen) pro Maschine: »VI_1« (Zeilen 9 bis 21) und »VE_1« (Zeile 24 bis 37). Damit der Failover für beide IP-Adressen gleichzeitig erfolgt, fasst die Gruppe »VG1« beide Instanzen zusammen (Zeilen 1 bis 6).

Bei der Definition der Instanzen erhält jede eine eigene »virtual_router_id« (Zeilen 12 und 28). Der Parameter »state« definiert den Zustand des Knotens beim Start von Keepalived: Beide Instanzen dieser Gruppe beginnen als Master (Zeilen 10 und 25). Im Betrieb entscheidet der »priority«-Wert, wer beim Ausfall einer Maschine neuer Master wird.

Authentischer Knoten

Die VRRP-Kommunikation der Clusterknoten läuft auf Wunsch authentifiziert. Gerade für eine Sicherheitsanwendung ist dies sehr zu empfehlen. Die »authentication«-Konfiguration (Zeilen 14 bis 17 und 30 bis 33) sorgt für die IPsec-verschlüsselte Authentifizierung (»auth_type AH«, unverschlüsselt wäre »PASS«). Das Passwort muss auf allen Systemen des Clusters identisch sein. Der Parameter »lvs_sync_daemon_interface« wählt die interne Netzwerkkarte »eth0« für diese Kommunikation aus.

Der Slave erhält eine fast identische Konfiguration, nur lauten dort die beiden »state«-Parameter auf »BACKUP« und die »priority«-Einträge erhalten den Wert 50. Nach seinem Start auf Master und Slave überwacht Keepalived den Zustand beider Knoten und stuft beim Ausfall des Masters einen Slave zum neuen Master hoch. Hat der Administrator sein Firewallskript vorher auf beiden Knoten installiert und gestartet, bleibt die Funktionalität der Firewall auch beim Ausfall eines Knoten bestehen.

Verbindungsverlust

Nach der Übernahme kennt der neue Master den Zustand der vorhandenen Verbindungen nicht. Client und Server wissen nichts vom Rollentausch im Firewall-Cluster und erwarten, dass ihre Verbindungen weiterhin existieren. Ist die Firewall wie in Listing 1 konfiguriert und erhält sie von einem extern angesiedelten Server ein TCP-ACK-Paket, das zu einer bestehenden Verbindung gehört, findet Netfilter in seiner noch leeren Zustandstabelle keinen passenden Eintrag und verwirft das Paket.

Sendet jedoch der Client ein TCP-ACK, akzeptiert Netfilter es als »NEW« und trägt die (für ihn neue) Verbindung in die Zustandstabelle ein. Die Zustandsüberwachung wertet neben TCP-SYN- auch TCP-ACK-Pakete als Verbindungsaufbau. Damit erkennt eine Firewall sogar nach einem Failover oder einem Reboot vorher gestartete Verbindungen. Das funktioniert aber nur, wenn die Regeln nicht gleichzeitig prüfen, ob es sich tatsächlich um ein TCP-SYN-Paket handelt. Bei folgender Regel würde der Failover die Verbindung kappen:

iptables -A FORWARD -i $INTDEV -o $EXTDEV -p tcp --syn -m state --state NEW -j ACCEPT

Solche Regeln sind noch vermeidbar. Schwieriger wird es bei anderen Aufgaben der Firewall: In vielen Fällen sorgt sie für Adressübersetzungen (NAT, Network Address Translation).

Bei SNAT (Source-NAT) auf der Firewall scheitert die Wiedererkennung. Die übliche Variante Masquerading (auch N-zu-1-SNAT genannt) verarbeitet n Clients innen und gibt ihnen eine externe Adresse des NAT-Gateway. In diesem Szenario modifiziert die Firewall zusätzlich zur IP-Adresse oft auch den Absenderport. Die Technik heißt auch NAPT (Network Address and Port Translation).

SNAT stört den Failover

Ist der Originalport auf der Firewall belegt, wählt SNAT den nächsten verfügbaren. Das Ergebnis hängt vom aktuellen Zustand der Firewall ab und ist nicht deterministisch bestimmt. Wenn vor dem Failover ein Client eine Verbindung vom TCP-Port 1025 aus aufgebaut hat und die Firewall stattdessen Port 1089 einträgt, erwartet der Server alle weiteren Pakete dieser Verbindung ebenfalls vom Port 1089. Nach dem Failover wird der neue Firewallknoten dem Port wahrscheinlich eine andere Nummer zuweisen. Davon weiß der Server nichts und erkennt die folgenden Pakete nicht mehr.

Um den Verbindungsverlust zu vermeiden, ist es nötig, dass die Knoten des Firewall-Clusters ihre Zustandstabellen synchron halten. In ihnen stehen bei Netfilter/IPtables auch die Informationen über den NAT-Zustand.

Listing 1: IPtables mit
Status

01 #!/bin/sh
02 
03 INTDEV=eth0 # interne Netzwerkkarte
04 EXTDEV=eth1 # externe Netzwerkkarte
05 
06 iptables -P INPUT DROP    # Alle Pakete verwerfen,
07 iptables -P OUTPUT DROP   # die nicht durch Regeln
08 iptables -P FORWARD DROP  # akzeptiert werden
09 
10 iptables -A FORWARD -i $INTDEV -o $EXTDEV -m state --state NEW -j ACCEPT
11 iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

Listing 2: Keepalived, Master
ohne Ct_sync

01 vrrp_sync_group VG1 {
02   group {
03     VI_1
04     VE_1
05   }
06 }
07 
08 ! Interne virtuelle IP-Adresse
09 vrrp_instance VI_1 {
10   state MASTER
11   interface eth0
12   virtual_router_id 1
13   priority 100
14   authentication {
15     auth_type AH
16     auth_pass Passwort
17   }
18   virtual_ipaddress {
19     192.168.1.1/24 brd 192.168.1.255 dev eth0
20   }
21 }
22 
23 ! Externe virtuelle IP-Adresse
24 vrrp_instance VE_1 {
25   state MASTER
26   interface eth1
27   lvs_sync_daemon_interface eth0
28   virtual_router_id 2
29   priority 100
30   authentication {
31     auth_type AH
32     auth_pass Passwort
33   }
34   virtual_ipaddress {
35     192.168.2.1/24 brd 192.168.1.255 dev eth1
36   }
37 }

Ct_sync

Viele kommerzielle Paketfilter bieten Lösungen für dieses Problem. Auch OpenBSD hat mit PFsync seit einiger Zeit eine ausgereifte Implementierung (siehe Artikel in diesem Heft). Für Linux fehlte etwas Entsprechendes. Harald Welte stellte erste Ideen hierzu im Jahr 2002 auf dem Ottawa Linux Symposium vor [4]. Krisztian Kovacz realisierte sie in einer ersten Proof-of-Concept-Implementierung namens Ct_sync für Kernel 2.4.26 [5]. Inzwischen ist Ct_sync gereift und auch für Kernel 2.6.10 verfügbar.

Ct_sync arbeitet als Kernelmodul, das sich um die Synchronisation der Zustandstabelle kümmert. Ändert sich die Tabelle auf dem Master, meldet Ct_sync das Ereignis an den Conntrack-Sync-Sender-Thread, der die Meldungen sammelt und an den Slave weiterleitet. Der Slave übergibt die Nachrichten – an seiner eigenen Zustandsüberwachung vorbei – dem Conntrack-Sync-Receive-Thread, der die Events aus dem Paket extrahiert und seine Verbindungstabelle anpasst.

Eigenes Multicast-Protokoll

Für die Kommunikation zwischen Master und Slave haben Welte und Kovacz ein UDP-Multicast-Protokoll entwickelt, das auf Authentifizierung und Verschlüsselung verzichtet. Sie wollten ein unkompliziertes und schnelles Protokoll, das Paketverluste erkennt und behebt: Eine Sequenznummer in jeder Nachricht sorgt dafür, dass der Empfänger den Verlust bemerkt und das Paket erneut anfordert. Um die übertragenen Daten vor Manipulation und Ausspähen zu schützen, empfiehlt es sich, ein dediziertes Netzwerk ausschließlich für Ct_sync-Verkehr zu verwenden.

Beim Ausfall des Masters macht der Slave – dank Ct_sync – dort weiter, wo der Master aufgehört hat. Startet ein Slave neu (erster Start oder Neustart nach Wartung oder Ausfall), beginnt er mit einer leeren Zustandstabelle die Meldungen des Masters entgegenzunehmen. Da der Master nur Änderungen meldet, würden dem Slave alle älteren Informationen fehlen. Ct_sync erledigt aber auch die initiale Synchronisation. Der aktuelle Code überträgt bis zu 4000 Einträge pro Sekunde. In ein UDP-Paket mit 1500 Byte passen vier Einträge, es entsteht eine Netzwerklast von 1,5 MByte/s. Serielle Leitungen scheiden für den Transport der Sync-Pakete daher aus.

Verlust auf Ebene 2

Um die Konfiguration des Firewall-Clusters zu vereinfachen, geht Ct_sync einen anderen Weg als das VRRP-Protokoll. Statt virtuelle IP-Adressen zu verwenden, die es bei einem Failover übergeben müsste, erlaubt Ct_sync die Verwendung identischer IP-Adressen für alle Clusterknoten. Dem drohenden IP-Adresskonflikt begegnet Ct_sync mit einem Layer-2-Drop: Alle Slaves verwerfen sämtliche Pakete auf der Netzwerkschicht 2. Nur die Netzwerkkarte, über die Ct_sync die Synchronisationsmeldungen transportiert, ist vom Drop ausgenommen.

Beim Laden des Moduls sagt der Parameter »syncdev«, welches Interface dafür zuständig ist:

modprobe ct_sync syncdev=eth2 notrack=1 l2drop=1 cmarkbit=31

Die »notrack«-Angabe nimmt Pakete, die über diese Netzwerkkarte das System erreichen, von Einflüssen der Zustandstabelle aus. »l2drop« aktiviert den Layer-2-Drop für Slaves. Mit »cmarkbit« ist es möglich, nur markierte Verbindungen satt der vollständigen Zustandstabelle zu synchronisieren. Anschließend muss der Admin den Systemzustand festlegen:

echo 2 > /proc/sys/net/ipv4/netfilter/ct_sync/state

Die möglichen Werte sind 0 (keine Synchronisation), 1 (Slave) und 2 (Master). Tabelle 1 erklärt die weiteren Parameter im Verzeichnis »/proc/sys/net/ipv4/netfilter/ct_sync«.

Ct_sync sorgt lediglich für die Synchronisation und den Layer-2-Drop, es erkennt keinen Ausfall eines Knotens. Dazu ist weitere Software nötig, die die Gesundheit der beteiligten Systeme überwacht und den Failover einleitet. Der bereits genannte Keepalived bietet sich hierfür an. Sind beide Systeme identisch konfiguriert (Abbildung 2) und verwenden den Layer-2-Drop, muss Keepalived beim Failover nur den Ct_sync-Statuseintrag im Proc-Verzeichnis auf »2« setzen.

Die Keepalived-Konfiguration bietet dafür die Schlüsselwörter »notify_master« und »notify_backup«. Im ersten Fall läuft das angegebene Skript, sobald der Knoten Masterstatus erlangt; das zweite Skript kommt beim Übergang in den Slave-Zustand zum Zuge (Listing 3, Zeilen 13 und 14).

Die Skripte brauchen nur den State-Eintrag für Ct_sync im Proc-Verzeichnis zu setzen. Taucht ein neuer Master mit höherer Priorität im Cluster auf, dann degradiert er den alte Master zum Slave und aktiviert dessen Layer-2-Drop.

Beim Einsatz von Layer-2-Drop funktionieren die Keepalived-Überwachung und -Kommunikation nur noch im Synchronisationsnetzwerk, auf externen und internen Firewall-Interfaces verwerfen die Slaves alle Pakete. Die »lvs_sync_daemon_interface«-Anweisung in Listing 3 sorgt dafür, dass Keepalived per »eth2« mit seinem Gegenüber kommuniziert. Die Instanz überwacht aber »eth0« (Zeile 3), das interne Interface.

Ct_sync installieren

Ct_sync ist derzeit noch kein fester Bestandteil des Linux-Kernels. Der Code liegt im Netfilter-Subversion-Server. Folgendes Kommando lädt ihn für Linux 2.4.26:

svn co http://svn.netfilter.org/netU
filter/trunk/netfilter-ha

Für Kernel 2.6 ist ein anderer Pfad gültig:

svn co http://svn.netfilter.org/netU
filter/branches/netfilter-ha/linux-2.6

Die Ct_sync-Patches liegen anschließend im Unterverzeichnis »linux-2.6/patches«. Sie passen am besten zum Vanilla-Kernel, beim aktuellen Ct_sync wären das 2.4.26 oder 2.6.10. Das Patches-Verzeichnis in die Kernelquellen kopieren und per »quilt«-Befehl [6] einspielen – für Kernel 2.6 lauten die Kommandos:

tar xjf linux-2.6.10.tar.bz2
cd linux-2.6.10
cp -r ../linux-2.6/patches .
quilt push -a

Anschließend den Ct_sync-Quelltext an die richtigen Stellen kopieren. Der steht (noch) nicht unter der Regie von Quilt:

cp ../linux-2.6/ct_sync/*.h U
   include/linux/
cp ../linux-2.6/ct_sync/*.c U
   net/ipv4/netfilter/

Nun ist der Kernel bereit zur Konfiguration. Wichtig: Bei den Netfilter-Optionen die Synchronisation aktivieren (Abbildung 1). Nach dem Übersetzen und einem Reboot mit dem neuen Kernel steht das Ct_sync-Kernelmodul zur Verfügung.

Wählerisch

Will der Admin nur ausgewählte Verbindungen synchronisieren, erreicht er das mit Hilfe der Firewallmarkierung von Netfilter. Beim Laden des Ct_sync-Moduls gibt er den Parameter »cmarkbit« an, dann synchronisiert Ct_sync nur die Verbindungen, die diese Markierung besitzen. Zum Beispiel ist die Synchronisation von DNS-Verbindungen unnötig:

iptables -t mangle -A PREROUTING -p udp --dport 53 -j ACCEPT
iptables -t mangle -A PREROUTING -m state --state NEW -j CONNMARK --set-mark 0x40000000/0x40000000

Mit Ct_sync besitzt Linux eine Technik für hochverfügbare, zustandsorientierte Firewall-Cluster. In Entwicklung befinden sich Funktionen für Active-Active-Cluster, bei denen sich die HA-Knoten per Loadbalancing im Regelbetrieb ihre Last teilen. Erster Code ist bereits auf dem Subversion-Server veröffentlicht.

Abbildung 1: Der Schalter für die Ct_sync-Synchronisation versteckt sich in der Kernelkonfiguration unter »Device Drivers | Networking support | Networking options | Network packet filtering | IP: Netfilter Configuration | Connection tracking events«.

Abbildung 1: Der Schalter für die Ct_sync-Synchronisation versteckt sich in der Kernelkonfiguration unter »Device Drivers | Networking support | Networking options | Network packet filtering | IP: Netfilter Configuration | Connection tracking events«.

Gut trotz kleiner Schwächen

Leider ist Ct_sync noch nicht in der Lage, mit Conntrack- und NAT-Helfermodulen zusammenzuarbeiten. FTP-Datenverbindungen bleiben daher von der Synchronisation ausgeschlossen. Auch die Zusammenarbeit mit dem TCP-Window-Tracking-Code fehlt noch. Diese schärfere TCP-Zustandsüberwachung ist daher abzuschalten:

echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal

Beim Autor ist Ct_sync mit dem Linux-Kernel 2.4.26 seit Monaten erfolgreich im Einsatz. Die Firewall-Cluster bewältigen spielend Bandbreiten von mehreren 100 MBit/s. (fjl)

Tabelle 1:
Ct_sync-Parameter (Kernel 2.6.10)

 

Parameter

Erklärung

state

0=None, 1=Slave, 2=Master

maxage

Maximales Alter der Meldung in der Warteschlange, bevor eine
Synchronisation erfolgen muss.

send_burst

Spätestes bei dieser Anzahl von Meldungen in der
Warteschlange schickt Ct_sync alle ausstehenden Nachrichten an den
Slave.

recv_burst

Bei dieser Anzahl empfangener Pakete wird die Zustandstabelle
auf dem Slave aktualisiert.

recovery_threshold

Sobald mehr Pakete verloren gehen, erfolgt eine komplette
Reinitialisierung des Slave.

Abbildung 2: Die Knoten im Firewall-Cluster verwenden aus Sicherheitsgründen ein eigenes Interface (»eth2«) zur Synchronisation per Ct_sync. Keepalived überwacht den Zustand der Geräte und schaltet bei einem Ausfall auf den Slave um.

Abbildung 2: Die Knoten im Firewall-Cluster verwenden aus Sicherheitsgründen ein eigenes Interface (»eth2«) zur Synchronisation per Ct_sync. Keepalived überwacht den Zustand der Geräte und schaltet bei einem Ausfall auf den Slave um.

Listing 3: Keepalived mit
Ct_sync

01 vrrp_instance VI_1 {
02   state MASTER
03   interface eth0
04   virtual_router_id 1
05   priority 100
06   lvs_sync_daemon_interface eth2
07   authentication {
08     auth_type AH
09     auth_pass Passwort
10   } virtual_ipaddress {
11     192.168.1.1/24 brd 192.168.1.255 dev eth0
12   }
13   notify_master /etc/keepalived/script_master.sh
14   notify_backup /etc/keepalived/script_backup.sh
15 }

Infos

[1] Keepalived, VRRP-Implementierung für Linux: [http://www.keepalived.org]

[2] RFC 3768, „Virtual Router Redundancy Protocol (VRRP)“: [http://www.ietf.org/rfc/rfc3768.txt]

[3] UCARP: [http://www.ucarp.org]

[4] Harald Welte, „How to replicate the fire (OLS)“: [http://www.linux.org.uk/~ajh/ols2002_proceedings.pdf.gz]

[5] Krisztian Kovacz, „Netfilter Failover – Connection Tracking State Replication“: [http://people.netfilter.org/~kadlec/workshop-2003-budapest/nfws_ha.sxi]

[6] Patchwork Quilt: [http://savannah.nongnu.org/projects/quilt]

Der Autor


Ralf Spenneberg arbeitet als freier Unix/Linux-Trainer, Berater und Autor. Mit seinem Unternehmen „Open Source Training“ führt er Schulungen und Beratungen durch. Er veröffentlichte bereits mehrere Bücher zu Intrusion Detection und VPN. In wenigen Wochen erscheint sein neues Buch „Linux Firewalls mit Iptables & Co“.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben