Gestaltung einer Firewall mit Linux
Keywords: Buch

> Nächste Seite: Die Firewall als Router Aufwärts: Crashkurs Firewallaufbau unter Linux Vorherige Seite: Filtern von ausgehenden Paketen Inhalt
> Unterabschnitte
> - Die drei Standard-Ketten
> - Das Handwerkszeug: ipchains
> - Format der Angaben
>   - IP-Adressen
>   - Ports
> - Praktischer Aufbau einer Firewall
>   - Grundeinstellungen
>   - Ein paar Techniken zur Abwehr von gefälschten Paketen
>   - Ausfiltern offensichtlich falscher Adressen
>   - ICMP-Nachrichten filtern
>   - Dienste auf unprivilegierten Ports
>     - Häufige TCP-Dienste auf unprivilegierten Ports
>     - UDP-Dienste auf unprivilegierten Ports
>   - Wichtige Dienste erlauben
>     - DNS
>     - Der identd-Service

> # Gestaltung einer Firewall mit Linux

> Nachdem nun alle wichtigen theoretischen Voraussetzungen erfüllt sind, ist es an der Zeit, die konkrete Umsetzung einer Firewall unter Linux anzusprechen. Linux hat, im Gegensatz zu den meisten anderen Betriebssystemen, die Funktionalität der Firewall bereits im Betriebssystemkern (Kernel) fest eingebaut. Das macht die Firewall sowohl stabiler, als auch wesentlich schneller, als wenn sie im sogenannten Userspace realisiert werden müsste.

> # Die drei Standard-Ketten

> Wir haben bereits gesehen, daß eine Firewall unter Linux aus vielen einzelnen Regeln besteht, die zu einer Kette (chain) zusammengefasst werden. Standardmäßig existieren drei Ketten, es können aber beliebig viele weitere Ketten erstellt werden - z.B. spezielle Regeln für einen Modemanschluß oder ähnliches. Die drei Standard-Ketten sind input, output und forward.

> Wenn ein Paket von außen an einer beliebigen Netzwerk-Schnittstelle ankommt, dann wird sein Header mit jeder Regel der input-Chain verglichen, solange bis entweder eine Regel zutrifft, oder das Ende der Regelkette erreicht ist. Jede Regel, die zutreffen sollte, kann als Folge ein `ACCEPT, REJECT` oder `DENY` definiert haben. Erst, wenn das Ende der Kette erreicht ist, wird die voreingestellte Policy der Chain aktiv, die wiederum einen der drei Werte `ACCEPT, REJECT` oder `DENY` darstellt. Die erste gefundene passende Regel kommt zum Zug.

> Genauso verhält es sich mit den Paketen, die zu einer beliebigen Netzwerkschnittstelle geschickt werden, um den Computer zu verlassen. In diesem Fall tritt - statt der input-Chain - die output-Chain in Kraft.

> Wenn ein Paket nicht für den Rechner bestimmt ist, auf dem die Firewall läuft, sondern an andere Rechner weitergeleitet (geroutet) werden soll, dann tritt die dritte Regelkette zu, die forward-Chain. Diese Regelkette kann neben den normalen Regeln noch eine weitere Fähigkeit anbieten, das sogenannte Masquerading. Hierbei handelt es sich um die Fähigkeit des Kernels, zu routende Pakete zu maskieren, so daß von außen der Eindruck erweckt wird, sie kämen ausschließlich von dem Firewall-Rechner. Das hat zwei Vorteile:

> - Ein internes Netzwerk kann mit reservierten IP-Adressen arbeiten, trotzdem kann jeder Rechner des internen Netzes über die Firewall nach außen ins Internet gelangen.
> - Von außen ist die Struktur des internen Netzes nicht sichtbar, weil es nach außen hin nur die IP-Adresse der Firewall gibt. Ein Angriff auf einen Rechner innerhalb des Netzes ist von daher schon unmöglich, daß jeder Rechner innen eine Adresse hat, die im Internet gar nicht geroutet werden darf.
> Zusammengefasst könnte man eine Firewall mit all ihren Regelketten also folgendermaßen darstellen:

> Bild 6..1: Architektur der IP-Chains im Kernel

> Die Regeln der einzelnen Ketten werden in Tabellen im Kernel gespeichert, für jede einzelne Kette eine eigene Tabelle. Diese Regeln stehen in der Reihenfolge im Kernel, in der sie eingegeben wurden. In manchen Fällen ist es notwendig, die Reihenfolge zu beachten, weil eine falsche Reihenfolge den Sinn der Regeln umwerfen könnte. Zur Erinnerung, die erste passende Regel zählt.

> Es ist auch möglich, Regeln am Anfang einer Kette neu einzufügen, in der Praxis ist jedoch der Aufbau von Firewall-Regeln nicht, was man bei jedem Start von Hand übernimmt. Wir werden im Gegenteil natürlich Scripte erstellen, die die Regeln definieren und die bei jedem Start ausgeführt werden. Jede notwendige Änderung von Regeln sollte auch immer in diesen Scripten stattfinden, denn
> Von Hand eingegebene Firewall-Regeln sind nach dem nächsten Neustart verschwunden!

> # Das Handwerkszeug: ipchains

> Das Programm, mit dem wir Firewall-Regeln erstellen, bearbeiten oder löschen heißt `ipchains` und ist sozusagen ein Werkzeug zur Manipulation der Regeltabellen im Kernel. Dieses Programm sollte im Verzeichnis `/sbin` liegen und nur vom Systemverwalter aufgerufen werden. Als typisches Unix-Programm wird es ausschließlich über die Kommandozeile gesteuert, also über einen Berg von verschiedenen Kommandozeilenparametern. Das ist von daher wichtig, daß wir ja Scripte schreiben wollen, die die einzelnen Regeln erstellen. Nur mit einem Kommandozeilenorientierten Programm kann das gelingen.

> `ipchains` ist ein Programm, das für jede Firewall-Regel einmal aufgerufen werden muß. Die wichtigen Parameter werden jetzt kurz dargestellt. Eine vollständige Beschreibung der Parameter finden Sie auf der entsprechenden Man-Page `ipchains(8)` und im `IPCHAINS-HOWTO`.

>       ipchains
>         -A | -I [Chain]
>         [-i Interface]
>         [-p Protokoll]
>         [[!] -y]
>         [-s Adresse [Port[:Port]]]
>         [-d Adresse [Port[:Port]]]
>         -j Policy
>         [-l]

> `-A [`Chain`]` | Hängt eine Regel ans Ende der angegebenen Kette (Chain) an. Wenn keine Chain angegeben wird, so gilt die Regel für alle Chains.
> `-I [`Chain`]` | Fügt eine Regel vor den Anfang der angegebenen Kette (Chain) ein. Wenn keine Chain angegeben wird, so gilt die Regel für alle Chains.
> `-i` Interface | Netzwerkinterface, für das die Regel gelten soll. Wird kein Interface angegeben, so gilt die Regel für alle Interfaces.
> `-p` Protokoll | Protokollnamen oder -nummer, für das die Regel gilt. Gültige Werte sind hier `tcp, udp, icmp` und `all`. Daneben sind alle Protokollnamen und -nummern aus `/etc/protocols` erlaubt.
> `-y` | Das SYN-Flag einer TCP-Nachricht muß gesetzt, das ACK-Flag darf nicht gesetzt sein. Das Paket ist also das erste eines Verbindungsaufbaues und kommt vom Client.
> `! -y` | Das ACK-Flag einer TCP-Nachricht muß gesetzt sein. Das heißt, das Paket ist entweder der zweite Teil des Verbindungsaufbaues oder, es ist ein Teil einer bestehenden Verbindung. Ist weder `-y` noch `! -y` gesetzt, werden die TCP-Flags nicht überprüft.
> `-s` Adresse`[`Port`]` | Absender-Adresse des Paketes. Wenn keine Absenderadresse angegeben wird, so sind alle Adressen angesprochen. Wenn zusätzlich ein Port oder ein Portbereich (Port:Port) angegeben ist, so gilt die Regel nur für diese Ports. Sind keine Ports angegeben, so sind alle Ports gemeint.
> `-d` Adresse`[`Port`]` | Empfänger-Adresse des Paketes. Ports wie bei der Absender-Adresse.
> `-j` Policy | Policy dieser Regel. Gültige Policies sind `ACCEPT, REJECT` und `DENY`. In der `forward`-Chain ist auch die Policy `MASQ` für Masquerading zulässig.
> `-l` | Logbucheintrag. Jedesmal, wenn diese Regel zutrifft, wird eine `syslog`-Meldung der Herkunft `kern` und der Priorität `info` erzeugt.

> # Format der Angaben

> ## IP-Adressen

> Eine Firewall-Regel kann sowohl eine Sender-, als auch eine Empfängeradresse beinhalten. In diesem Fall gilt die Regel dann eben nur für diese Adresse. Statt der Adressen können auch die Rechnernamen angegeben werden, das hat aber ein paar gewichtige Nachteile. Zur Namensauflösung muß ein funktionierendes DNS-System vorhanden sein. Diese Voraussetzung könnte - zumindestens am Anfang einer Regelkette - noch nicht gegeben sein, wenn DNS selbst einer Beschränkung unterliegt. Es ist daher grundsätzlich ratsam, statt Namen eben IP-Adressen zu verwenden. Zum Anderen können DNS-Namen leichter gefälscht werden, als IP-Adressen.

> IP-Adressen können dazu noch mit einer Maske versehen werden, die die signifikannten Bits der Adresse angibt. Diese Maske wird als Bitmaske realisiert und einfach mit einem Slash (`/`) hinten an die Adresse angehängt. Die Bedeutung ist einfach, die Adressangabe

>       192.168.100.123/24

> bedeutet, daß die ersten 24 Bit der Adressangabe mit der gefundenen Adresse übereinstimmen müssen, damit die Regel greift. In diesem Beispiel sind das also alle Adressen, die vorne `192.168.100` stehen haben. Eine Maske /32 bedeutet also, daß die Adresse exakt übereinstimmen muß, eine Maske /0 bedeutet, daß kein Bit übereinstimmen muß, also alle Adressen gemeint sind. Dafür ist auch die Abkürzung `any/0` zulässig.

> ## Ports

> Ports können sowohl als Portnummer, als auch als symbolische Namen angegeben werden, wie sie in `/etc/services` eingetragen sind. Allerdings hat der Eintrag des symbolischen Namens den Nachteil, daß diese Namen nicht immer gleich sind, sie sind nicht fest vorgegeben. Bei einem Distributionswechsel könnte es vorkommen, daß eine Firewallregel nicht mehr funktioniert, wenn eben die Namen der Ports sich verändert haben. Andererseits ist natürlich ein Name aussagekräftiger, als eine Nummer.

> #
> Praktischer Aufbau einer Firewall

> Eine Firewall wird grundsätzlich aufgebaut, indem jede einzelne Regel durch einen Aufruf von `ipchains` angegeben wird. Das ist eine Aufgabe, die natürlich nicht jedesmal von Hand ausgeführt wird, sondern immer in Form eines Scripts erledigt werden sollte. Ein solches Script kann nach jedem Neustart neu geladen werden und es wird nichts dabei vergessen. Es ist in fast allen Fällen unsinnig, einzelne Regeln von Hand im Nachhinein einzufügen. Ein solches Script muß selbst erstellt werden, auch wenn Distributoren wie etwa S.u.S.E schon vorgefertigte Scripts enthalten. Wir werden uns hier ein solches exemplarisches Script erstellen.

> ## Grundeinstellungen

> Zum besseren Verständnis der einzelnen Regeln ist es üblich, einzelne, immer wiederkehrende Begriffe, Adressen o.ä. als Konstanten vorzudefinieren. Fangen wir mit einem einfachen Beispiel an, in dem die Firewall nur sich selbst schützt (Das vollständige Script liegt im Anhang ab Seite nochmal zur Einsicht):

>       EXTERNAL_INTERFACE=eth0        # Das fremde Netz
>       LOOPBACK_INTERFACE=lo          # Local Loopback
>       IPADDR=192.168.100.1           # Eigene Adresse
>       ANYWHERE=any/0                 # Jede IP-Adresse
>       MY_ISP=123.45.67.89/16         # Der Bereich meines Providers
>       LOOPBACK=127.0.0.0/8           # Loopback-Adressbereich
>       CLASS_A=10.0.0.0/8             # Reservierter Bereich Klasse A
>       CLASS_B=172.16.0.0/12          # Reservierter Bereich Klasse B
>       CLASS_C=192.168.0.0/16         # Reservierter Bereich Klasse C
>       CLASS_D=224.0.0.0/4            # Komplette Klasse D
>       CLASS_E=240.0.0.0/5            # Komplette Klasse E
>       BROADCAST_SRC=0.0.0.0          # Broadcast Absender
>       BROADCAST_DEST=255.255.255.255 # Broadcast Empfänger
>       PRIVPORTS=0:1023               # Privilegierte Portnummern
>       UNPRIVPORTS=1024:65535         # Unprivilegierte Portnummern

> Der erste Schritt, zum Aufbau einer neuen Regelsammlung ist immer das Löschen eventuell schon bestehender Regeln. Das Programm `ipchains` kann seine Chains mit dem Parameter -F (Flush) löschen. Wenn keine Chain angegeben wird, dann löschen wir alle eingebauten Chains, also `input, output` und `forward`.

>       # Alle bestehenden Regeln löschen
>       ipchains -F

> Alle Ketten sind jetzt leer. Allerdings haben wir noch nicht die Grund-Policies gelöscht bzw. verändert. Sie bleiben auch nach dem Löschen vorhanden. Also stellen wir jetzt diese Policies ein, für jede Kette extra. Dazu stellt ipchains den Parameter -P (nicht verwechseln mit -p) zur Verfügung:

>       # Voreingestellte Policies setzen
>       ipchains -P input    DENY
>       ipchains -P output   REJECT
>       ipchains -P forward  REJECT

> Ab diesem Punkt ist - zumindestens für unseren Rechner - jeder Netzwerkverkehr gesperrt. Die Grundeinstellungen lehnen alles ab, einzelne Regeln haben wir noch nicht. Nun geben wir dem Loopback Interface alle Rechte. Von diesem Interface droht uns keinerlei Gefahr, aber einzelne lokale Dienste wie Drucker oder X11 können nicht ohne Loopback funktionieren:

>       # Loopback ohne Einschränkungen
>       ipchains -A input  -i $LOOPBACK_INTERFACE -j ACCEPT
>       ipchains -A output -i $LOOPBACK_INTERFACE -j ACCEPT

> ##
> Ein paar Techniken zur Abwehr von gefälschten Paketen

> Nun haben wir eine Grundeinstellung, mit der sich bereits arbeiten lässt. Allerdings ist bisher außer dem lokalen Loopback noch nichts erlaubt. Bevor wir jetzt weitere Regeln erstellen ist es sinnvoll, noch ein paar Sicherheitsmechanismen gegen SYN-Flooding und gefälschte IP-Pakete einzubauen. Der Linux-Kernel enthält bereits einige dieser Mechanismen, die wir nur noch aktivieren müssen. Dazu stehen ein paar sehr Linux-typische Wege zur Verfügung.

> Das Verzeichnis `/proc` enthält Dateien, die eigentlich keine Dateien sind, sondern Schnittstellen zum Kernel. Einige dieser Dateien ermöglichen es, bestimmte Fähigkeiten des Kernels ein- oder auszuschalten. Das geschieht, indem in diese Dateien eine 1 oder eine 0 hineingeschrieben wird. Für uns sind dabei insbesondere die folgenden Dateien interessant:

> - `/proc/sys/net/ipv4/tcp_syncookies`
>   schaltet den Schutzmechanismus gegen SYN-Flooding ein/aus.
> - `/proc/sys/net/ipv4/conf/*/rp_filter`
>   Das Verzeichnis `/proc/sys/net/ipv4/conf` enthält für jedes IP-Interface ein Unterverzeichnis, in dem jeweils eine Datei `rp_filter` liegt. Diese Dateien ermöglichen es, die sogenannte Source-Address-Verification einzuschalten.
> In all diese Dateien schreiben wir jetzt eine 1 hinein, um die jeweiligen Mechanismen zu aktivieren. Auch das geschieht innerhalb unseres Scripts:

>       # SYN_COOKIES aktivieren
>       echo 1 > /proc/sys/net/ipv4/tcp_syncookies

>       # SOURCE ADDRESS VERIFICATION aktivieren
>       for i in /proc/sys/net/ipv4/conf/*/rp_filter
>       do
>         echo 1 > $i
>       done

> ## Ausfiltern offensichtlich falscher Adressen

> Es gibt ein paar Adressen, die wir grundsätzlich ablehnen können, weil sie nie legal aus dem externen Netz (Internet) kommen können. Dazu zählt zuerst einmal die eigene IP-Adresse, danach alle Adressen aus reservierten Adressbereichen. Also verbieten wir zunächst jedes Paket, das hereinkommt und vorgibt von unserer eigenen Adresse zu stammen:

>       # Pakete ablehnen, die vorgeben von der eigenen Adresse zu stammen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $IPADDR -j DENY -l

> Die beiden verwendeten Begriffe `$EXTERNAL_INTERFACE` und `$IPADDR` hatten wir ja oben bei der Konstantendefinition festgelegt. Das angehängte `-l` bedeutet, daß, falls diese Regel zutrifft eine Meldung an den `syslogd` geht und der Vorfall protokolliert wird.

> Der nächste Schritt ist das Verbot aller ein- und ausgehender Pakete mit reservierten Adressen - sowohl Sender- als auch Empfängeradressen. Wir hatten ja bei der Definition der Konstanten extra diese Bereiche definiert, jetzt brauchen wir sie:

>       # Reservierte A-Klasse Adressen ablehnen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $CLASS_A -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -d $CLASS_A -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -s $CLASS_A -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -d $CLASS_A -j DENY

>       # Reservierte B-Klasse Adressen ablehnen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $CLASS_B -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -d $CLASS_B -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -s $CLASS_B -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -d $CLASS_B -j DENY

>     # Reservierte C-Klasse Adressen ablehnen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $CLASS_C -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -d $CLASS_C -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -s $CLASS_C -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -d $CLASS_C -j DENY

> Noch eine Adressform, die denkbar ungültig ist, ist die Verwendung einer Loopback-Adresse als Absender-Adresse vom externen Interface. Weder ein- noch ausgehende Pakete dürfen das vorweisen:

>       # Pakete mit Loopback als Absender verwerfen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $LOOPBACK -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -s $LOOPBACK -j DENY

> Als nächstes filtern wir illegale Broadcast Adressen aus, das heißt Broadcast Adressen, die die Broadcast-Absender-Adresse als Empfänger-Adresse haben oder umgekehrt:

>       # Pakete mit illegalen Broadcast Adressen verwerfen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $BROADCAST_DEST -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -d $BROADCAST_SRC -j DENY

> Jetzt folgen die Abweisung der Klasse-D Adressen im Absender-Feld. Klasse D-Adressen sind sogenannte Multicast Adressen, die grundsätzlich nur im Empfänger-Feld auftauchen dürfen. Wir verbieten diese Adressen im Absenderfeld sowohl für ein-, als auch für ausgehende Pakete:

>       # Pakete mit Klasse-D Adresse als Absender verwerfen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $CLASS_D -j DENY
>       ipchains -A output -i $EXTERNAL_INTERFACE -s $CLASS_D -j REJECT

> Als nächstes verbieten wir alle Arten von Klasse-E Adressen, weil diese Adressen reserviert und daher nie legal sein können. Hier genügt es, die eingehenden Pakete zu untersuchen:

>       # Klasse-E Adressen ablehnen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s $CLASS_E -j DENY

> Neben den reservierten Adressen der verschiedenen Klassen, hat die zentrale Vergabestelle für Internetadressen (IANA) noch einige weitere A- und B-Klasse Adressen reserviert. Diese Adressen dürfen nicht im öffentlichen Internet auftauchen, also schließen wir sie aus. Es handelt sich um die Netzadressen
> - 0-2.*.*.*
> - 5.*.*.*
> - 7.*.*.*
> - 23.*.*.*
> - 27.*.*.*
> - 31.*.*.*
> - 37.*.*.*
> - 39.*.*.*
> - 41-42.*.*.*
> - 58-60.*.*.*
> - 65-69.*.*.*
> - 80-95.*.*.*
> - 96-126.*.*.*
> - 217-219.*.*.*
> - 220-223.*.*.*
> Das ist viel Arbeit, weil leider bei einigen Fällen die Masken andere, legale Adressen mitnehmen würden. Es bleibt uns also nichts anderes übrig, als diese Adressen einzeln anzugeben:

>       # Von der IANA reservierte Adressen verwerfen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 1.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 2.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 5.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 7.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 23.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 27.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 31.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 37.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 39.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 41.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 42.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 58.0.0.0/7 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 60.0.0.0/8 -j DENY
>       # 65 entspricht 01000001 - also würde die Maske /3 leider die 64
>       # mit ansprechen. Wir müssen daher 65-79 einzeln angeben
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 65.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 66.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 67.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 68.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 69.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 70.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 71.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 72.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 73.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 74.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 75.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 76.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 77.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 78.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 79.0.0.0/8 -j DENY
>       # 80-95 lässt sich mit der Maske /4 ansprechen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 80.0.0.0/4 -j DENY
>       # 96-111 lässt sich mit der Maske /4 ansprechen
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 96.0.0.0/4 -j DENY
>       # 126 entspricht 01111110 - die Maske /3 würde 127 beinhalten
>       # daher müssen wir 112 - 126 einzeln angeben
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 112.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 113.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 114.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 115.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 116.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 117.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 118.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 119.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 120.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 121.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 122.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 123.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 124.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 125.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 126.0.0.0/8 -j DENY
>       # 217-219 einzeln
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 217.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 218.0.0.0/8 -j DENY
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 219.0.0.0/8 -j DENY
>       # 220-223 -> /6
>       ipchains -A input -i $EXTERNAL_INTERFACE -s 220.0.0.0/6 -j DENY

> Damit hätten wir die offensichtlich ungültigen Adressen jetzt ausgefiltert. Alleine das Volumen dieser Regeln zeigt nochmal deutlich, daß sie nicht jedesmal beim Start neu eingegeben werden sollten. Natürlich kann so viel Information nur in Form von Scripten verarbeitet werden. Jetzt können wir anfangen, bestimmte Dinge zuzulassen - wir erinnern uns, die voreingestellte Policy ist ja DENY, alles ist verboten, was nicht grundsätzlich erlaubt ist.

> ## ICMP-Nachrichten filtern

> ICMP, das Internet Control Message Protocol, wird benutzt, um Fehler- bzw. Kontrollmechanismen zu tansportieren. Es arbeitet auf der IP-Schicht und somit eigentlich nicht mit Portnummern. Trotzdem unterscheidet ICMP verschiedene Nachrichtentypen, die aus der Sicht der Firewall wie Portnummern verwendet werden. Eine Liste der wichtigsten Nachrichtentypen samt ihren Portnummern finden Sie im Anhang auf Seite .

> Vier ICMP-Typen müssen grundsätzlich die Firewall passieren: `source-quench, parameter-problem`, eingehende `destination-unreachable` sowie ausgehende
> `destination-unreachable` Nachrichten vom Subtyp `fragmentation-needed`. Vier weitere Typen sind optional, dazu gehören die beiden für ping notwendigen Typen `echo-request` und echo-reply, sowie alle anderen Sorten von abgehender
> `destination-unreachable` Meldungen und `time-exceeded`. Alle anderen Typen ignorieren wir, sie werden dann von der voreingestellten Policy abgewiesen...

> Wir akzeptieren `source-quench` (Typ 4) Pakete. Sie werden zur Synchronisation benötigt, wenn ein Router schneller, als ein anderer ist.

>       # ICMP Typ 4 erlauben
>       ipchains -A input -i $EXTERNAL_INTERFACE -p icmp\
>                -s $ANYWHERE 4 -d $IPADDR -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p icmp\
>                -s $IPADDR 4 -d $ANYWHERE -j ACCEPT

> Die Darstellung ist mittels Backslash in zwei Zeilen gebrochen, das dient nur der besseren Lesbarkeit, nicht der Funktionalität. Wir erlauben also eingehende Pakete vom Typ ICMP-4 von überall her mit unserer Adresse als Empfänger, sowie ausgehende Pakete dieses Typs, von uns überall hin.

> Genauso verfahren wir mit der Statusmeldung `parameter-problem` (ICMP-Typ 12). Wir erlauben diese Typen von überall her zu unserer Adresse und von unserer Adresse überall hin:

>       # ICMP Typ 12 erlauben
>       ipchains -A input -i $EXTERNAL_INTERFACE -p icmp\
>                -s $ANYWHERE 12 -d $IPADDR -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p icmp\
>                -s $IPADDR 12 -d $ANYWHERE -j ACCEPT

> Bei der Fehlermeldung `destination-unreachable` (ICMP-Typ 3) liegt die Sache etwas anders. Solche Fehlermeldungen entstehen nämlich während eines Portscans, wenn ein Angreifer versucht herauszufinden, welche Ports geöffnet sind. Die einfachste Form wäre jetzt, diesen Typ ganz zu sperren, das geht aber leider nicht, denn ein Untertyp dieser Meldung (`fragmentation-needed`) wird zwingend für das Aushandeln der Paketgröße im Netz benötigt. Aus diesem Grund können wir alle Typ-3 Nachrichten nur an den (oder die) Rechner unseres Providers zulassen, während wir den Subtyp `fragmentation-needed` an alle erlauben:

>       # ICMP-Typ 3 für Providerrechner erlauben
>       ipchains -A input -i $EXTERNAL_INTERFACE -p icmp\
>                -s $ANYWHERE 3 -d $IPADDR -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p icmp\
>                -s $IPADDR 3 -d $MY_ISP -j ACCEPT

>       # ICMP-Typ 3 Subtyp fragmentation-needed für alle freigeben
>       ipchains -A output -i $EXTERNAL_INTERFACE -p icmp\
>                -s $IPADDR fragmentation-needed -d $ANYWHERE -j ACCEPT

> Die letzte zwingende ICMP-Nachricht ist die Statusmeldung `time-exceeded` (ICMP-Typ 11). Auch hier müssen wir nicht zwingend aller Welt erlauben, diese Nachricht von uns zu empfangen, es reicht unser Provider.

>       # ICMP-Typ 11 erlauben (ausgehend nur an unseren Provider)
>       ipchains -A input -i $EXTERNAL_INTERFACE -p icmp\
>                -s $ANYWHERE 11 -d $IPADDR -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p icmp\
>                -s $IPADDR 11 -d $MY_ISP -j ACCEPT

> Die wichtigste aller ICMP-Nachrichten wird im Programm ping benutzt. Mit Hilfe dieses Programms kann festgestellt werden, ob ein anderer Computer über das Netz zu erreichen ist, oder nicht. Der suchende Computer schickt an den gesuchten Computer ein `echo-request` (ICMP-Typ 8). Wenn der gesuchte Rechner dieses Paket empfängt, so schickt er ein `echo-reply` (ICMP-Typ 0 oder auch pong) zurück.

> Üblich ist es, daß wir jeden Host im Internet anpingen dürfen, also gehen Typ 8 Pakete raus und Typ 0 Pakete rein:

>       # Ausgehendes Ping erlauben
>       ipchain -A output -i $EXTERNAL_INTERFACE -p icmp\
>               -s $IPADDR 8 -d $ANYWHERE -j ACCEPT

>       ipchain -A input -i $EXTERNAL_INTERFACE -p icmp\
>               -s $ANYWHERE 0 -d $IPADDR -j ACCEPT

> Wiederum als Beispiel einer eingeschränkten Zulassung von außen, erlauben wir nur den Rechnern unseres Providers, uns anzupingen:

>       # Ankommendes Ping nur für Provider erlauben
>       ipchain -A input -i $EXTERNAL_INTERFACE -p icmp\
>               -s $MYISP 8 -d $IPADDR -j ACCEPT

>       ipchain -A output -i $EXTERNAL_INTERFACE -p icmp\
>               -s $IPADDR 0 -d $MYISP -j ACCEPT

> Damit sind die notwendigen ICMP-Nachrichten erlaubt, die unnötigen verhindert. Jetzt können wir uns langsam an die echten Netzwerkdienste heranwagen...

> ## Dienste auf unprivilegierten Ports

> Dienste, die auf unprivilegierten Ports laufen, erschweren den Aufbau einer Firewall erheblich. Unprivilegierte Ports haben eine Doppelbedeutung, einerseits können Dienste diese Ports nutzen, andererseits können Clients, die andere Dienste nützen, auf diese Portnummern zurückgreifen (siehe Seite ).

> Mit dem TCP-Transportprotokoll ist diese Doppelbedeutung kein Problem, weil wir ja anhand der Steuerflags (SYN und ACK) unterscheiden können, ob das Paket von einem Client oder von einem Server stammt. Schwieriger wird es mit UDP. Aber eins nach dem anderen, schauen wir zunächst mal ein paar TCP-Dienste an:

> ### Häufige TCP-Dienste auf unprivilegierten Ports

> Ein typisches Beispiel ist ein Open Window Display Server (TCP-Port 2000). Ausgehende Verbindungen zu einem solchen Server sollte man sperren. Mit Hilfe der Option `-y` formulieren wir diese Regel so, daß sie nur für den Aufbau einer Verbindung eines lokalen Clients zu einem fremden Server gilt. Fremde Cleients, die zufällig den Port 2000 nutzen, um mit einem unserer Server in Verbindung zu treten, sind von dieser Regel also nicht betroffen. Genausowenig müssen wir eingehende Verbindungswünsche blockieren, denn Linux bietet keinen Open Window Display Server an.

>       # Open Window Verbindungsaufbau sperren
>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp -y\
>                -s $IPADDR -d $ANYWHERE 2000 -j REJECT

> Bei X11-Display-Servern sieht die Sache ähnlich aus, jedoch müssen wir hier auch eingehende Verbindungen unterbinden. Ausgehende Verbindungen werden aus Sicherheitsgründen verboten, weil X11 grundsätzlich auch einen sicheren ssh-Eingang bietet, der immer vorzuziehen ist. X11-Display-Server benutzen die Ports 6000 bis 6063. Auf einem Rechner können also bis zu 64-Server laufen. Ein typischer Fall eines Portbereichs.

>       # X11 Aufbau zu einem fremden Server verbieten
>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp -y\
>                -s $IPADDR -d $ANYWHERE 6000:6063 -j REJECT

>       # X11 Aufbau von außen zu einem unserer Server verbieten
>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp -y\
>                -d $IPADDR 6000:6063 -j DENY -l

> ### UDP-Dienste auf unprivilegierten Ports

> UDP bietet viel weniger Information für das Betreiben einer Firewall, als TCP. Wir haben keinerlei Anhaltspunkt, ob ein Paket eine Client-Anfrage oder eine Server-Antwort ist. Das ist natürlich gerade bei den unprivilegierten Ports eine große Gefahr. Aus diesem Grund sollten wir UDP gänzlich sperren und nur einzelne Verbindungen zu ganz bestimmten Rechnern zulassen.

> Im unprivilegierten Bereich spielt zum Glück nur ein wichtiger UDP-Dienst eine rolle, das Network File System NFS. NFS benutzt den Port 2049. NFS lässt sich zwar auch für TCP konfigurieren, aber das ist wesentlich unüblicher, auch bei TCP benutzt es den Port 2049.

> In der Regel wird NFS ausschließlich im LAN benutzt, eine Firewall sollte es eigentlich selbst nicht brauchen. Wir sperren also die NFS-Dienste völlig:

>       # NFS (2049) über UDP sperren
>       ipchains -A input -i $EXTERNAL_INTERFACE -p udp\
>                -d $IPADDR 2049 -j DENY -l

>       # NFS (2049) über TCP sperren
>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp -y\
>                -d $IPADDR 2049 -j DENY -l

>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp -y\
>                -d $ANYWHERE 2049 -j DENY -l

> ## Wichtige Dienste erlauben

> Nachdem unsere Firewall jetzt soweit konfiguriert ist, können wir uns an die eigentlich wichtige Arbeit machen, die jeweilig benötigten Dienste zuzulassen. Im folgenden sind nur ein paar Beispiele genannt, die wirlich wichtig sind. Im Anhang ab Seite sind die notwendigen Informationen zu den wichtigsten notwendigen Protokollen zusammengestellt.

> ### DNS

> Ohne DNS-Serverdienste wäre das Internet - und wohl heute auch die meisten Intranets - nicht zu gebrauchen. DNS ist also ein Dienst, auf den wir keinesfalls verzichten können. Normalerweise laufen alle DNS-Anfragen über UDP, nur wenn eine Antwort zu groß für ein UDP-Paket wäre, schickt der Server ein gekürztes Paket an den Client zurück, worauf der Client die gleiche Anfrage nochmal über TCP versuchen kann. In der Praxis wird das selten benötigt. Für den Abgleich der Daten zwischen primären und sekundären Nameservern einer Zone (dem sogenannten Zone Transfer) wird allerdings immer TCP benutzt. Um also Pakete durch unsere Firewall zu lassen, die eine UDP-Anfrage bei unserem primären Nameserver zu gestatten, beschränken wir uns auf diese eine IP-Adresse des Nameservers:

>       # DNS IP Adresse:
>       NAMESERVER=xx.xx.xx.xx

>       # UDP-Nameserverzugriff
>       ipchains -A output -i $EXTERNAL_INTERFACE -p udp\
>                -s $IPADDR $UNPRIVPORTS \
>                -d $NAMESERVER 53 -j ACCEPT

>       ipchains -A input -i $EXTERNAL_INTERFACE -p udp\
>                -s $NAMESERVER 53 \
>                -d $IPADDR $UNPRIVPORTS -j ACCEPT

> Damit das Ganze auch über TCP funktioniert, insbesondere, damit ein eventueller Sekundärserver einen Zone-Transfer durchführen kann, benötigen wir noch zwei weitere Regeln, diesmal für TCP:

>       # TCP-Nameserverzugriff
>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp\
>                -s $IPADDR $UNPRIVPORTS \
>                -d $NAMESERVER 53 -j ACCEPT

>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp ! -y\
>                -s $NAMESERVER 53 \
>                -d $IPADDR $UNPRIVPORTS -j ACCEPT

> Beachten Sie hier die Angabe `! -y`, die besagt, daß das ACK-Flag gesetzt sein muß.

> Wenn wir jetzt aber selbst einen Nameserver betreiben, und der in der Lage sein muß, sich mit einem übergeordneten Nameserver zu unterhalten, dann brauchen wir noch einen weiteren Satz Regeln. Diese Kommunikation zwischen zwei Nameservern findet nämlich - standardmäßig - auf dem Port 53 statt - und zwar bei beiden Kommunikationspartnern! Bisher hatten wir ja immer nur Kommunikation zwischen einem unprivilegierten Port mit dem Port 53 des Servers zugelassen. Also los:

>       #DNS Forwarding (Server zu Server)
>       ipchains -A output -i $EXTERNAL_INTERFACE -p udp\
>                -s $IPADDR 53 \
>                -d $NAMESERVER 53 -j ACCEPT

>       ipchains -A input -i $EXTERNAL_INTERFACE -p udp\
>                -s $NAMESERVER 53 \
>                -d $IPADDR 53 -j ACCEPT

> Die letzte Form, die wir für DNS noch besprechen müssen, ist die Frage, ob auch Clients von außerhalb auf unseren Nameserver zugreifen dürfen sollen. Das ist nur dann nötig, wenn wir tatsächlich eine echte Domain mit autoritativen Nameserver betreiben. In der Regel werden wir den Zugriff auf einen bestimmten Kreis von IP-Adressen beschränken. Nennen wir diesen Kreis mal `MYDNSCLIENTS`.

>       # DNS Zugriff fremder Clients
>       MYDNSCLIENTS=ww.xx.yy.zz/mm

>       ipchains -A input -i $EXTERNAL_INTERFACE -p udp\
>                -s $MYDNSCLIENTS $UNPRIVPORTS\
>                -d $IPADDR 53 -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p udp\
>                -s $IPADDR 53\
>                -d $MYDNSCLIENTS $UNPRIVPORTS -j ACCEPT

>       # DNS Forwarding für fremde Clients
>       ipchains -A output -i $EXTERNAL_INTERFACE -p udp\
>                -s $IPADDR 53 \
>                -d $MYDNSCLIENTS 53 -j ACCEPT

>       ipchains -A input -i $EXTERNAL_INTERFACE -p udp\
>                -s $MYDNSCLIENTS 53 \
>                -d $IPADDR 53 -j ACCEPT

> Für den sehr unwahrscheinlichen Fall, daß Sie einen Zone-Transfer von außen zulassen wollen, gibt es noch die Möglichkeit, auch diesen mit Regeln zuzulassen. Allerdings sollten wir hier die Liste der Clients, die diesen Zone-Transfer ausführen dürfen wirklich nur auf die existierenden Sekundären Server von außen beschränken, die diesen Transfer tatsächlich brauchen. Sonst kann jeder alle DNS Informationen unserer Domäne einfach frei Haus bekommen...

>       # DNS Zone-Transfer fremder Clients
>       MYDNSZONECLIENTS=ww.xx.yy.zz/mm

>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp\
>                -s $MYDNSZONECLIENTS $UNPRIVPORTS\
>                -d $IPADDR 53 -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp ! -y\
>                -s $IPADDR 53\
>                -d $MYDNSZONECLIENTS $UNPRIVPORTS -j ACCEPT

> ### Der identd-Service

> Der `identd`-Dienst (manchmal auch `auth` genannt) läuft auf dem TCP-Port 113. Er ist für viele anderen Dienste zwingend notwendig, etwa für das Versenden von E-Mail. Der Server unseres Rechners stellt dabei Informationen über einen bestimmten User zur Verfügung, etwa, ob es diesen User überhaupt gibt....

> Wenn unser Rechner einen FTP- oder Mailserver betreibt, dann fungiert er gleichzeitig immer auch als auth-Client. Es gibt keinen sicherheitsrelevanten Grund, warum unser Rechner soche Anfragen nicht stellen dürfte, also lassen wir solche Anfragen zu:

>       # abgehende auth-Anfragen
>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp\
>                -s $IPADDR $UNPRIVPORTS\
>                -d $ANYWHERE 113 -j ACCEPT

>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp ! -y\
>                -s $ANYWHERE 113 \
>                -d $IPADDR $UNPRIVPORTS -j ACCEPT

> Ob wir diesen Dienst tatsächlich auch anbieten sollen, ist Ansichtssache. Wenn wir uns entscheiden, diesen Dienst zu aktivieren (in /etc/inetd.conf), dann brauchen wir die beiden folgenden Regeln, damit er funktioniert:

>       # ankommende auth-Anfragen
>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp\
>                -s $ANYWHERE $UNPRIVPORTS\
>                -d $IPADDR 113 -j ACCEPT

>       ipchains -A output -i $EXTERNAL_INTERFACE -p tcp ! -y\
>                -s $IPADDR 113 \
>                -d $ANYWHERE $UNPRIVPORTS -j ACCEPT

> Sollten wir uns gegen diesen Dienst entscheiden, so ist es in diesem einzigen Fall günstiger, das Paket mit REJECT statt mit DENY zu behandeln. Ansonsten entstehen hohe Timeout-Wartezeiten. Also:

>       # ankommende auth-Anfragen ablehnen
>       ipchains -A input -i $EXTERNAL_INTERFACE -p tcp\
>                -s $ANYWHERE\
>                -d $IPADDR 113 -j REJECT

> Die weiteren Dienste, die wir vielleicht aktivieren wollen, können jetzt anhand der Beschreibungen im Anhang ab Seite aufgebaut werden. Das grundlegende Prinzip einer Firewall sollte jetzt klar geworden sein. Allerdings hat unsere Firewall bisher noch einen Nachteil, der sie eindeutig in die Amateur-Klasse verweist. Sie schützt im Augenblick nämlich nur sich selbst. In den nächsten Kapiteln werden wir das ändern...

> Um unsere erste Firewall zu aktivieren, muß das Script, das wir gerade erstellt haben nur noch ausführbar gemacht werden und aufgerufen werden. Die Firewall läuft. Natürlich sollte das in einem automatisierten Start-Script - je nach verwendeter Distribution - geschehen.

> Nächste Seite: Die Firewall als Router Aufwärts: Crashkurs Firewallaufbau unter Linux Vorherige Seite: Filtern von ausgehenden Paketen Inhalt