Autor Thema: Einrichten eines Routers mit Firewall unter Linux  (Gelesen 8611 mal)

0 Mitglieder und 1 Gast betrachten dieses Thema.

Offline Dennis

  • HT4U.net Redakteur
  • 64-Bit-Prozessor
  • *****
  • Beiträge: 5939
  • Geschlecht: Männlich
  • Killerspiele-Spieler!
    • Profil anzeigen
    • Meine :)
Einrichten eines Routers mit Firewall unter Linux
« am: 16. Juni 2005, 19:39:58 »
Einrichten eines Routers mit Firewall unter Linux

Die etwas komplexeren Themen unter Linux, Firewalling und Routing, sind, wenn man sich erst einmal eingearbeitet hat sehr interessant. Ziel dieser Doku soll sein eine Iptables basierende Firewall auf einem Linuxsystem aufzusetzen und dieser noch Routingfähigkeiten zu vermitteln. Es wird hier aber etwas Grundverständnis von Netzwerken vorausgesetzt, man sollte also schon wissen was man an Ports, IPs etc. für seinen Rechner braucht. Ich werde hier aber nur die notwendigen Befehle dokumentieren und ein Firewall Script anhängen. Ich werde nicht erklären wie dieses Script im einzelnen funktioniert. Wer eine einfache Firewall selbst erstellen will für den ist diese Doku richtig. Das angehängte Script ist ca. 200 Zeilen lang, nach dem durcharbeiten dieser Doku sollte aber klar sein was dort passiert. Aber selbst gemacht ist doch immer noch am besten ;-).

Firewalling unter Linux mit iptables

Die Policies

Als erste muss man sich damit befassen; Was kann iptables? Ich würde soweit gehen, zu sagen eigentlich alles was man mit IP Traffic anstellen kann: Routen, Ports filtern, Traffic verschleiern etc. Ich werde mich hier nur auf die simplen Regeln bzw. Regelketten von iptables beziehen, da der Umfang von iptables einfach enorm ist. Die Grundketten die immer existieren sind folgende:

INPUTSteht für alle Pakete die ins System wollen
OUTPUTSteht für alle Pakete die aus dem System raus wollen
FORWARDSteht für alle Pakete die weitergeleitet werden wollen

Schauen wir uns doch einfach mal auf einem System ohne aktive Firewall die iptables an. Das geschieht mit dem einfachen Befehl iptables -L. Dieser sollte im Normalfall folgende Ausgabe bringen.

[root@auriga bin]# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
[root@auriga bin]#

Hie sehen wir die o.g. 3 Regelketten. Jede dieser Regelketten ist mit einer Policy versehen, die man ändern kann. Die Policy ist die globale Regel, die als Standard für alle Verbindungen gilt, die der jeweiligen Kette entsprechen. D.h. wenn die Policy von INPUT auf DROP stehen würde, würde sämtlicher eingehender Traffic geblockt, egal was und egal woher der Traffic stammt. Genau andersherum sieht es aus wenn die Policy wie hier auf ACCEPT steht. Des Weiteren ist zu erkennen dass in noch keiner der Ketten eine benutzerdefinierte Regel existiert.

Wie man an den Policies sehen kann, wird jede Verbindung, die nur irgendwie hergestellt werden kann, akzeptiert. Das kann für ein sicheres System so nicht bleiben. Mit dem Befehl iptables -P Policy DROP, Policy steht für INPUT, OUTPUT oder FORWARD kann ich jede Verbindung blockieren. Setze ich jetzt allerdings die Policies auf DROP, so ist das System komplett abgeschottet. Wenn man also per ssh oder telnet an seiner Maschine arbeitet wäre es eine ganz dumme Idee das jetzt zu testen. Wie man an dieser Stelle gut sieht, sollte man das ganze in einem Script schreiben, das die ganzen Befehle abhandelt. Denn wenn die Policies auf DROP stehen, muss der Maschine danach ja mitgeteilt werden welcher Verkehr denn nun aber trotzdem zugelassen wird.

Die erste benutzerdefinierte Regel

Da wir gerade über eine SSH Verbindung arbeiten brauchen wir als erstes einen offenen Port für SSH, das funktioniert wie folgt:

Die Schalter, die wir für diesen Befehl brauchen sind folgende:

-A   ADD Rule, Regel in die danach angegebene Kette einfügen (Hier INPUT)
-i   incoming Interface, Angabe über welche Karte das Paket kommt (ppp0, ethx ...)
-p   Protocol, Angabe des Protokolls (tcp, udp, icmp)
--dport   Destination Port, nur anwendbar mit vorhergehendem -p tcp oder -p udp
   entsprechender Port der überwacht werden soll (Hier 22 für SSH)
-j   jump, was soll mit dem Paket geschehen (ACCEPT, DROP, REJECT, DNAT, SNAT ...)

Der auszuführende Befehl:

[root@auriga bin]# iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT
Wenn wir diesen Befehl eingegeben haben, können wir die Policy für INPUT auf DROP setzen. Jetzt nimmt er aber nur noch Pakete über die Netzwerkkarte eth0 auf Port 22 an, denn der gesamte restliche Verkehr wird jetzt blockiert, selbst INPUT Anfragen vom localhost. Localhost kann aber eh nur jemand sein der am Rechner sitzt und Pakete verschickt was im Normalfall der User selbst sein sollte, also lassen wir alle eingehenden Verbindung vom localhost auch zu.

[root@auriga bin]# iptables -A INPUT -i lo -j ACCEPT
Hier sieht man gut, dass man nicht immer mit Protokollangaben und Ports arbeiten muss. Ich sage einfach INPUT vom Interface lo akzeptieren. Lassen wir uns noch einmal die iptables anzeigen.

[root@auriga bin]# iptables -L
Chain INPUT (policy DROP)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere           tcp dpt:ssh
ACCEPT     all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
[root@auriga bin]#


Hier sehen wir jetzt, dass die Policy für INPUT auf DROP geändert wurde und unsere beiden erstellten Regeln in der Kette Input enthalten sind. Mit dem Befehl iptables -L -v kann man noch eine detailliertere Ausgabe aktivieren, dort sieht man dann auch die Interfaces.

Um aber noch ein Stufe auf der Sicherheitsleiter hoch zu gehen, setzen wir die Policies OUTPUT und FORWARD auch auf DROP. Vorher müssen wir aber noch angeben, welche Interfaces noch ins Netz senden dürfen. Das sind bei uns die beiden Netzwerkkarten eth0, eth1 und das lo interface, denn raus ist ja erst mal egal, rein darf nichts kommen.

[root@auriga bin]# iptables -A OUTPUT -o eth0 -j ACCEPT
[root@auriga bin]# iptables -A OUTPUT -o eth1 -j ACCEPT
[root@auriga bin]# iptables -A OUTPUT -o lo -j ACCEPT

und danach

[root@auriga bin]# iptables -P OUTPUT DROP
[root@auriga bin]# iptables -P FORWARD DROP

Jetzt ist sämtlicher Verkehr von innen nach aussen erlaubt, aber es kann nur eine Verbindung über Port 22 TCP von aussen nach innen erfolgen der Rest ist blockiert. Um weitere Ports freizugeben einfach an den obigen Befehl für SSH halten und aus der 22 den gewünschten Port machen, bzw. für UDP Verbindungen aus dem -p tcp ein -p udp machen und den gewünschten Port ändern. Falls ihr nicht wisst welche Ports ihr freigeben müsst gibt es in der /etc/services eine sehr gute Übersicht. Hier einmal kurz die wichtigsten Ports.

20,21TCPFTP
22TCPSSH
80TCPHTTP
445TCPSAMBA

Erweiterte Regeln & Zusatzmodule

Hat man seine ganzen Ports korrekt freigegeben, kann man noch folgende 2 Befehle einfügen.

[root@auriga bin]# iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
Damit werden neu ausgehende Pakete geblockt, die nicht zu einer regulären Verbindung gehören. Eine TCP Paket was eine neue Verbindung anfordert, würde ein syn Flag haben. Durch das ! wird hier aber abgefragt ob es ein Paket ist, dass kein gültiges syn Flag hat, aber trotzdem eine neue Verbindung öffnen will. Sollte dies  der Fall sein so wird dieses Paket geblockt.

[root@auriga bin]# iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
Dieser Befehl erlaubt Pakete durchzuschleusen auf schon hergestellten (ESTABLISHED) Verbindungen, oder auf Verbindungen, die zu einer anderen offenen Verbindung Bezug hat. (RELATED)

Hier hinzugekommen ist der Schalter -m dieser lädt verschiedene Module, in diesem Fall das Modul state. Als zweiter Schalter, der zu dem Modul state gehört sieht man --state. Damit kann man verschiedene Statusabfragen an dem aktuellen Paket durchführen was hier mit NEW, ESTABLISHED und RELATED geschehen ist.

Wer aufmerksam gelesen hat, dem dürfte bei dem Jump Schalter noch das REJECT in Erinnerung sein, dieses hat die gleiche Wirkung wie DROP, schickt aber eine Fehlermeldung zurück. Sollte also ein Rechner von außen auf unseren Router zugreifen und auf eine Regel Treffen, die bei uns mit einem REJECT behandelt wird, so bekommt er eine Fehlermeldung und weiß das der Rechner da ist. Ist die Verbindung mit einem DROP versehen, so wird das ankommende Paket einfach weggeschmissen und es wird auf den Verbindungsversuch nicht reagiert. Es gibt noch viel mehr als nur ACCEPT, DROP und REJECT für eine komplette Liste aller möglichen Varianten kann man sich die MAN-PAGES von iptables mit man iptables anschauen.

Das ist eigentliche das Grobe zum Firewalling, iptables ist ein sehr effektives Tool zum Verwalten von IP Verkehr das ich hier in seinen kompletten Möglichkeiten nur anreissen kann. Kommen wir dann jetzt zum wirklich interessanten Teil, dem Routing.

Wer noch einen alten lautlosen Rechner herumzustehen hat, für den dürfte das folgende Kapitel sehr interessant werden. Voraussetzung dazu sind eben dieser Rechner mit einer funktionierenden Linux Installation, 2 Netzwerkkarten, oder die etwas unschöne Variante mit einer Karte und einem Switch.

Routing unter Linux mit iptables

Als erstes müssen wir aus dem Rechner ein Gateway machen, so dass er überhaupt als Router fungieren kann. Dazu muss IP Forwarding aktiviert werden, ohne dem würde dass Gerät keine IP Adressen durchreichen und wäre einfach nur ein Rechner im Netz. Um das IP Forwarding zu aktivieren muss in der Datei /proc/sys/net/ipv4/ip_forward eine 1 eingetragen werden. Das geht ohne die Datei manuell öffnen zu müssen ganz einfach mit dem Befehl.

[root@auriga bin]# echo "1" > /proc/sys/net/ipv4/ip_forward
Echo ist nur für Bildschirmausgabe zuständig, würde ich nur echo "1" eingeben so würde mir auf dem Bildschirm einfach eine 1 ausgegeben werden. Durch das > leite ich die Ausgabe in die dahinter- stehende Datei um. Somit steht jetzt die 1 in der ip_forward Datei.

Jetzt muss noch die IP Verschleierung aktiviert werden, d.h. Pakete die von einem Rechner an den Router geschickt werden erhalten als AbsenderIP die IP des Routers. Würde er das nicht machen, so würde das Paket mit der Absende IP meines Rechners rausgehen und das Ziel würde auf diese Adresse versuchen zu antworten was ja unmöglich ist. Stattdessen muss die Antwort an den Router erfolgen der sie dann zu mir schickt. Um dieses zu realisieren gibt es noch 2 weitere Tabelle die Nat und die Mangle Tables. In diese müssen die Einträge fürs Routing erfolgen. Um in eine andere Tabelle eine Regel hinzuzufügen, muss mit dem Schalter -t die Tabelle angegeben werden. Der fertige Befehl sieht wie folgt aus.

[root@auriga bin]# iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
Durch -t nat wird die Regel in die NAT Tabelle, und durch -A POSTROUTING in die Kette POSTROUTING eingefügt. Diese sorgt für Paketänderungen an Paketen, die dabei sind aus dem Router rauszugehen. Es gibt dort noch die Ketten PREROUTING und OUTPUT. PREROUTING steht dabei für Änderungen an eingehenden Paketen und OUTPUT kann Änderungen an lokal erstellten Paketen vornehmen.

Ändern von Paketdaten

Die Verschleierung mit MASQUERADE macht aber nur Sinn bei Interfaces, an denen sich die IP des Öfteren ändert, wie z.B. ISDN oder DSL Verbindungen. Die o.g. angegebene Lösung ist daher nicht die ganz optimalste, für das Routen in einem Netzwerk würde sich eher SNAT anbieten. Hierbei gebe ich dann die IP des Routers direkt an.

[root@auriga bin]# iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 172.21.18.18
Mit SNAT --to wird festgelegt dass alle Pakete die über das Interface eth1, also unsere 2. Netzwerkkarte, rausgehen als Absender IP Adresse die 172.21.18.18 erhalten. Jetzt funktioniert unser Router ohne Probleme für rausgehende Verbindungen. Dadurch dass die Verbindung von unserem Rechner etabliert wird und durch den Router rausgeht, weiß der Router auch wo die Antwortpakete hinmüssen. Wenn jetzt aber Anfragen von Außen kommen, die auf meinem Rechner landen sollen, so laufen diese Anfragen am Router auf und er weiß nichts damit anzufangen. Das beste Beispiel ist hierfür ein FTP-Server. Ich habe auf meinem Rechner einen FTP-Server gestartet auf den jemand von Außen zugreifen will. Jetzt landet auf dem Router eine Anfrage auf Port 21, da auf dem Router aber kein FTP-Server läuft und er nichts damit anfangen kann schmeißt er das Paket weg und gibt an den Anfragenden Clienten zurück Server nicht erreichbar. Jetzt gibt es über die NAT Tabelle die Möglichkeiten zu sagen, Pakete die auf einem bestimmten Interface reinkommen und auf Port 21 adressiert sind an eine bestimmte IP zu senden. Diese Regel wird dann verständlicherweise in die PREROUTING Kette geschrieben da diese Änderung an eingehenden Paketen durchgeführt wird.

Also übliche Konfiguration das Paket kommt über eth1 an und soll auf meinen Rechner, der an eth0 verbunden ist mit der IP 172.21.1.128. Als erstes der Befehl:

[root@auriga bin]# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 21 -j DNAT --to 172.21.1.128
Anstatt SNAT wird hier als Ziel DNAT eingetragen, da dieses Mal nicht die Source sondern die Destination IP geändert wird. Alle Pakete die an eth1 ankommen und auf Port 21 anklopfen, bekommen jetzt als Zieladresse die 172.21.1.128 und erreichen den FTP-Server der auf meinem Rechner läuft. Jetzt stehen wir aber vor einem Problem. Wir haben am Anfang die Policy fürs IP Forwarding auf DROP gesetzt, d.h. es wird trotz der eingestellten Routen nicht ein Paket weitergereicht, da dass von unserer Firewall blockiert wird. Die einfachste Variante wäre die Policy wieder auf ACCEPT zu setzen, da man ohne Routing Regeln, von außen über den Router, sowieso nicht an die dahinter liegenden Rechner herankommt. Aber da wir eine sichere Firewall haben wollen und die Policy daher auf DROP lassen, muss es noch einen anderen Weg geben.

Freischalten der Paketweiterleitung

Wir erinnern uns an die 3 Ketten in der Firewall INPUT, OUTPUT und FORWARD. Wir haben Regeln für INPUT und OUTPUT festgelegt, aber noch keine für FORWARD. In die FORWARD Kette werden nämlich genau die Regeln eingetragen die das Routing zulassen. In der FORWARD Kette muss immer ein Interface angegeben werden, über das die Pakete hereinkommen und das Interface an das sie weitergeleitet werden. Lassen wir am besten erst einmal die Weiterleitung von meinem PC durch den Router nach Außen zu:

[root@auriga bin]# iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT
Verkehr der durch eth0 hereinkommt und an eth1 wieder raus soll wird zugelassen. Jetzt kann von außen aber immer noch keiner auf meinen FTP-Server zugreifen. Jetzt kommen wieder die identischen Schalter, wie auch bei INPUT und OUTPUT zum Einsatz und zusätzlich -d mit dem ich die Zieladresse der Pakete angebe.

[root@auriga bin]# iptables -A FORWARD -i eth1 -o eth0 -d 172.21.1.128 -p tcp --dport 21 -j ACCEPT
Pakete die auf eth1 ankommen und über eth0 auf die Adresse 172.21.1.128:21 geleitet werden sollen, werden erlaubt. Mit diesen Regeln bewaffnet kann man sich einen sauber funktionierenden Router zusammenbasteln, den man nach Herzenslust selbst konfigurieren kann. Vor allem ist er nicht von der Stange, sondern selbst gemacht.

Sonstige Tipps und Hinweise

Zu beachten ist, dass für Pakete die weiter geleitet werden sollen, bei INPUT keine Ports geöffnet werden müssen. Für diese Pakete ist der Router gar nicht da, sie treffen zwar bei ihm ein, werden aber nur durchgeschleust. Beim erstellen der Routing Regeln ist darauf achten, dass auch für jede eine FORWARD Regel angelegt wird, bzw. man lässt die Policy auf ACCEPT je nach dem wie sicher man es machen will.

Sollte man auf absolute Sicherheit bedacht sein, so kann man zu jeder Regel noch einstellen dass ein Paket von einer bestimmter Quelle kommen muss. Man könnte SSH z.B so einstellen dass nur mein Rechner auf diesen Port zugreifen darf. Wir erinnern uns an den ersten Befehl:

[root@auriga bin]# iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT
Jeder der auf eth0 an Port 22 eine SSH Verbindung anfordert wird zugelassen. Wenn jetzt aber nur mein Rechner auf diesen Dienst Zugriff haben soll, so können wir noch explizit die Source IP angeben. Das ganze sieht dann so aus:

[root@auriga bin]# iptables -A INPUT -i eth0 -s 172.21.1.128 -p tcp --dport 22 -j ACCEPT
Jetzt kann nur noch der Rechner mit der IP 172.21.1.128 auf Port 22 zugreifen. Man kann hier alternativ auch ein Netz angeben z.B. 172.21.0.0/18. Dadurch können nur noch Rechner aus meinem Netz auf SSH zugreifen. Verantwortlich dafür ist der Schalter -s (Source).



Und mein persönliches Firewall Skript

Das habe ich in 2 Tagen Langeweile auf Arbeit erstellt und es gerade auf meinem Rechner hier installier. Es läuft 1A ;D

Fragen, Anregungen und Kritik könnt ihr Hier loswerden.
« Letzte Änderung: 14. November 2005, 14:58:36 von CaBaL »
|| HT4U ||

Offline Dennis

  • HT4U.net Redakteur
  • 64-Bit-Prozessor
  • *****
  • Beiträge: 5939
  • Geschlecht: Männlich
  • Killerspiele-Spieler!
    • Profil anzeigen
    • Meine :)
Diskussion zu Router mit Firewall unter Linux
« Antwort #1 am: 16. Juni 2005, 19:42:01 »
KLICK

Mal wieder ein Excerpt meiner erfolgreichen Testrechnerquälerei :P

Wen es interessiert bitte ausreichend Kritik, bin momentan noch am überarbeiten, finde das Thema aber so interessant da hab ichs heute schon einmal gepostet ;)
« Letzte Änderung: 16. Juni 2005, 19:43:02 von CaBaL »
|| HT4U ||

deathinjune

  • Gast
Re:Diskussion zu Router mit Firewall unter Linux
« Antwort #2 am: 21. Juni 2005, 23:02:22 »
Mag mich ja irren, aber kann es sein, dass das nur unter Debian funktioniert? Ich hab' das Skript überflogen und mich gefragt, ob das überhaupt mit Fedora und Redhead so hinhaut.

Offline Dennis

  • HT4U.net Redakteur
  • 64-Bit-Prozessor
  • *****
  • Beiträge: 5939
  • Geschlecht: Männlich
  • Killerspiele-Spieler!
    • Profil anzeigen
    • Meine :)
Re:Diskussion zu Router mit Firewall unter Linux
« Antwort #3 am: 22. Juni 2005, 18:16:30 »
Hmm, kann ich nicht nachvollziehen.

Was soll denn nicht funktionieren ??? und *hust* das Skript wurde unter RedHat erstellt ;)
|| HT4U ||