|
Unter Verbindungsverfolgung oder auch connection tracking
versteht man das Speichern von Statusinformationen einer Verbindung
(z.B. Quell- und Zieladresse, Portnummern, Protokolltyp,
Timeouts,...). Bei iptables ist dafür die state-Option
zuständig (siehe
Explizite Erweiterungen).
Die Verbindungsverfolgung erfolgt entweder in der
PREROUTING- oder in der OUTPUT- Kette. Die Pakete werden
automatisch defragmentiert, Fragmente (siehe
Fragmente filtern)
brauchen also nicht behandelt werden.
Die Statustabellen für UDP- und TCP Verbindungen werden in
/proc/net/ip_conntrack gehalten. Wie die Einträge in dieser
Tabelle aussehen, wird später gezeigt. Die maximale
Anzahl der Verbindungen, die diese Tabelle aufnehmen kann,
wird in /proc/sys/net/ipv4/ip_conntrack_max gespeichert und
je nach verfügbaren Hauptspeicher mit einem Standardwert
belegt (z.B. 512 MB, ip_conntrack_max = 32760).
Die nächsten drei Unterkapiteln erläutern die
Verbindungsverfolgung für die Protokolle udp, tcp und icmp,
im letzten Kapitel wird das FTP-Protokoll behandelt.
|
UDP wird auch als verbindungsloses (zustandsloses) Protokoll
bezeichnet, da im Header keine Sequenznummern oder Ähnliches
zu finden sind. Das bedeutet aber nicht, dass man UDP-Verbindungen
nicht aufspüren und verfolgen kann. Es gibt immer noch andere
nützliche Informationen, welche das genau sind, zeigt der folgende
Eintrag in der Statustabelle:
udp 17 21 src=192.168.1.3 dst=192.168.1.88 sport=1032 dport=53 [UNREPLIED] src=192.168.1.88 dst=192.168.1.3 sport=53 dport=1032
use=1
|
Aus diesem Eintrag kann Folgendes herausgefunden werden:
-
Protocol = udp (IP Protokollnummer ist 17)
-
Der Eintrag läuft nach 21 Sekunden ab, und ist danach nicht mehr gültig.
-
Quell und Zieladressen mit entsprechenden Ports der Anfrage
-
Quell und Zieladressen mit entsprechenden Ports der erwarteten Antwort
-
Die Verbindung ist als UNREPLIED markiert, es wurde also noch nichts empfangen
Die folgenden Regel erlaubt UDP-Verbindungen vom eigenen
Rechner (bei einer restriktiven Politik)
root@linux #
iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
root@linux #
iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
|
Udp Timeouts (Zeitlimits) werden vor dem Kompilieren in
/usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_udp.c
gesetzt, und zwar genau an dieser Stelle:
ip_conntrack_proto_udp.c
|
*** Datei /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_udp.c
#define UDP_TIMEOUT (30*HZ)
#define UDP_STREAM_TIMEOUT (180*HZ)
****** Ende Datei
|
Eine einzelne TCP-Anforderung wird normalerweise 30 Sekunden
in der Tabelle vorgehalten, im Falle unseres Eintrages
(Beispiel oben) sind es nur noch 21 Sekunden. Das bedeutet, dass
die Verbindungsanfrage bereits seit 9 Sekunden bestand ohne
dass ein Antwortpaket empfangen wurde. Wenn nun ein Antwortpaket
eintrifft, wird das Zeitlimit auf 30 Sekunden zurückgesetzt und
die UNREPLIED Markierung gelöscht. Der Tabelleneintrag hätte
nun folgendes Aussehen:
udp 17 28 src=192.168.1.3 dst=192.168.1.88 sport=1032 dport=53 src=192.168.1.88 dst=192.168.1.3 sport=53 dport=1032 use=1
|
Sollten mehrere Verbindungsanfragen (multiple requests) und
Verbindungsantworten zwischen gleichen Socket-Paaren auftreten,
so wird ein Stream festgestellt und das Zeitlimit auf 180
Sekunden erhöht. Der Tabelleneintrag hätte nun folgendes
Aussehen:
udp 17 177 src=192.168.1.2 dst=192.168.1.50 sport=1032 dport=53 src=192.168.1.50 dst=192.168.1.2 sport=53 dport=1032 [ASSURED]
use=1
|
Man erkennt die Markierung ASSURED, das bedeutet, dass die
Verbindung am längsten aufrechterhalten wird. Wir erinnern
uns an den Parameter ip_conntrack_max, der die maximale
Verbindungsanzahl festlegte. Bei Erreichen dieses Limits
werden die mit UNREPLIED markierten Verbindungen zuerst
gelöscht und die mit ASSURED Markierten zuletzt.
Anmerkung: Es gibt weder für UDP noch für TCP Verbindungen
ein absolutes Zeitlimit.
|
Jede TCP-Verbindung wird mittels eines Drei-Wege-Händeschüttelns
(three-way handshake) aufgebaut. Kurz zur Erinnerung:
Es beginnt mit einer Synchronisationsanforderung (SYN)
des Client, der Server bestätigt diese
Synchronisationsanforderung (SYN+ACK) und
letztendlich bestätigt der Client die aufgebaute Verbindung
(ACK). Alle weiteren TCP-Pakete dieser Verbindung werden
ebenfalls mit einem ACK-Flag gekennzeichnet.
Neben SYN und ACK-Flags beinhaltet der TCP-Header eine
32 bit Sequenz (Sequence Number) eine ACK-Nummer
(Acknowledgment Number), sodass ein TCP-Paket eindeutig
einer Verbindung zugewiesen werden kann.
Zur Verfolgung von TCP-Verbindungen dienen
beispielsweise folgende Regeln:
root@linux #
iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
root@linux #
iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
|
An dieser Stelle sollen die Tabelleneinträge während des
Verbindungsaufbau analysiert werden:
1) Eine Verbindungsanforderung (SYN-Paket) wandert in die
OUTPUT-Kette und wird dort akzeptiert.
tcp 6 119 SYN_SENT src=140.208.5.62 dst=207.46.230.218 sport=1 dport=80 [UNREPLIED] src=207.46.230.218 dst=140.208.5.62 sport=80
dport=1 use=1
|
Der Verbindungsstatus ist SYN_SENT und die Verbindung wurde
als UNREPLIED markiert.
2) Nach Empfang eines SYN+ACK Paketes wird der Verbindungsstatus
auf SYN_RECV gesetzt und die UNREPLIED Markierung verschwindet.
tcp 6 57 SYN_RECV src=140.208.5.62 dst=207.46.230.218 sport=1 dport=80 src=207.46.230.218 dst=140.208.5.62 sport=80 dport=1
use=1
|
3) Nun sollte eine Bestätigung, also ein ACK-Paket, folgen.
Dieses müsste dieselbe Sequenznummer wie das ACK Paket
des Servers aufweisen. Bei
Übereinstimmung erhält die Verbindung eine ESTABLISHED Markierung
und der Tabelleneintrag wird als ASSURED markiert.
(Hinweis: Bei Erreichen des ip_conntrack_max Limits werden
die mit UNREPLIED markierten Verbindungen zuerst gelöscht und
die mit ASSURED Markierten zuletzt.)
tcp 6 431985 ESTABLISHED src=140.208.5.62 dst=207.46.230.218 sport=1 dport=80 src=207.46.230.218 dst=140.208.5.62 sport=80
dport=1 [ASSURED] use=1
|
|
Bei der Verbindungsverfolgung gibt es lediglich den Status
NEW, ESTABLISHED, RELATED und INVALID
( Explizite Erweiterungen).
Achtung! Dieser Status ist
nicht äquivalent
zum TCP Status. Wenn beispielsweise ein ACK-Paket zu einem
nicht-existierenden Rechner hinter der Firewall versendet wird,
wird ein Verbindungseintrag in der Statustabelle erzeugt, weil
es als erstes Paket einer noch nicht bestehenden Verbindung
(und damit als NEW) gilt. Unter dem Gesichtspunkt, dass solche
ACK Pakete die Statustabelle überfluten können, ist die
folgende Regel sehr sinnvoll:
root@linux #
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
|
Diese Regel stellt sicher, dass der Aufbau von TCP-Verbindungen
ausschlißlich durch SYN-Pakete initiiert werden kann. Sie hat
jedoch einen kleinen Nachteil:
Manchmal treten bei TCP-Verbindungen
äußerst lange Wartezeiten auf, und die korrespondierenden Einträge
verschwinden dann aus der Statustabelle. Wenn aber nun die
Verbindung mit ACK-Paketen (Daten)
von außen fortgesetzt wird, dann verhindert die Regel den
Transport nach innen. Anders ausgedrückt: Die Firewall dachte, die
Verbindung wurde abgebrochen und entfernte deshalb den Eintrag in
der Statustabelle. Das ankommende Datenpaket (ACK) kann keiner
Verbindung zugeordnet werden und gilt deshalb als "NEW". Die
obenstehende Regel blockiert aber TCP-Pakete ohne SYN-Flag, die
zu keiner Verbindung zugeordnet werden können.
[besser Workaround tcp-window-tracking]
|
Die Zeitbeschränkungen ähneln sehr denen von UDP. Wenn eine
Verbindung ein Paket erhält, wird der Timeout zurückgesetzt.
Die Zeitbeschränkungen sind fest eincompiliert und in der
Datei ersichtlich:
/usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
Wichtig sind die folgenden Zeilen:
ip_conntrack_proto_tcp.c
|
[static unsigned long tcp_timeouts]
= { 30 MINS, /* TCP_CONNTRACK_NONE, */
5 DAYS, /* TCP_CONNTRACK_ESTABLISHED, */
2 MINS, /* TCP_CONNTRACK_SYN_SENT, */
60 SECS, /* TCP_CONNTRACK_SYN_RECV, */
2 MINS, /* TCP_CONNTRACK_FIN_WAIT, */
2 MINS, /* TCP_CONNTRACK_TIME_WAIT, */
10 SECS, /* TCP_CONNTRACK_CLOSE, */
60 SECS, /* TCP_CONNTRACK_CLOSE_WAIT, */
30 SECS, /* TCP_CONNTRACK_LAST_ACK, */
2 MINS, /* TCP_CONNTRACK_LISTEN, */
};
|
|
Der Verbindungsabbau kann auf zwei unterschiedliche Arten erfolgen.
Der natürliche Weg (FIN+ACK) wurde kurz skizziert:
Verbindungspartner 1 Verbindungspartner 2
.........
.........
FIN+ACK --->
<--- ACK
|
Sollte die Statustabelle den Verbindungswert auf TIME_WAIT setzen,
dann wird der Eintrag nach zwei Minuten automatisch gelöscht.
Der andere Weg des Verbindungsabbaus ist, wenn ein
Verbindungspartner ein Reset-Paket sendet (RST-Flag gesetzt).
Reset Pakete werden nicht bestätigt (kein resultierendes
ACK-Paket). Der Verbindungsstatus in der Tabelle wird auf
CLOSE geändert und läuft nach 10 Sekunden ab. Das passiert häufig
bei (ausgelasteten) HTTP-Servern.
|
|
Es gibt nur vier ICMP-Typen, deren Pakete als NEW oder
ESTABLISHED markiert werden können.
-
Echo request (ping, 8) und echo reply (pong, 0).
-
Timestamp request (13) und reply (14).
-
Information request (15) und reply (16).
-
Address mask request (17) und reply (18).
Die Anforderung (request) wird in jedem fall mit NEW markiert und
die Antwort (reply) mit ESTABLISHED.
Pakete anderer ICMP-Typen können nur eine verwandtschaftliche
Beziehung zu einer Verbindung haben und werden deshalb mit
RELATED markiert.
Hier ein paar Beispiele:
root@linux #
iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED, RELATED -j ACCEPT
root@linux #
iptables -A INPUT -p icmp -m state --state ESTABLISHED, RELATED -j ACCEPT
|
-
Ein icmp echo request ist NEW somit in der OUTPUT Kette erlaubt.
-
Ein icmp echo reply als Antwort auf einen echo request ist ESTABLISHED und somit in der INPUT Kette erlaubt. Ein echo reply würde immer in der OUTPUT Kette herausgefiltert werden, da keine korrespondierende Echo-Anforderung (echo request) die INPUT-Kette passieren kann.
-
Ein icmp redirect gilt als RELATED und passiert somit sowohl INPUT- als auch OUTPUT-Kette, vorausgesetzt es existiert eine korrespondierende
TCP- oder UDP-Verbindung in der Statustabelle.
|
Man benötigt das ip_conntrack_ftp Kernelmodul.
Für den Hausgebrauch (einzelner PC am Internet) eignen sich
die folgenden zwei Regeln, um FTP-Verbindungen zu akzeptieren:
root@linux #
iptables -A INPUT -p tcp --sport 21 -m state --state ESTABLISHED -j ACCEPT
root@linux #
iptables -A OUTPUT -p tcp --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT
|
(Voraussetzung: icmp RELATED wird erlaubt, siehe
vorangehendes Unterkapitel)
Leider ist es damit noch nicht getan, eine FTP-Verbindung benötigt
einen eigenständigen Datenkanal, welcher durch zwei
unterschiedliche Arten aufgebaut werden kann. Die folgenden zwei
Unterkapitel beschreiben die Vorgehensweise.
|
Der FTP-Client sendet eine Port-Nummer über den FTP-Kanal
zum FTP-Server. Dieser verbindet sich von Port 20 zu diesem Port
und sendet über diese neue Verbindung (ftp-data) die Daten, z.B.
das Resultat eines ls oder eines get-Kommandos. Die
FTP-Datenverbindung (ftp-data) wird also vom Server aufgebaut,
und nicht wie die FTP-Verbindung vom Client.
Um aktives FTP zu erlauben (Der Zielport ist nicht bekannt),
müsste eine generelle Regel für alle einkommenden
Verbindungen von Port 20 (FTP-Server) auf hohe Port-Nummern
(>1023) des Clients anlegt werden. Dies stellt jedoch
eine sehr unsichere Lösung dar.
Eine besseren Ansatz besteht in der Verbindungsverfolgung.
Dazu muß das Modul ip_conntrack_ftp eingebunden werden.
Es durchsucht das PORT Kommando nach der
Port-Nummer, mit der sich der Server verbinden wird. Damit kann
eine Beziehung zwischen ftp und ftp-data hergestellt
werden (RELATED). Die folgenden Regeln sind also vollkommen
ausreichend:
root@linux #
iptables -A INPUT -p tcp --sport 20 -m state --state ESTABLISHED,RELATED -j ACCEPT
root@linux #
iptables -A OUTPUT -p tcp --dport 20 -m state --state ESTABLISHED -j ACCEPT
|
|
Im Gegensatz zu aktivem ftp wird die gewünschte PORT-Nummer
der Datenverbindung nicht vom Client sondern vom Server
festgelegt (via PORT-Kommando). Der Client verbindet sich dann
zu diesem Port auf dem Server und der Datenverkehr kann beginnen.
Obwohl diese Vorgehensweise als sicherer gilt, sollte man
bedenken, dass fast gar nichts mehr über die Portnummern
der Verbindung bekannt ist.
Analog zu aktiven FTP kann man die Filterregeln aufstellen,
allerdings wird anstelle von NEW für die OUTPUT Kette der
Parameter RELATED angewendet:
root@linux #
iptables -A INPUT -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED -j ACCEPT
root@linux #
iptables -A OUTPUT -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
|
Zur erweiterten Verbindungsverfolgung (andere Protokolle) eignen
sich auch folgende Patches, die unter
http://www.netfilter.org/documentation/HOWTO/de/netfilter-extensions-HOWTO.html
dokumentiert sind und von dort auch heruntergeladen werden
können.
-
amanda-conntrack-nat
-
eggdrop-conntrack
-
h323-conntrack-nat
-
ip_conntrack-timeouts
-
mms-conntrack-nat
-
pptp-conntrack-nat
-
quake3-conntrack
-
rpc
-
rsh
-
talk-conntrack-nat
-
tcp-window-tracking
-
tftp-conntrack-nat
|
|
|