Nächste Seite: Die Firewall als Router
Aufwärts: Crashkurs Firewallaufbau unter Linux
Vorherige Seite: Filtern von ausgehenden Paketen
  Inhalt
Unterabschnitte
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.
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 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. |
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 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.
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
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, 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, 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:
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 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
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.
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-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
root
2001-08-07