[OpenBSD]

[Zurück: Firewall-Redundanz mit CARP und pfsync] [Inhalt]

PF: Beispiel: Firewall für zuhause oder ein kleines Büro


Inhaltsverzeichnis


Das Szenario

In diesem Beispiel läuft PF auf einer OpenBSD-Maschine, deren Aufgabe es ist, als Firewall und NAT-Gateway für ein kleines Netzwerk zuhause oder in einem Büro zu agieren. Das Gesamtziel ist, dem Netzwerk Internetzugriff und begrenzten Zugriff zur Firewallmaschine vom Internet aus zu gewähren. Des Weiteren soll ein interner Webserver vom Internet aus erreichbar gemacht werden. Dieses Dokument wird einen kompletten Regelsatz durchgehen, der genau das macht.

Das Netzwerk

Das Netzwerk ist wie folgt aufgebaut:

  [ COMP1 ]    [ COMP3 ]
      |            |
   ---+------+-----+------- xl0 [ OpenBSD ] fxp0 -------- ( Internet )
             |
         [ COMP2 ]

In diesem internen Netzwerk befinden sich einige Computer. Das Diagramm zeigt drei, aber die tatsächliche Anzahl ist unbedeutend. Diese Computer sind reguläre Arbeitsplätze, die fürs Websurfen, E-Mail, Chatten etc. verwendet werden. COMP3 ist eine Ausnahme, da er als kleiner Webserver läuft. Das interne Netzwerk verwendet den Netzwerkblock 192.168.0.0/255.255.255.0.

Die OpenBSD-Firewall ist ein Celeron 300 mit zwei Netzwerkkarten: eine 3com 3c509B (xl0) und eine Intel EtherExpress Pro/100 (fxp0). Die Firewall hat eine Kabelverbindung zum Internet und verwendet NAT, um diese Verbindung mit dem internen Netzwerk zu teilen. Die IP-Adresse des externen Interfaces wird vom Internetprovider dynamisch zugewiesen.

Das Ziel

Die Ziele sind:

Vorbereitung

Dieses Dokument nimmt an, dass der OpenBSD-Host ordentlich konfiguriert wurde, sodass er als Router funktioniert - einschließlich der Überprüfung der IP-Netzwerkeinstellungen, Internetverbindung und dass die sysctl(3)-Variablen net.inet.ip.forwarding und/oder net.inet6.ip6.forwarding auf »1« eingestellt wurde. Du musst ebenfalls PF mittels pfctl(8) starten oder die entsprechende Variable in /etc/rc.conf.local aktivieren. PF ist seit dem Release OpenBSD 4.6 standarmäßig aktiviert.

Der Regelsatz

Das nächste Kapitel wird Schritt für Schritt durch einen Regelsatz gehen, der die zuvor genannten Ziele realisiert.

Makros

Die folgenden Makros wurden definiert, um die Wartung und das Lesen des Regelsatzes einfacher zu machen:
int_if="xl0"

tcp_services="{ 22, 113 }"
icmp_types="echoreq"

comp3="192.168.0.3"

Die erste Zeile definiert eine interne Netzwerkschnittstelle, auf der das Filtern stattfinden wird. Da wir sie hier definieren, können wir den Regelsatz bei einer Hardwareumstellung so belassen wie er ist und müssen lediglich diese eine Zeile ändern. (Für dieses Beispiel wird die externe Schnittstelle von der egress Schnittstellengruppe gehandhabt. In dieser finden sich alle Schnittstellen, für die eine Standard-Route gesetzt ist, in diesem Fall fxp0.) Die zweite und dritte Zeile listen die TCP-Portnummern der Dienste auf, die dem Internet gegenüber offengelegt werden (SSH und ident/auth) und den ICMP-Pakettyp, dem erlaubt wird, die Firewallmaschine zu erreichen. Zum Schluss definiert die letzte Zeile die IP-Adresse von COMP3.

Hinweis: Wenn die Internetverbindung PPPoE benötigt, werden Filter und NAT auf der Schnittstelle pppoe0 stattfinden und nicht auf der egress-Schnittstelle (fxp0).

Optionen

Die folgenden beiden Optionen werden die standardmäßige Antwort für block-Filterregeln setzen und Statistikaufzeichnungen für das externe Interface anstellen:
set block-policy return
set loginterface egress

Jedes Unix-System hat ein sogenanntes Loopbackinterface. Hierbei handelt es sich um ein virtuelles Netzwerkinterface, das von Applikationen genutzt wird, um mit anderen auf dem gleichen System zu kommunizieren. Unter OpenBSD ist das Loopbackinterface lo(4). Es ist allgemein gute Praxis, jegliches Filtern auf den Loopbackinterfaces zu unterbinden. Die Verwendung von set skip bewerkstelligt dies.

set skip on lo
Beachte, dass wir alle lo-Schnittstellen überspringen. Auf diese Weise können wir später noch Loopbackinterfaces hinzufügen, ohne dass wir uns Gedanken darüber machen müssten, diesen Teil unseres Regelsatzes anzupassen.

Firewall-Regeln

Wir starten mit Regeln zur Unterstützung des ftp-proxy(8), sodass sich FTP-Clients des lokalen Netzwerks mit FTP-Servern im Internet verbinden können. Dies funktioniert durch dynamisches Einfügen von Regeln, wenn eine FTP-Verbindung aufgebaut wird. Dies wird mit Hilfe von Ankern bewerkstelligt:
anchor "ftp-proxy/*"

Nun fügen wir die Regel hinzu, die benötigt wird, um FTP-Verbindungen dergestalt umzuleiten, dass sie vin ftp-proxy(8) gesehen werden:

pass in quick on $int_if inet proto tcp to any port ftp \
    divert-to 127.0.0.1 port 8021

Diese Regel fängt jegliche FTP-Verbindung zu Port 21 ab, leitet sie zu der ftp-proxy(8)-Instanz auf Port 8021 um, und vermeidet, durch Nutzung des Schlüsselworts quick, eine Abarbeitung von entsprechenden Paketen durch den Rest des Regelsatzes. Verbinden sich Benutzer regulär auf anderen Ports mit FTP-Servern, so sollte eine Liste mit Zielports spezifiziert werden, zum Beispiel: to any port { 21, 2121 }.

Beachte, dass sich sowohl der Anker, als auch die ftp-proxy(8) Umleitungsregel vor jeglicher match-Regel für NAT befinden müssen, oder andernfalls wird der ftp-proxy(8) nicht wie erwartet funktionieren.

Machen wir weiter mit einigen match-Regeln. Für sich selbst kann eine match-Regel nicht festzustellen, ob es einem Paket erlaubt ist, zu passieren. Stattdessen werden die Parameter der Regel erinnert, denen ein Paket entsprach; diese werden dann für jegliche, die Pakete weiterverarbeitenden pass-Regeln benutzt.

Dies ist leistungsfähig: Parameter wie NAT oder Warteschlangen können auf bestimmte Klassen von Paketen angewendet werden, und so können Zugriffsrechte separat definiert werden.

Um NAT für das gesamtes internes Netzwerk auszuführen, wird die folgende match-Regel genutzt:

match out on egress inet from !(egress:network) to any nat-to (egress:0)

In diesem Fall könnte das »!(egress:network)« einfach durch ein »$int_if:network« ersetzt werden, aber wenn du mehrere interne Schnittstellen hinzufügen würdest, so müsstest du zusätzliche NAT-Regeln hinzufügen, währenddessen mit dieser Struktur NAT auf allen geschützten Schnittstellen ausgeführt wird.

Da die IP-Adressen der externen Schnittstelle dynamisch zugewiesen werden, werden Klammern um die Übersetzungsschnittstelle gesetzt, sodass PF bemerkt, wenn sich eine Adresse ändert. Die Nachsilbe :0 wird benutzt so dass, falls die externe Schnittstelle mehrere Adressen besitzt, nur die erste Adresse für die Übersetzung benutzt wird.

Zuletzt wird die Protokollfamilie inet (IPv4) spezifiziert. Dies verhindert die Übersetzung aller inet6 (IPv6)-Pakete, welche eventuell empfangen werden.

Nun die Regeln zur Kontrolle von Zugriffsrechten. Beginne mit der standardmäßigen Verweigerung.

block in log

An diesem Punkt wird jeglicher Datenverkehr, der versucht, in eine Schnittstelle zu gelangen, blockiert, selbst solcher aus dem internen Netzwerk. Alle diese Pakete werden protokolliert. Spätere Regeln werden die Firewall für obige Objektiven öffnen, und ebenfalls alle notwendigen virtuellen Schnittstellen öffnen.

Denke daran, PF kann sowohl in eine Schnittstelle eingehenden, als auch aus einer Schnittstelle ausgehenden Verkehr blocken. Es kann dein Leben vereinfachen, wenn du dich dafür entscheidest, den Verkehr einer Richtung zu filtern, anstatt zu versuchen die Dinge einfach zu halten, wenn man einige Sachen in der einen, und andere in der anderen Richtung filtert. In unserem Fall entscheiden wir uns dafür, den eingehenden Verkehr zu filtern, aber sobald der Datenverkehr einmal für die Schnittstelle zugelassen ist, werden wir nicht mehr versuchen, sie am Verlassen zu hindern, sodass wir bei folgendem Konstrukt enden:

pass out quick
Durch Nutzung von quick wird die Überprüfung ausgehender Pakete durch folgende Regeln vermieden, was die Leistung erhöht.

Die Blockade gefälschter Adressen ist gut:

antispoof quick for { lo $int_if }

Nun öffne die Ports, die von den Netzwerkdiensten genutzt werden, die über das Internet erreichbar sein sollen. Zuerst, der Verkehr, der für die Firewall selbst bestimmt ist:

pass in on egress inet proto tcp from any to (egress) \
    port $tcp_services

Die Spezifikation der Netwerk-Ports durch das Makro $tcp_services macht das Öffnen weiterer Dienste für das Internet einfach, da einzig das Makro geändert, und der Regelsatz neu geladen werden muss. UDP-Dienste können ebenfalls, durch die Erzeugung eines Makros $udp_services und dem Hinzufügen einer Filterregel ähnlich der obigen, die jedoch proto udp spezifizieren muss, geöffnet werden.

Die nächste Regel fängt alle Versuche ab, aus dem Internet zu dem TCP-Port 80 der Firewall eine Verbindung aufzubauen. Legitime Versuche, auf diesen Port zuzugreifen, kommen von Benutzern, die auf den Webserver des Netzwerks zugreifen möchten. Diese Verbindungsversuche müssen zu COMP3 umgeleitet werden:

pass in on egress inet proto tcp to (egress) port 80 rdr-to $comp3

ICMP-Datenverkehr muss durchgelassen werden:

pass in inet proto icmp all icmp-type $icmp_types

Ähnlich dem Makro $tcp_services kann ein Makro $icmp_types einfach editiert werden, um die Arten von ICMP-Paketen, denen es erlaubt werden soll, die Firewall zu erreichen, zu ändern. Beachte, dass diese Regel für alle Netzwerkschnittstellen gilt.

Nun muss der Datenverkehr zu und von dem internen Netzwerk durchgelassen werden. Wir nehmen an, dass die Benutzer des internen Netzwerks wissen was sie tun, und keinerlei Ärger verursachen. Dies ist nicht notwendigerweise eine richtige Annahme; ein viel restriktiverer Regelsatz wäre in vielen Umgebungen angemessen.

pass in on $int_if

TCP-, UDP- und ICMP-Datenverkehr ist es durch die frühere Zeile »pass out« erlaubt, die Firewall in Richtung Internet zu verlassen. Zustandsinformationen werden aufbewahrt, sodass zurückkommende Pakete ihren Weg zurück durch die Firewall nehmen können.

Der komplette Regelsatz

# macros

int_if="xl0"

tcp_services="{ 22, 113 }"
icmp_types="echoreq"

comp3="192.168.0.3"

# options

set block-policy return
set loginterface egress
set skip on lo

# FTP Proxy rules

anchor "ftp-proxy/*"

pass in quick on $int_if inet proto tcp to any port ftp \
    divert-to 127.0.0.1 port 8021

# match rules

match out on egress inet from !(egress:network) to any nat-to (egress:0)

# filter rules

block in log
pass out quick

antispoof quick for { lo $int_if }

pass in on egress inet proto tcp from any to (egress) \
    port $tcp_services

pass in on egress inet proto tcp to (egress) port 80 rdr-to $comp3

pass in inet proto icmp all icmp-type $icmp_types

pass in on $int_if

[Zurück: Firewall-Redundanz mit CARP und pfsync] [Inhalt]


[zurück] www@openbsd.org
$OpenBSD: example1.html,v 1.32 2012/11/02 07:24:05 ajacoutot Exp $