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

Unterabschnitte

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:

Zusammengefasst könnte man eine Firewall mit all ihren Regelketten also folgendermaßen darstellen:


\includegraphics{firewall1.eps}


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:

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 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.


next up previous contents
Nächste Seite: Die Firewall als Router Aufwärts: Crashkurs Firewallaufbau unter Linux Vorherige Seite: Filtern von ausgehenden Paketen   Inhalt
root 2001-08-07