1. Vorwort
Es gibt zahlreiche sogenannte Firewall-Skripte und Frontends für Linux. Doch
ihrer praktischen Verwendung stehen oft mehrere Gründe im Weg:
- Sie passen nicht auf die eigenen Anforderungen
- Ihre Konfiguration oder ihre Wirkungsweise ist undurchschaubar
- Sie erfordern eine bestimmte Kernel-Version (2.0, 2.2 oder 2.4)
Besonders der zweite Punkt ist entscheidend. Wenn ich einen Firewall einrichte,
will ich 100% sicher sein, daß er so funktioniert, wie ich mir das vorstelle.
Wie aber will ich das bei einem vorgefertigten Programm bewerkstelligen?
Trial and Error wäre hier angesagt. Doch bevor ich mehrere Programme daraufhin
untersucht habe, ob sie korrekt funktionieren, habe ich schneller etwas Eigenes
geschrieben.
Daher stelle ich hier eine Art Baukasten für einen Paketfilter vor, der
folgende Bedingungen erfüllt:
- Benötigt nur die Shell (und die üblichen Tools)
- Kann in den Init-Skripten gestartet und gestoppt werden
- Bietet maximale Sicherheit
- Funktioniert mit Kernel-Version 2.4 (andere Versionen später)
- Seine Wirkungsweise ist (hoffentlich) verständlich
Ich gehe nicht davon aus, daß das hier vorgestellte Skript der Weisheit letzter
Schluß ist. Vielmehr werden sich vermutlich noch einige Änderungen und
Erkenntnisse ergeben, die in eine neuere Version dieses Artikels einfließen
werden. Die Hauptsache ist zunächst einmal, daß das Skript tut, was ich mir
als Ziel gesetzt habe.
Dieser Artikel ist kein Tutorial zur Wirkungsweise von Netfilter oder dem
iptables-Programm. Entsprechende Dokumentation, teilweise auch in Deutsch,
gibt es bereits (s. Quellen). Es wäre unsinnig, dies zu
wiederholen. Stattdessen liefert dieser Artikel ein Beispiel aus der Praxis
und hilft damit hoffentlich, die Lücke zwischen Dokumentation und realer
Anwendung kleiner zu machen.
2. Einleitung
Allzu oft wird Firewall mit Paketfilter gleichgesetzt. Das ist falsch.
Paketfilter sind nur ein Teil einer Firewall-Konfiguration. Ich will hier aber
trotzdem nur auf Paketfilter eingehen, und zwar auf die Paketfilter, die
Bestandteil des Linux-Kernels sind. Es gibt auch eine oder mehrere
Implementierungen außerhalb des Kernels, doch habe ich noch keine näheren
Informationen darüber.
Definieren wir schnell einmal ein paar Begriffe. Der Firewall-Rechner ist
der Rechner, der die Paketfilter implementiert. Es kann ein einzelner Rechner
mit Internet-Zugang (Modem, ISDN, DSL oder gar LAN) sein, es kann aber auch ein
Router sein, an den ein oder mehrere (bei mir letzteres) lokale Netze
angeschlossen sind. Das "Draußen" ist demzufolge das Internet und wird über
das Netzwerk-Interface erreicht, das dorthin führt. Bei Modem-Zugang also
ppp0, bei ISDN ippp0, und bei anderen Zugängen eth0, eth1 oder was auch immer.
Ein Paketfilter ist zwar wichtig, garantiert für sich allein aber noch keine
absolute Sicherheit. Würde man alle Pakete nach draußen sperren, hätte man
absolute Sicherheit erreicht, doch aus pragmatischen Gründen wird man das eine
oder andere Paket durch den Filter durchlassen wollen. Dies könnte ein Angreifer
jedoch ausnutzen, um mit gefälschten Paketen eventuell Zugang zu dem Rechner
zu erlangen. Je mehr Löcher man im Paketfilter lassen muß, desto größer die
Gefahr. Filter-Kriterien sind die IP-Adressen von Sender und Empfänger, die
Portnummer von Sender und Empfänger, das Protokoll, die Schnittstelle, die
hereinkommende und ausgehende Pakete nehmen, und die Richtung (ins Internet
oder vom Internet kommend). Wir müssen davon ausgehen, daß die Adresse und die
Portnummer eines hereinkommenden Paketes gefälscht sein können. Ferner müssen
wir alle Pakete ablehnen, die nicht für uns bestimmt sind. Das mag trivial
erscheinen, weil man glauben könnte, daß Pakete, die nicht für uns bestimmt
sind, erst gar nicht bei uns ankommen. Doch dem ist nicht so.
3. Der Paketfilter von Kernel 2.4
Kernel 2.4 kommt mit einer neuen Implementierung der Paketfilter, der vierten
oder fünften in der Geschichte von Linux. Der Name der Implementierung ist
Netfilter. Um diese zu benutzen, bedarf es auch eines
neuen Tools, iptables. Version 1.2 wird mindestens vorausgesetzt.
Zwar erlaubt Netfilter es mit Hilfe von Modulen, Rückwärtskompatibilität mit
Linux 2.2 und sogar 2.0 herzustellen, doch ich rate davon ab. Erstens
funktioniert dann Masquerading nicht, was für viele Nutzer wichtig ist, und
zweitens bringt es nichts, auf einem Auslaufmodell herumzureiten. Kernel 2.4 ist
hier und wird spätestens mit Version 2.4.3 weite Verbreitung erfahren.
Netfilter ist endlich die Implementierung der Paketfilter, die die optimale
Flexibilität und Erweiterbarkeit garantiert und dazu noch exzellente
Performance bietet. Man darf davon ausgehen, daß sie mehr Bestand haben wird
als ihre Vorgänger.
Es ist wichtig, die grundlegenden Konzepte von Netfilter zu kennen. Hier eine
Kurzfassung. Netfilter arbeitet mit "Ketten" von Regeln. Ketten sind wie
Unterprogramme. Regeln können in eine neue Kette verzweigen, und Ketten
können zur aufrufenden Kette zurückkehren. Dieses Konzept stammt von den
ipchains von Linux 2.2.
Es gibt drei vordefinierte Ketten: INPUT, OUTPUT und FORWARD. Ferner kann man
beliebige weitere Ketten definieren. Standardmäßig sind nach dem Booten alle
Ketten leer, und alle Pakete werden durchgelassen. Im Unterschied zu früher
durchläuft jedes eingehende oder ausgehende Netzwerk-Paket nur noch eine
einzige Kette:
- Pakete, die auf den lokalen Rechner erzeugt werden, durchlaufen die
OUTPUT-Kette.
- Pakete, die für den lokalen Rechner bestimmt sind, durchlaufen die
INPUT-Kette.
- Pakete, die lediglich geroutet werden, also den Rechner auf einem
Netzwerk-Interface erreichen und auf einem anderen wieder verlassen,
durchlaufen die FORWARD-Kette. Offensichtlich muß der Rechner als Router
konfiguriert sein, damit dies funktioniert.
Ein Paket durchläuft eine Kette, indem alle Regeln in der Kette der Reihe
nach abgearbeitet werden, bis entweder eine Regel entscheidet, was mit dem
Paket passiert, oder alle Regeln durchlaufen sind, worauf die Default Policy
angewandt wird. Das Schicksal des Pakets kann im Wesentlichen nur zwei
mögliche Verläufe nehmen: Es wird ignoriert (DROP) oder normal verarbeitet
(ACCEPT). Der Weg bis zu dieser Entscheidung kann natürlich beliebig
komplex werden, je nach Umfang der Anforderungen. Darüber hinaus kann auf
diesem Weg noch einiges mit dem Paket passieren. Wir beschränken uns hier
aber auf das Logging.
4. Die Konstruktion
4.1. Grundsätzliches
Das von mir entwickelte Skript behandelt kein Masquerading (auch Network
Address Translation, NAT, genannt). Das kann man in einem separaten Skript
behandeln, das nicht Thema dieses Artikels sein soll.
Das Skript sollte aufgerufen werden, bevor die entsprechenden
Netzwerk-Schnittstellen definiert sind. Dadurch entsteht kein Loch im Filter,
auch wenn dieses nur Sekundenbruchteile lang sein sollte.
Sicherheit läßt sich nur erreichen, wenn der Paketfilter möglichst restriktiv
ist. Das heißt, es muß alles ausgefiltert werden, was nicht explizit erlaubt
ist. Die umgekehrte Vorgehensweise, nämlich alles zu erlauben, was nicht
explizit verboten ist, bietet keinerlei Schutz vor unvorhergesehenen Attacken.
Alle Verstöße gegen die Filterregeln werden protokolliert (sie erscheinen
im Kernel-Log). Das ist neben dem Entdecken von Angriffen auch für das
Debuggen nützlich.
4.2. Der Rahmen
Das Skript ist wegen der Übersichtlichkeit und späteren Erweiterbarkeit
auf zwei Dateien verteilt. Das
"Hauptprogramm" des Skripts ist für das
Starten und Stoppen der IP-Filter zuständig:
#!/bin/sh
. "`dirname $0`/ipfilter-util"
case "$1" in
start)
echo -n "Starting IP filtering... "
Start
echo "done."
;;
stop)
echo -n "Stopping IP filtering... "
Stop
echo "done."
;;
close)
echo -n "Closing IP interfaces... "
Close
echo "done."
;;
*)
echo "Usage: $0 start|stop|close"
exit 1
esac
exit 0
Ich habe den Rahmen so einfach gehalten, daß er mit jeder Distribution
funktioniert. Jede Distribution kommt jedoch mit einem Beispiel eines
Init-Skripts, das man stattdessen auch verwenden könnte. Dazu muß man den
obigen Code in das Beispiel-Skript einfügen. Rudimentäre Kenntnisse in der
Shell-Programmierung sollte man dazu aber schon haben.
4.3. ipfilter-util
4.3.1 Stop und Close
Wie man am obigen Skript sieht, muß das Skript
ipfilter-util nur drei
Funktionen zur Verfügung stellen: Start, Stop und Close. Es stellt sich
heraus, daß die Funktionen Stop und Close sehr simpel sind:
Stop()
{
Flush ACCEPT
}
Close()
{
Flush DROP
}
Wir haben also eine weitere Funktion zu definieren:
Flush()
{
${IPTABLES} -P INPUT $1
${IPTABLES} -P OUTPUT $1
${IPTABLES} -P FORWARD $1
${IPTABLES} -F
${IPTABLES} -F INPUT
${IPTABLES} -F OUTPUT
${IPTABLES} -F FORWARD
${IPTABLES} -X destroy > /dev/null 2>&1
${IPTABLES} -X in_private > /dev/null 2>&1
${IPTABLES} -X out_private > /dev/null 2>&1
}
Flush benötigt ein Argument, das entweder ACCEPT (Aufruf von Stop) oder DROP
(Aufruf von Close) ist. Dies bestimmt die vorgegebene Policy des Paketfilters:
Ist sie ACCEPT, werden alle Pakete durchgelassen. Ist sie DROP, werden alle
Pakete vernichtet. Letzteres hat weitgehend dieselbe Wirkung wie das
Herausreißen des Netzwerkkabels und ist nur für Notfälle gedacht.
Ferner entfernt Flush alle Regeln aus den vordefinierten Ketten (Option -F)
und löscht die selbstdefinierten Ketten (Option -X). Wie diese definiert
werden, werden wir später noch sehen. Die Ausgabe der letzten Kommandos geht
nach /dev/null, damit wir keine Fehlermeldungen sehen, falls die Ketten nicht
existieren sollten.
Zu erwähnen ist noch die Variable IPTABLES. Diese enthält den Pfad des
iptables-Programms, in meinem System /sbin/iptables. Wo wir gerade dabei sind,
ist jetzt eigentlich eine gute Gelegenheit, die drei Variablen zu beschreiben,
die am Anfang des Skripts definiert sind. Diese müssen Sie an Ihre eigene
Umgebung anpassen.
INTERFACE="ippp0"
LOCAL="192.168.0.0/24"
IPTABLES="/sbin/iptables"
INTERFACE ist der Name des Netzwerk-Interfaces, das zum Internet führt. Für die
Leute, die eine dynamische IP-Adresse haben, und das ist ja die Mehrheit, ist
es nicht sinnvoll, die IP in den Regeln zu verwenden. Stattdessen nimmt man
den Namen des Interfaces. LOCAL ist der Adreßbereich des lokalen Netzes, das
meist über das Interface eth0 erreicht wird. IPTABLES schließlich ist der
Pfad des iptables-Programms, wie schon gesagt.
4.3.2 Start
Wir kommen nun zur Funktion Start, die unsere Filter definiert. Im Wesentlichen
besteht Start aus einer Reihe von weiteren Funktionsaufrufen. Diese Funktionen
werde ich im Folgenden vorstellen. Die ersten Funktionen initialisieren den
Filter und definieren einige Grundregeln. Der Mittelteil definiert, welche
Pakete durch den Filter passieren dürfen, und sollte von Ihnen an Ihre
Bedürfnisse angepaßt werden. Er besteht aus den Funktionen InOutTCP, OutTCP,
OutFTP, OutEqualUDP und InOutUDP. Danach folgen Regeln für ICMP (ich lasse
derzeit alles durch). Zum Schluß folgen Regeln, die alle Pakete, die es bis
hierher geschafft haben, vernichten. Im Grunde benötigt man diese Regeln
nicht, aber sie sorgen dafür, daß weggeworfene Pakete im Syslog erscheinen.
Ohne spezielle Regel würden sie einfach verschwinden. Das wäre weniger gut,
weil man ja wissen will, wenn jemand einen Crack-Versuch unternommen hat.
Start()
{
Close
CreateBucket
EgressFilter
AcceptLocal
InOutTCP 22 # auth, Telnet and ssh
OutTCP http 81 https 8080
OutTCP ftp smtp finger pop3 nntp
OutTCP 871 # sup
OutTCP realplayer
OutTCP cvspserver
OutTCP 6667 # IRC
OutFTP
OutEqualUDP ntp
InOutUDP domain
# Firewall ICMP. ICMP is useful so allow any by default.
${IPTABLES} -A INPUT -j ACCEPT -p icmp
${IPTABLES} -A OUTPUT -j ACCEPT -p icmp
${IPTABLES} -A FORWARD -j ACCEPT -p icmp
${IPTABLES} -A INPUT -j destroy
${IPTABLES} -A OUTPUT -j destroy
}
Die erste Funktion, Close, kennen wir bereits. Sie dient hier zum Rücksetzen
der Ketten auf einen definierten Zustand. Außerdem wird die Default Policy
gleich mal richtig gesetzt, nämlich auf DROP.
4.3.3 CreateBucket
CreateBucket()
{
${IPTABLES} -N destroy
# These rules are only there for logging.
${IPTABLES} -A destroy -j LOG --log-level notice -m limit
${IPTABLES} -A destroy -j DROP
}
CreateBucket ist eine simple Funktion, die eine neue Kette destroy
erzeugt und dieser zwei Regeln hinzufügt. Jedes Paket, das in
dieser Kette landet, wird vernichtet, jedoch vorher im Syslog protokolliert.
Das ist auch der einzige Sinn dieser Kette, nämlich sicherzustellen, daß kein
unerlaubtes Paket unbemerkt bleibt. CreateBucket erzeugt also gewissermaßen
unseren (Abfall-)Eimer.
4.3.4 EgressFilter
EgressFilter()
{
# Dont't let private addresses in.
${IPTABLES} -N in_private
${IPTABLES} -F in_private
${IPTABLES} -A in_private -j destroy -s 127.0.0.0/8
${IPTABLES} -A in_private -j destroy -s 10.0.0.0/8
${IPTABLES} -A in_private -j destroy -s 172.16.0.0/12
${IPTABLES} -A in_private -j destroy -s 192.168.0.0/16
${IPTABLES} -A in_private -j destroy -s 224.0.0.0/4
${IPTABLES} -A in_private -j destroy -s 240.0.0.0/4
# Dont't let private addresses escape.
${IPTABLES} -N out_private
${IPTABLES} -F out_private
${IPTABLES} -A out_private -j destroy -d 127.0.0.0/8
${IPTABLES} -A out_private -j destroy -d 10.0.0.0/8
${IPTABLES} -A out_private -j destroy -d 172.16.0.0/12
${IPTABLES} -A out_private -j destroy -d 192.168.0.0/16
${IPTABLES} -A out_private -j destroy -d 224.0.0.0/4
${IPTABLES} -A out_private -j destroy -d 240.0.0.0/4
${IPTABLES} -A INPUT -j in_private -i ${INTERFACE}
${IPTABLES} -A FORWARD -j in_private -i ${INTERFACE}
${IPTABLES} -A OUTPUT -j out_private -o ${INTERFACE}
${IPTABLES} -A FORWARD -j out_private -o ${INTERFACE}
}
Bei Egressfilter handelt es sich genaugenommen um einen kombinierten
Egress- und Ingress-Filter. Ersterer verhindert, daß Pakete mit privaten
IP-Adressen (RFC 1537) nach draußen gelangen. Letzterer verhindert, daß Pakete
mit solchen Absenderadressen von draußen zu uns herein gelangen, denn da solche
Adressen im Internet nicht erlaubt sind, muß es sich um Fälschungen handeln.
Die Funktion definiert zwei neue Chains, in_private und out_private. Die vier
Regeln am Ende der Funktion sorgen dafür, daß alle Pakete eine der beiden
Chains durchlaufen. Das Sprungziel für Pakete, die ausgefiltert werden, ist
unsere Chain destroy, die wir in der Funktion CreateBucket definiert haben.
4.3.5 AcceptLocal
AcceptLocal()
{
${IPTABLES} -A INPUT -j ACCEPT -i \! ${INTERFACE}
${IPTABLES} -A OUTPUT -j ACCEPT -o \! ${INTERFACE}
${IPTABLES} -A FORWARD -j ACCEPT -i \! ${INTERFACE} \
-o \! ${INTERFACE}
${IPTABLES} -A FORWARD -j destroy -i ${INTERFACE} \
-o ${INTERFACE}
}
AcceptLocal sorgt dafür, daß der Datenverkehr über unseren Router problemlos
vonstatten geht, solange Quelle und Ziel im internen LAN liegen. Diese Funktion
ist nur nötig, wenn man mehr als eine interne Netzwerkschnittstelle hat. Man
kann sich natürlich darüber streiten, ob es sinnvoll ist, auf dem
Internet-Gateway diese Routerfunktion zu haben. Bei mir ist es im Moment eben
so, aber ich habe vor, das zu ändern.
Als zusätzlichen Bonus (letzte Regel) vernichtet die Funktion alle Pakete, die
aus dem Internet stammen und gleich wieder ins Internet geroutet werden wollen.
Solche Pakete sind höchst verdächtig, da sie unseren Rechner als Router
mißbrauchen.
4.3.6 InOutTCP und InOutUDP
Bis jetzt haben wir einen Rahmen geschaffen, der alle Pakete zwischen LAN und
Internet sperrt und alles andere weitgehend erlaubt. Nun müssen wir daran
gehen, einige Protokolle zwischen dem LAN und dem Internet freizugeben. Dies
tun wir getrennt für Verbindungen, die von innen nach außen und umgekehrt
gehen. Verbindungen von außen nach innen werden wir weitgehend gesperrt lassen,
denn dafür besteht auf einem Heim-Rechner kaum ein Anlaß. Es sei denn, man
betreibt einen Server, was bei den heutigen Internet-Zugängen mit dynamischen
IPs aus mehreren Gründen problematisch ist.
Es folgen zunächst zwei Funktionen, mit denen man Protokolle in beiden
Richtungen freigeben kann. Sie delegieren die ganze Arbeit an einfachere
Funktionen, die wir gleich vorstellen werden. Alle diese Funktionen erwarten
als Argumente beliebig viele Portnummern oder Protokollnamen. Wenn man Namen
verwendet, müssen diese in der Datei /etc/services einer Portnummer zugewiesen
sein. Beispiele sind oben in der Funktion Start zu finden.
InOutTCP()
{
InTCP $*
OutTCP $*
}
InOutUDP()
{
InUDP $*
OutUDP $*
}
4.3.7 InTCP
InTCP()
{
for i in $*
do
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p tcp \
--dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p tcp \
--sport $i -m state --state ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -i ${INTERFACE} -p tcp \
--dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -o ${INTERFACE} -p tcp \
--sport $i -m state --state ESTABLISHED,RELATED
done
}
Diese Funktion erlaubt hereinkommende Verbindungen auf einem oder mehreren
Ports. Sie ist nicht zuständig für ausgehende Verbindungen. Zu einer ankommenden
Verbindung gehören Pakete in beide Richtungen, die jedoch unterschiedlich
behandelt werden. Daher benötigt man eine Regel für jede Richtung. Wenn man
wie hier auch noch Pakete hat, die entweder für den lokalen Rechner bestimmt
sind oder weitergeleitet werden, dann ergibt das vier Regeln insgesamt. Auf
einem normalen Heimrechner, der nicht mit weiteren Rechnern im LAN verbunden
ist, kann man die Regeln für die FORWARD-Chain weglassen. Umgekehrt könnte bzw.
sollte man auf einem reinen Router, der keinerlei lokale Dienste zur Verfügung
stellt, die ersten beiden Regeln weglassen.
Die Regeln machen Gebrauch von "Stateful Filtering", was bedeutet, daß sich das
System den Zustand von Verbindungen merkt. Damit können alle Pakete, die den
aktuellen Zustand der Verbindung verletzen oder gar nicht zu einer bestehenden
Verbindung gehören können, ausgefiltert werden. Das Einschalten des Stateful
Filtering geschieht mit der Option -m state. Mit der Option --state wird
ausgewählt, in welchen Verbindungszuständen ein Paket passieren darf. Da wir
hier ankommende Verbindungen freischalten wollen, erlauben wir den Zustand
NEW für ankommende Pakete aus dem Internet. Durch die Option -i ${INTERFACE}
wird sichergestellt, daß nur aus dem Internet kommende Pakete betroffen sind.
Zusätzlich geben wir für beide Richtungen die Zustände ESTABLISHED und RELATED
frei. ESTABLISHED steht für eine aufgebaute Verbindung, auf der ganz normal
Pakete ausgetauscht werden. RELATED steht für Pakete, die mit der Verbindung
in Zusammenhang stehen, besonders ICMP-Pakete.
4.3.8 OutTCP
OutTCP()
{
for i in $*
do
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p tcp \
--sport 1024: --dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p tcp \
--sport $i -m state --state ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -o ${INTERFACE} -p tcp \
-s ${LOCAL} --sport 1024: --dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -i ${INTERFACE} -p tcp \
--sport $i -d ${LOCAL} -m state --state ESTABLISHED,RELATED
done
}
Diese Funktion entspricht InTCP, nur werden hiermit Verbinundgen nach außen
freigegeben, also das, was man in 99% aller Fälle benötigt. Also sind die
Richtungen gegenüber InTCP gerade vertauscht. Als zusätzliche Restriktion
kommt noch hinzu, daß die Portnummer des Absenders über 1023 liegen muß. Dies
ist OK bei allen TCP-basierten Protokollen, die ich verwende, doch es kann
Ausnahmen geben. FTP ist solch eine Ausnahme, doch auch noch aus anderen
Gründen.
4.3.9 InUDP
InUDP()
{
for i in $*
do
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p udp \
--dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p udp \
--sport $i --dport 1024: -m state --state ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -i ${INTERFACE} -p udp \
--dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -o ${INTERFACE} -p udp \
--sport $i -m state --state ESTABLISHED,RELATED
done
}
Diese Funktion entspricht InTCP, nur ist sie für das UDP-Protokoll. Der einzige
Unterschied ist in der Tat der Protokollname, und das, obwohl UDP kein
verbindungsorientiertes Protokoll ist.
4.3.10 OutUDP
OutUDP()
{
for i in $*
do
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p udp \
--sport 1024: --dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p udp \
--sport $i -m state --state ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -o ${INTERFACE} -p udp \
-s ${LOCAL} --sport 1024: --dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A FORWARD -j ACCEPT -i ${INTERFACE} -p udp \
--sport $i -d ${LOCAL} -m state --state ESTABLISHED,RELATED
done
}
Diese Funktion entspricht OutTCP, nur ist sie für das UDP-Protokoll. Der einzige
Unterschied ist in der Tat der Protokollname, und das, obwohl UDP kein
verbindungsorientiertes Protokoll ist.
4.3.11 OutEqualUDP
OutEqualUDP()
{
for i in $*
do
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p udp \
--sport $i --dport $i -m state --state NEW,ESTABLISHED,RELATED
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p udp \
--sport $i --dport $i -m state --state ESTABLISHED,RELATED
done
}
Diese Funktion läßt ausgehende Verbindungen für UDP-basierte Protokolle zu, die
nicht unserer ursprünglichen Annahme folgen, daß die Portnummer des Senders
über 1023 liegt. Das einzige mir bekannte Beispiel ist NTP, das Network Time
Protocol. Bei dessen Implementierung sind die Portnummern von Sender und
Empfänger identisch. Hier habe ich auf die FORWARD-Regeln verzichtet, d.h.
weitergeleitet wird nicht. Die entsprechenden Regeln bei Bedarf hinzuzufügen,
sei dem Leser als Übung überlassen.
4.3.12 OutFTP
OutFTP()
{
InOutTCP ftp-data
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p tcp \
--sport ftp-data -m state --state NEW,ESTABLISHED,RELATED
}
Für aktives FTP ist die Funktion OutFTP gedacht. Aktives FTP ist bekanntlich
ein Spezialfall, der bei einem Firewall einige Kopfschmerzen bereitet, weshalb
oft passives FTP oder ein FTP-Proxy verwendet wird. Passives FTP habe ich hier
nicht vorgesehen. Das wird im übernächsten Abschnitt beschrieben.
Die Funktion erlaubt zunächst Verbindungen in beide Richtungen auf dem ftp-data
Port, welcher als Port Nr. 20 definiert ist. Durch die nachfolgende Regeln
werden Pakete auf einem beliebigen Port akzeptiert, wenn die Portnummer des
Senders 20 ist. Ohne Stateful Filtering würde dies sozusagen ein riesiges
Scheunentor öffnen.
Für passives FTP wandelt man die Funktion OutFTP ab, wie nachfolgend
angegeben. Passives FTP macht keinen Gebrauch von Port 20, sondern macht die
Verbindung zwischen zwei beliebigen Ports oberhalb von 1023. Ohne Stateful
Filtering wäre das eine sicherheitstechnische Katastrophe, da man alle Ports
freigeben müßte. Mit den hier angegebenen Regeln sollte es aber sicherer sein
als aktives FTP. Wir lassen hier explizit keine neuen Verbindungen zu, sondern
nur solche, die mit einer existierenden Verbindung in Zusammenhang stehen. Die
existierende Verbindung ist hier natürlich die ursprüngliche Verbindung zum
FTP-Server auf Port 21.
OutFTP()
{
${IPTABLES} -A OUTPUT -j ACCEPT -o ${INTERFACE} -p tcp \
--sport 1024: --dport 1024: -m state --state ESTABLISHED,RELATED
${IPTABLES} -A INPUT -j ACCEPT -i ${INTERFACE} -p tcp \
--sport 1024: --dport 1024: -m state --state ESTABLISHED,RELATED
}
5. Installation
Zur Installation kopiert man die beiden Skripte ipfilter und ipfilter-util ins
Verzeichnis der Init-Skripte. Dieses ist distributions-abhängig, oft jedoch
/etc/rc.d. ipfilter muß ausführbar sein. Beide Skripte sollten root gehören.
Danach sollte man die Skripte erst einmal durch Aufruf von
/etc/rc.d/ipfilter start und /etc/rc.d/ipfilter stop testen. Insbesondere
sollte man testen, ob alle benötigten Applikationen noch funktionieren. Dann
sollte man testen, ob unerwünschte Pakete tatsächlich ausgefiltert werden.
Dies funktioniert offensichtlich nur von einem Rechner im Internet, doch gibt
es Webseiten, die das als Dienst anbieten.
Netfilter ist sehr modular aufgebaut. Eine ganze Reihe von Kernel-Modulen
müssen geladen werden, damit alles funktioniert. Glücklicherweise sorgt das
Programm iptables dafür, daß alle Module automatisch geladen werden. Nur das
Modul "ip_tables" muß eventuell von Hand (d.h. in den Start-Skripten vor
Aufruf unseres Skripts) geladen werden.
Funktioniert alles zufriedenstellend, kann man dafür sorgen, daß das Skript
automatisch beim Systemstart ausgeführt wird. Wie das genau geschieht, ist
wiederum distributions-abhängig.
6. Einschränkungen
Das Skript unterstützt noch nicht mehr als ein lokales Netz. Entweder erweitert
man in diesem Fall die Funktionen In, Out usw., was ja simpel genug ist, oder
man faßt die IP-Adressen der Netze unter einer Adresse/Maske zusammen.
Mein Filter ist sehr restriktiv. Jedes neue Protokoll muß extra freigegeben
werden. Dies hat den Vorteil der absoluten Kontrolle, kann aber auch unpraktisch
sein. Im Prinzip könnte man alle Protokolle in der Richtung von LAN ins
Internet freigeben. Solange die Gegenrichtung gesperrt bleibt, kann dennoch
kein Eindringling ins LAN. Wer die Notwendigkeit dafür sieht, kann das machen.
Doch mein Ziel war maximale Sicherheit, nicht maximaler Komfort.
7. Quellen
Linux 2.4 Paket Filtering HOWTO
Deutsche Übersetzung des Linux 2.4 Paket Filtering HOWTO
iptables(8) - Manual Page für iptables Version 1.2
|
Feedback ist wichtig für die Verbesserung des Service
|
Copyright (C) hjb
Datum: 2001-06-14 - Pro-Linux
(Info)
|
|