29.3. PF

Überarbeitet und aktualisiert von John Ferrell.

In FreeBSD 5.3 wurde PF von OpenBSD in das Basissystem integriert. Bei PF handelt es sich um eine komplette, voll ausgestattete Firewall, die optional auch ALTQ (Alternatives Queuing) unterstützt. ALTQ stellt Quality of Service (QoS) zur Verfügung.

Das OpenBSD-Projekt pflegt die maßgebliche Referenz von PF in der PF FAQ. Peter Hansteen betreut ein sehr ausführliches PF-Tutorial unter http://home.nuug.no/~peter/pf/.

Warnung:

Bedenken Sie beim Studium der PF FAQ, dass FreeBSD die PF-Version aus OpenBSD 4.5 enthält.

Die FreeBSD packet filter mailing list ist ein guter Anlaufpunkt für Fragen zur Konfiguration und dem Einsatz der PF-Firewall. Überprüfen Sie aber zunächst die Archive der Mailingliste, bevor Sie eine Frage stellen. Vielleicht wurde die Frage dort schon beantwortet.

Weitere Informationen über die Portierung von PF nach FreeBSD finden Sie unter http://pf4freebsd.love2party.net/.

Dieser Abschnitt konzentriert sich auf PF in FreeBSD. Es wird beschrieben, wie PF und ALTQ aktiviert werden. Anschließend wird demonstriert, wie Regelsätze auf einem FreeBSD-System erstellt werden.

29.3.1. PF aktivieren

Damit PF benutzt werden kann, muss zunächst das Kernelmodul geladen werden. Dieser Abschnitt beschreibt die Einträge für /etc/rc.conf, die verwendet werden können um PF zu aktivieren.

Beginnen Sie mit folgender Zeile in /etc/rc.conf:

pf_enable="YES"

pfctl(8) beschreibt zusätzliche Optionen, die beim Start an PF übergeben werden können. Fügen Sie diesen Eintrag in /etc/rc.conf hinzu und schreiben Sie die benötigten Optionen zwischen die Anführungszeichen:

pf_flags=""                     # additional flags for pfctl startup

PF kann nicht gestartet werden, wenn es seine Konfigurationsdatei nicht findet. In der Voreinstellung existiert bereits ein Regelsatz namens /etc/pf.conf. Wenn bereits ein Regelsatz an anderer Stelle gespeichert wurde, fügen Sie in /etc/rc.conf einen Eintrag mit dem vollständigen Pfad zur Datei ein:

pf_rules="/path/to/pf.conf"

Protokollierungsfunktionen für PF werden von pflog(4) zur Verfügung gestellt. Fügen Sie folgende Zeile in /etc/rc.conf ein, um diese Funktion zu aktivieren:

pflog_enable="YES"

Die folgenden Zeilen können ebenfalls hinzugefügt werden, um den Speicherort der Protokolldatei zu bestimmen und weitere Optionen beim Start an pflog(4) zu übergeben:

pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup

Falls ein LAN hinter der Firewall existiert und die Pakete an die Rechner im LAN weitergeleitet werden müssen, oder wenn NAT benötigt wird, fügen Sie die folgende Option hinzu:

gateway_enable="YES"            # Enable as LAN gateway

Nachdem die Änderungen gespeichert wurden, kann PF mit Unterstützung für Protokollierung gestartet werden:

# service pf start
# service pflog start

In der Voreinstellung liest PF seine Konfiguration aus /etc/pf.conf und modifiziert, verwirft oder akzeptiert Pakete anhand der Definitionen in dieser Datei. FreeBSD enthält mehrere Beispieldateien unter /usr/share/examples/pf/. Auch die PF FAQ enthält sehr ausführliche Beispiele für PF-Regeln.

Zur Steuerung von PF wird pfctl verwendet. Tabelle 29.1, „Nützliche pfctl Optionen“ fasst einige nützliche Optionen für diesen Befehl zusammen. Eine Beschreibung aller verfügbaren Optionen finden Sie in pfctl(8).

Tabelle 29.1. Nützliche pfctl Optionen
KommandoAufgabe
pfctl -ePF aktivieren
pfctl -dPF deaktivieren
pfctl -F all -f /etc/pf.confAlle Filterregeln zurücksetzen (NAT, Filter, Zustandstabelle) und /etc/pf.conf erneut einlesen.
pfctl -s [ rules | nat | states ]Zusammenfassung der Filterregeln, NAT-Regeln, oder der Zustandstabelle.
pfctl -vnf /etc/pf.confÜberprüft /etc/pf.conf auf Fehler, lädt aber die Filterregeln nicht neu.

Tipp:

security/sudo ist nützlich um Kommandos mit erhöhten Berechtigungen auszuführen, wie beispielsweise pfctl. Das Programm kann aus der Ports-Sammlung installiert werden.

Um den ein- und ausgehenden Verkehr im Auge zu behalten, können Sie ein Werkzeug wie sysutils/pftop benutzen. Sobald das Programm installiert ist, können Sie pftop ausführen, um einen Snapshot des Datenverkehrs zu sehen. Das Format der Ausgabe ist der von top(1) sehr ähnlich.

29.3.2. ALTQ aktivieren

Unter FreeBSD kann ALTQ zusammen mit PF benutzt werden, um Quality of Service (QoS) bereitzustellen. Sobald ALTQ aktiviert ist, können Warteschlangen definiert werden, mit denen Sie die Priorität für ausgehende Pakete festlegen können.

Bevor Sie ALTQ aktivieren, sollten Sie altq(4) lesen und sicherstellen, das der Treiber der Netzwerkkarte diese Funktion unterstützt.

ALTQ steht nicht als ladbares Kernelmodul zur Verfügung. Wenn die Netzwerkkarte des Systems ALTQ unterstützt, erstellen Sie nach den Anweisungen in Kapitel 8, Konfiguration des FreeBSD-Kernels einen angepassten Kernel. Als erstes muss ALTQ aktiviert werden. Zudem ist mindestens eine weitere Option nötig, um den Algorithmus für die Warteschlange zu bestimmen:

options         ALTQ
options         ALTQ_CBQ        # Class Based Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Schedule (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)

Die folgenden Algorithmen stehen zur Verfügung:

CBQ

Class Based Queuing (CBQ) erlaubt es, die Bandbreite einer Verbindung in verschiedene Klassen oder Warteschlangen zu unterteilen, um die Priorität von Datenpaketen basierend auf Filterregeln zu beeinflussen.

RED

Random Early Detection (RED) wird eingesetzt, um eine Überlastung des Netzwerks zu vermeiden. Dazu ermittelt RED die Größe der Warteschlange und vergleicht diesen Wert mit den minimalen und maximalen Grenzwerten der Warteschlange. Ist die Warteschlange größer als das erlaubte Maximum, werden alle neuen Pakete nach dem Zufallsprinzip verworfen.

RIO

Random Early Detection In and Out (RIO). Dieser Modus verwaltet mehrere Warteschlangen durchschnittlicher Größe mit mehreren Schwellwerten, eine für jedes QoS-Level.

HFSC

Hierachical Fair Service Curve Packet Scheduler (HFSC) wird in http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html beschrieben.

PRIQ

Priority Queuing (PRIQ) lässt den Verkehr einer Warteschlange mit höherer Priorität zuerst durch.

Weitere Informationen über diese Algorithmen und Beispiele für Regelsätze finden Sie unter http://www.openbsd.org/faq/pf/queueing.html.

29.3.3. PF Regelsätze

Beigetragen von Peter N. M. Hansteen.

Dieser Abschnitt beschreibt die Erstellung von angepassten Regelsätzen. Es wird mit dem einfachsten Regelsatz begonnen auf dem dann weitere aufgebaut werden, um die Konzepte und Funktionen von PF an einigen konkreten Beispielen zu verdeutlichen.

Der einfachste Regelsatz gilt für einen Rechner, der keine Dienste anbietet und Zugriff auf das Internet haben soll. Für diesen minimalen Regelsatz wird /etc/pf.conf wie folgt konfiguriert:

block in all
pass out all keep state

Die erste Regel blockiert jeglichen eingehenden Datenverkehr. Die zweite Regel erlaubt ausgehende Verbindungen von diesem Rechner, während die Zustandsinformationen dieser Verbindungen gespeichert werden. Diese Zustandsinformationen machen es möglich, den Antwortverkehr für diese Verbindungen zu erlauben. Der Regelsatz wird mit dem folgenden Befehl geladen:

# pfctl -e ; pfctl -f /etc/pf.conf

Neben den Zustandsinformationen verfügt PF über Listen und Makros. Diese können bei der Erstellung der Regeln definiert werden. Makros können Listen enthalten und sie müssen vor ihrer ersten Benutzung definiert sein. Fügen Sie beispielsweise folgende Zeilen an den Anfang des Regelsatzes:

tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"

PF versteht sowohl Portnamen als auch Portnummern, solange die Namen in /etc/services aufgeführt sind. Dieses Beispiel erstellt zwei Makros. Das erste ist eine Liste mit sieben TCP-Portnamen, die zweite Liste enthält einen UDP-Portnamen. Sobald ein Makro definiert ist, kann es in den Regeln verwendet werden. In diesem Beispiel wird der gesamte Datenverkehr geblockt, mit Ausnahme der Verbindungen die von diesem Rechner initiiert wurden und sich auf einen der angegebenen TCP-Dienste oder den UDP-Dienst beziehen:

tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"
block all
pass out proto tcp to any port $tcp_services keep state
pass proto udp to any port $udp_services keep state

Obwohl UDP als zustandsloses Protokoll betrachtet wird, ist PF in der Lage einige Zustandsinformationen zu verfolgen. Wenn beispielsweise eine UDP-Abfrage für einen Nameserver das System verlässt, wird PF nach der Antwort Ausschau halten und das Antwortpaket durch lassen.

Nachdem der Regelsatz verändert wurde, muss er neu geladen werden:

# pfctl -f /etc/pf.conf

Wenn keine Syntaxfehler festgestellt werden, wird pfctl keine Ausgabe erzeugen. Die Syntax kann auch getestet werden, bevor der Regelsatz geladen wird:

# pfctl -nf /etc/pf.conf

Die Option -n bewirkt, dass die Regeln nur interpretiert, jedoch nicht geladen werden. Dies bietet die Möglichkeit, alle Fehler zu korrigieren. Es wird immer der letzte gültige Regelsatz geladen, bis PF entweder deaktiviert, oder ein neuer Regelsatz geladen wird.

Tipp:

Wenn Sie beim Laden oder Prüfen des Regelsatzes noch die Option -v hinzufügen, wird pfctl den komplett interpretierten Regelsatz anzeigen. Dies ist äußerst nützlich, wenn Sie versuchen Fehler im Regelsatz zu finden.

29.3.3.1. Einfaches Gateway mit NAT

Dieser Abschnitt zeigt wie ein FreeBSD-System mit PF als Gateway konfiguriert wird. Das Gateway muss über mindestens zwei Netzwerkkarten verfügen, die jeweils mit einem separaten Netzwerk verbunden sind. In diesem Beispiel ist xl1 mit dem Internet verbunden und xl0 ist mit dem internen Netzwerk verbunden.

Aktivieren Sie zunächst das Gateway, damit der Rechner den Netzwerkverkehr von einer Schnittstelle zur nächsten weiterleiten kann. Diese sysctl-Einstellung sorgt dafür, dass IPv4-Pakete weitergeleitet werden:

# sysctl net.inet.ip.forwarding=1

So leiten Sie IPv6-Datenverkehr weiter:

# sysctl net.inet6.ip6.forwarding=1

Um diese Einstellungen beim Systemstart zu aktivieren, fügen Sie folgende Zeilen in /etc/rc.conf ein:

gateway_enable="YES"		#für ipv4
ipv6_gateway_enable="YES"	#für ipv6

Prüfen Sie mit ifconfig, dass beide Schnittstellen vorhanden und aktiv sind.

Als nächstes erstellen Sie die nötigen PF-Regeln, damit das Gateway den Datenverkehr weiterleiten kann. Die folgende Regel erlaubt den zustandsorientierten Verkehr aus dem Internet zu den Rechnern im Netzwerk:

pass in on xl1 from xl1:network to xl0:network port $ports keep state

Diese Regel erlaubt lediglich den Datenverkehr über das Gateway auf der internen Schnittstelle. Damit die Pakete noch weiter gehen, wird eine passende Regel benötigt:

pass out on xl0 from xl1:network to xl0:network port $ports keep state

Obwohl diese beiden Regeln funktionieren, werden sie in der Praxis so spezifisch selten benötigt. Ein lesbarer Regelsatz ist oft ein sicherer Regelsatz. Der Rest dieses Abschnitts zeigt, wie Sie die Regeln so einfach und lesbar wie möglich halten. Zum Beispiel könnten die beiden Regeln zu einer Regel zusammengefasst werden:

pass from xl1:network to any port $ports keep state

Die Notation interface:network kann durch ein Makro ersetzt werden, um den Regelsatz besser lesbar zu machen. Zum Beispiel könnte für das Netzwerk an der internen Schnittstelle (xl0:network) ein Makro namens $localnet definiert werden. Alternativ könnte für die Definition von $localnet auch eine IP-Adresse/Netzmaske Notation verwendet werden, um ein Netzwerk zu bezeichnen, beispielsweise 192.168.100.1/24 für ein privates Subnetz.

Bei Bedarf kann für $localnet auch eine Liste von Netzwerken definiert werden. Abhängig von den Bedürfnissen kann $localnet auch für eine typische Regel wie folgt verwendet werden:

pass from $localnet to any port $ports keep state

Der folgende Regelsatz erlaubt sämtlichen Verkehr, der von den Rechnern im internen Netzwerk initiiert wird. Zunächst werden zwei Makros definiert, die die externen und internen 3COM-Schnittstellen repräsentieren.

Anmerkung:

Bei Einwählverbindungen wird tun0 für die externe Schnittstelle verwendet. Bei ADSL-Verbindungen, insbesondere denen die PPP over Ethernet (PPPoE) verwenden, ist die richtige externe Schnittstelle tun0 und nicht die physische Ethernet-Schnittstelle.

ext_if = "xl0"	# macro for external interface - use tun0 for PPPoE
int_if = "xl1"	# macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -> ($ext_if)
block all
pass from { lo0, $localnet } to any keep state

Dieser Regelsatz führt die NAT-Regel ein, die verwendet wird, um die Übersetzung der Netzwerkadressen von den nicht-routebaren Adressen im internen Netzwerk auf die IP-Adresse der externen Schnittstelle zu handhaben. Die Klammern im letzten Teil der NAT-Regel ($ext_if) werden angegeben, wenn die IP-Adresse der externen Schnittstelle dynamisch zugewiesen wird. Damit wird sichergestellt, dass der Netzwerkverkehr ohne schwerwiegende Unterbrechungen weiterläuft, auch wenn sich die externe IP-Adresse ändert.

Beachten Sie, dass dieser Regelsatz wahrscheinlich mehr Verkehr aus dem Netzwerk zulässt, als eigentlich nötig ist. Bei einem angemessenen Aufbau könnte folgendes Makro erstellt werden:

client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \
    https, cvspserver, 2628, 5999, 8000, 8080 }"

Dieses Makro wird dann in der Filterregel benutzt:

pass inet proto tcp from $localnet to any port $client_out \
    flags S/SA keep state

Weitere pass Regeln werden vielleicht noch benötigt. Diese Regel aktiviert SSH auf der externen Schnittstelle:

pass in inet proto tcp to $ext_if port ssh

Dieses Makrodefinition und Regel erlaubt DNS und NTP für interne Clients:

udp_services = "{ domain, ntp }"
pass quick inet proto { tcp, udp } to any port $udp_services keep state

Beachten Sie das Schlüsselwort quick in dieser Regel. Da der Regelsatz aus mehreren Regeln besteht, ist es wichtig, die Beziehungen zwischen den einzelnen Regeln zu verstehen. Die Regeln werden von oben nach unten ausgewertet, in der Reihenfolge wie sie geschrieben sind. Für jedes Paket oder jede Verbindung, das PF ausgewertet, wird die letzte übereinstimmende Regel im Regelsatz angewendet. Wenn jedoch ein Paket auf eine Regel passt, welche das Schlüsselwort quick enthält, wird das Paket entsprechend dieser Regel behandelt und die Regelverarbeitung wird gestoppt. Diese Vorgehensweise ist sehr nützlich, wenn eine Ausnahme von den allgemeinen Regeln erforderlich ist.

29.3.3.2. Einen FTP-Proxy einrichten

Die Konfiguration einer funktionierenden Regel für FTP kann aufgrund der Beschaffenheit des FTP-Protokolls problematisch sein. FTP ist sehr viel älter als Firewalls und schon vom Design her unsicher. Die häufigsten Argumente gegen eine Verwendung von FTP sind:

  • Passwörter werden im Klartext übertragen.

  • Das Protokoll erfordert die Verwendung von mindestens zwei TCP-Verbindungen (Steuerung und Daten) auf separaten Ports.

  • Wenn eine Sitzung aufgebaut wird, werden die Daten auf zufällig ausgewählten Ports übermittelt.

All diese Punkte stellen Herausforderungen dar, noch bevor die Client- oder Server-Software auf potenzielle Sicherheitslücken überprüft wurde. Es existieren aber auch sichere Alternativen für die Dateiübertragung, wie sftp(1) oder scp(1), wo die Authentifizierung und die Datenübertragung über eine verschlüsselte Verbindung erfolgt.

Für Situationen, in denen FTP erforderlich ist, kann PF den FTP-Datenverkehr an ein kleines Proxy-Programm namens ftp-proxy(8) weiterleiten. Dieses Programm ist im Basissystem von FreeBSD enthalten. Die Aufgabe des Proxies ist das dynamische Einfügen und Entfernen von Regeln im Regelsatz. Dies wird durch den Einsatz von Ankern erreicht, damit der FTP-Verkehr korrekt verarbeitet werden kann.

Fügen Sie folgende Zeilen in /etc/rc.conf ein, um den Proxy zu aktivieren:

ftpproxy_enable="YES"

Danach kann der Proxy mit service ftp-proxy start gestartet werden.

Für die Grundkonfiguration müssen drei weitere Einträge in /etc/pf.conf hinzugefügt werden. Zunächst werden die Anker hinzugefügt, die der Proxy für die FTP-Sitzungen verwendet:

nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"

Dann wird eine pass-Regel benötigt, damit der FTP-Datenverkehr durch den Proxy geleitet werden kann.

Die Regeln für Umleitung und NAT müssen vor den eigentlichen Filterregeln definiert werden. Fügen Sie diese rdr-Regel unmittelbar nach der NAT-Regel ein:

rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021

Zum Schluss muss der umgeleitete Verkehr die Firewall passieren dürfen:

pass out proto tcp from $proxy to any port ftp

$poxy enthält die Adresse, an dem der Proxy-Daemon gebunden ist.

Speichern Sie /etc/pf.conf und laden Sie die Regeln neu. Prüfen Sie von einem Client, ob die FTP-Verbindungen funktionieren:

# pfctl -f /etc/pf.conf

Dieses Beispiel umfasst eine Grundkonfiguration, in der die Rechner im lokalen Netzwerk Zugriff auf entfernte FTP-Server benötigen. Diese Konfiguration sollte mit den meisten FTP-Clients und -Servern gut funktionieren. Das Verhalten von ftp-proxy(8) kann durch diverse Optionen in ftpproxy_flags beeinflusst werden. Einige Clients und Server haben bestimmte Marotten, die bei der Konfiguration berücksichtigt werden müssen. Es kann zum Beispiel notwendig sein, den FTP-Datenverkehr für den Proxy einer bestimmten Warteschlange zuzuweisen.

Es besteht auch die Möglichkeit einen FTP-Server mit PF und ftp-proxy(8) zu schützen. Konfigurieren Sie einen separaten ftp-proxy mit -R für den Reverse-Modus auf einem separaten Port und einer eigenen Umleitungsregel.

29.3.3.3. ICMP verwalten

Viele Werkzeuge zur Fehlerbehebung in TCP/IP-Netzwerken verlassen sich auf das Internet Control Message Protocol (ICMP), das speziell für diese Zwecke entwickelt wurde.

Das ICMP-Protokoll sendet und empfängt Kontrollnachrichten zwischen Rechnern und Gateways, hauptsächlich um ungewöhnliche Bedingungen auf dem Weg zum Zielrechner zu berichten. Router verwenden ICMP um Paketgrößen und andere Übertragungsparameter zu ermitteln. Dieser Prozess ist auch als Path MTU Discovery bekannt.

Aus der Sicht einer Firewall sind einige ICMP-Kontrollnachrichten anfällig für bekannte Angriffsmethoden. Zwar ist die Fehlerbehebung einfacher, wenn alle ICMP-Pakete bedingungslos durch gelassen werden, aber dass macht es auch für Angreifer leichter, Informationen über das Netzwerk zu extrahieren. Aus diesen Gründen ist die folgende Regel nicht optimal:

pass inet proto icmp from any to any

Eine Lösung besteht darin, nur den ICMP-Verkehr aus dem lokalen Netz zu akzeptieren, während ICMP-Pakete von außerhalb des Netzwerks verworfen werden:

pass inet proto icmp from $localnet to any keep state
pass inet proto icmp from any to $ext_if keep state

Es stehen noch weitere Optionen zur Verfügung, die die Flexibilität von PF demonstrieren. Anstatt beispielsweise alle ICMP-Nachrichten zu erlauben, kann man die Nachrichten angeben, die von ping(8) und traceroute(8) verwendet werden. Beginnen Sie damit, ein Makro für diese Art von Nachrichten zu definieren:

icmp_types = "echoreq"

Erstellen Sie dann eine Regel, die das eben erstellte Makro benutzt:

pass inet proto icmp all icmp-type $icmp_types keep state

Wenn weitere Arten von ICMP-Nachrichten benötigt werden, kann die Liste icmp_types einfach erweitert werden. Geben Sie more /usr/src/contrib/pf/pfctl/pfctl_parser.c ein, um eine Liste der von PF unterstützten ICMP-Nachrichten zu sehen. Die Webseite http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml enthält eine Erklärung für jeden Nachrichtentyp.

Da UNIX® traceroute in der Voreinstellung UDP verwendet, wird eine weitere Regel benötigt:

# allow out the default range for traceroute(8):
pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state

Da TRACERT.EXE unter Microsoft® Windows®-Systemen ICMP Echo Request Meldungen verwendet, ist nur die erste Regel notwendig um Traces für solche Systeme zu ermöglichen. UNIX® traceroute kann aber auch andere Protokolle verwenden, zum Beispiel ICMP Echo Request, wenn der Schalter -I benutzt wird. Details finden Sie in traceroute(8).

29.3.3.3.1. Path MTU Discovery

Internet-Protokolle sind so ausgelegt, dass sie geräteunabhängig sind. Eine Folge davon ist, dass die optimale Paketgröße nicht immer zuverlässig vorhergesagt werden kann. Das größte Hindernis ist hier die Maximum Transmission Unit (MTU), welche die Obergrenze für die Paketgröße festlegt. Die MTU für die Schnittstelle des Systems können Sie sich mit ifconfig anzeigen lassen.

TCP/IP benutzt ein Verfahren, das als path MTU discovery bekannt ist, um die korrekte Paketgröße für eine Verbindung zu bestimmen. Dieses Verfahren sendet Pakete unterschiedlicher Größe mit dem Flag do not fragment und erwartet ein ICMP-Antwortpaket vom Typ type 3, code 4, wenn die Obergrenze erreicht worden ist. Typ 3 bedeutet Ziel nicht erreichbar und Code 4 ist die Abkürzung für Fragmentierung nötig, aber Do-not-Fragment Flag ist gesetzt. Um path MTU discovery zu erlauben und damit Verbindungen zu anderen MTUs zu unterstützen, fügen Sie dem Makro icmp_types den Typ destination unreachable hinzu:

icmp_types = "{ echoreq, unreach }"

Da die pass-Regel bereits das Makro verwendet, braucht es nicht geändert werden um den neuen ICMP-Typ zu unterstützen:

pass inet proto icmp all icmp-type $icmp_types keep state

PF kann alle Variationen von ICMP-Typen und Codes filtern. Eine Liste der verfügbaren Typen und Codes ist in icmp(4) und icmp6(4) dokumentiert.

29.3.3.4. Tabellen benutzen

Manchmal sind bestimmte Daten für die Filterung und Weiterleitung interessant, jedoch wäre eine Definition einer solchen Filterregel für einen Regelsatz viel zu lang. PF unterstützt die Verwendung von Tabellen. Dies sind definierte Listen, die verändert werden können, ohne den gesamten Regelsatz neu laden zu müssen. Zudem können diese Listen sehr schnell durchsucht werden. Tabellennamen sind immer in < > eingeschlossen und sehen wie folgt aus:

table <clients> { 192.168.2.0/24, !192.168.2.5 }

In diesem Beispiel ist das Netzwerk 192.168.2.0/24 Teil der Tabelle. 192.168.2.5 wurde im dem Operator ! ausgeschlossen und ist somit nicht Teil der Tabelle. Es ist auch möglich Tabellen aus Dateien zu laden, wo jeder Eintrag in einer separaten Zeile steht. Dieses Beispiel verwendet dazu die Datei /etc/clients:

192.168.2.0/24
!192.168.2.5

Um sich auf diese Datei zu beziehen, definieren Sie die Tabelle wie folgt:

table <clients> persist file "/etc/clients"

Sobald die Tabelle definiert ist, kann eine Filterregel Bezug darauf nehmen:

pass inet proto tcp from <clients> to any port $client_out flags S/SA keep state

Die Inhalte einer Tabelle können mit pfctl direkt verändert werden. Dieses Beispiel fügt ein weiteres Netzwerk zur Tabelle hinzu:

# pfctl -t clients -T add 192.168.1.0/16

Beachten Sie, dass auf diese Weise vorgenommene Änderungen direkt übernommen werden, jedoch bei einem Neustart des Systems oder bei einem Stromausfall verloren gehen. Um die Änderungen dauerhaft zu speichern, müssen sie in der Definition der Tabelle oder in der Datei, auf die sich die Tabelle bezieht, bearbeitet werden. Mit einem cron(8) Job und einem Befehl wie pfctl -t clients -T show >/etc/clients können Sie auch eine Kopie der Tabelle auf Platte speichern und dann in regelmäßigen Abständen aktualisieren. Alternativ kann /etc/clients auch mit den Tabelleneinträgen, die sich aktuell im Speicher befinden, aktualisiert werden.

# pfctl -t clients -T replace -f /etc/clients

29.3.3.5. Verwendung von Tabellen zum Schutz von SSH

Benutzer, die SSH auf einer externen Schnittstelle ausführen, haben wahrscheinlich schon einmal ähnliche Meldungen in den Protokolldateien gesehen:

Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user admin from 200.72.41.31 port 41484 ssh2

Diese Meldungen deuten auf einen Brute-Force-Angriff hin, bei dem ein Angreifer oder ein Programm versucht, den Benutzernamen und das Passwort zu erraten, um Zugriff auf das System zu bekommen.

Wenn der Zugriff über SSH für berechtigte Benutzer erforderlich ist, kann eine Änderung des Standard-Ports für SSH einen gewissen Schutz bieten. Allerdings bietet PF eine elegantere Lösung für dieses Problem. pass-Regeln können Einschränkungen für Dinge enthalten, die ein verbindender Rechner tun kann. Bei einem Verstoß gegen diese Einschränkungen kann dann dem betroffenen Rechner der Zugriff teilweise oder ganz entzogen werden. Es ist sogar möglich, alle bestehenden Verbindungen zu trennen, falls die Grenze überschritten wird.

Um dies zu konfigurieren, erstellen Sie folgende Tabelle im Regelsatz:

table <bruteforce> persist

Fügen Sie dann ziemlich am Anfang der Filterregeln folgende Regeln hinzu, um die Brute-Force-Angriffe zu blocken und gleichzeitig berechtigte Verbindungen zu erlauben:

block quick from <bruteforce>
pass inet proto tcp from any to $localnet port $tcp_services \
    flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, \
    overload <bruteforce> flush global)

Der Teil in Klammern definiert die Grenzwerte. Die Zahlen sollten an die lokalen Anforderungen angepasst werden. Die Zeilen können wie folgt interpretiert werden:

max-src-conn definiert die maximal erlaubte Anzahl gleichzeitiger Verbindungen von einem Rechner.

max-src-conn-rate definiert die maximal erlaubte Anzahl neuer Verbindungen eines einzelnen Rechners (15) pro Anzahl von Sekunden (5).

overload <bruteforce> bedeutet, dass jeder Rechner, der diesen Grenzwert überschreitet, zur Tabelle bruteforce hinzugefügt wird. Diese Filterregel blockiert jeglichen Datenverkehr von Adressen aus der Tabelle bruteforce.

flush global besagt, dass alle (global) Verbindungen dieses Rechners getrennt (flush) werden, wenn der Grenzwert erreicht wird.

Anmerkung:

Diese Filterregeln helfen nicht bei langsamen Brute-Force-Angriffen, wie sie in http://home.nuug.no/~peter/hailmary2013/ beschrieben sind.

Dieser Beispielregelsatz dient lediglich als Illustration. Wenn Sie allgemein eine große Anzahl an Verbindungen erlauben wollen, aber gleichzeitig bei SSH etwas restriktiver vorgehen möchten, können Sie die obige Regel ergänzen:

pass quick proto { tcp, udp } from any to any port ssh \
    flags S/SA keep state \
    (max-src-conn 15, max-src-conn-rate 5/3, \
    overload <bruteforce> flush global)

Es ist möglicherweise nicht notwendig, alle aggressiven Rechner zu blockieren:

Es ist zu erwähnen, dass der overlaod-Mechanismus eine allgemeine Technik darstellt, die nicht auf SSH beschränkt ist. Außerdem ist es nicht immer optimal, Datenverkehr von aggressiven Rechnern zu blockieren.

Eine overload-Regel kann beispielsweise benutzt werden, um einen Mail- oder Webserver zu schützen. Die overload-Tabelle könnte dann in einer Regel verwendet werden, um aggressive Rechner einer Warteschlange mit geringerer Bandbreite zuzuweisen, oder den Rechner auf eine bestimtme Webseite umzuleiten.

Im Laufe der Zeit werden die Tabellen durch die overload-Regeln immer größer und belegen immer mehr Speicher. Manchmal wird eine geblockte IP-Adresse einem Rechner dynamisch zugewiesen, der eigentlich berechtigt ist, mit den Rechnern im lokalen Netzwerk zu kommunizieren.

Für solche Situationen bietet pfctl die Möglichkeit, Tabelleneinträge auslaufen zu lassen. Dieses Kommando würde beispielsweise Einträge aus der Tabelle <bruteforce> löschen, die seit 86400 Sekunden nicht mehr referenziert wurden:

# pfctl -t bruteforce -T expire 86400

Eine ähnliche Funktionalität bietet security/expiretable, welches Einträge entfernt, die für einen bestimmten Zeitraum nicht referenziert wurden.

Nach der Installation kann expiretable benutzt werden, um Einträge aus der Tabelle <bruteforce> nach einer bestimmten Zeit zu entfernen. Dieses Beispiel entfernt alle Einträge, die älter sind als 24 Stunden:

/usr/local/sbin/expiretable -v -d -t 24h bruteforce

29.3.3.6. Schutzt vor SPAM

Im Gegensatz zum spamd-Daemon von spamassassin, kann mail/spamd zusammen mit PF den SPAM direkt an der Firewall abwehren. Dieser spamd wird in PF über einen Satz von Umleitungen konfiguriert.

Spammer neigen dazu, eine große Anzahl von Nachrichten zu versenden. Dabei nutzten Sie SPAM-freundliche Netzwerke und gekaperte Rechner, welche dann ziemlich schnell bei sogenannten Blacklists gemeldet werden.

Wenn eine SMTP-Verbindung von einer Adresse in der Blacklist empfangen wird, präsentiert spamd einen Banner und schaltet sofort in einen Modus, in dem die Antworten auf den SMTP-Verkehr jeweils ein Byte groß sind. Diese Technik, die möglichst viel Zeit des Spammers verschwenden soll, wird Tarpitting genannt. Die spezifische Implementierung, welche ein Byte SMTP-Antworten verwendet, wird als Stuttering bezeichnet.

Dieses Beispiel zeigt das grundlegende Verfahren zur Konfiguration von spamd mit automatisch aktualisierten Blacklists. Für weitere Informationen lesen die Manualpages, die zusammen mit mail/spamd installiert werden.

Prozedur 29.1. Konfiguration von spamd
  1. Installieren Sie das Paket oder den Port mail/spamd. Um spamd's Greylisting-Funktion zu nutzen, muss fdescfs(5) in /dev/fd eingehängt werden. Fügen Sie folgende Zeile in /etc/fstab ein:

    fdescfs /dev/fd fdescfs rw 0 0

    Danach hängen Sie das Dateisystem ein:

    # mount fdescfs
  2. Fügen Sie folgende Zeilen in den PF-Regelsatz ein:

    table <spamd> persist
    table <spamd-white> persist
    rdr pass on $ext_if inet proto tcp from <spamd> to \
        { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025
    rdr pass on $ext_if inet proto tcp from !<spamd-white> to \
        { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025

    Die beiden Tabellen <spamd> und <spam-white> sind von großer Bedeutung. SMTP-Verkehr von einer Adresse, die in <spamd> aber nicht in <spamd-white> ist, wird an den spamd-Daemon auf Port 8025 umgeleitet.

  3. Im nächsten Schritt wird spamd in /usr/local/etc/spamd.conf konfiguriert und einige Parameter werden in /etc/rc.conf hinzugefügt.

    Die Installation von mail/spamd enthält eine Beispielkonfiguration (/usr/local/etc/spamd.conf.sample) und eine Manualpage für spamd.conf. Beziehen Sie sich für zusätzliche Konfigurationsoptionen auf diese Dokumentation.

    Die Konfigurationsdatei enthält einen Block, in dem die all-Liste definiert ist, die wiederum weitere Listen spezifiziert:

    all:\
        :traplist:whitelist:

    Dieser Eintrag fügt die gewünschten Blacklists, getrennt durch einen Doppelpunkt (:), hinzu. Um auch eine Whitelist zu verwenden, fügen Sie den Namen unmittelbar hinter dem Namen der Blacklist ein. Zum Beispiel: :Blacklist:Whitelist:.

    Danach folgt die Definition der verwendeten Blacklist:

    traplist:\
        :black:\
        :msg="SPAM. Your address %A has sent spam within the last 24 hours":\
        :method=http:\
        :file=www.openbsd.org/spamd/traplist.gz

    In der ersten Zeile steht der Name der Blacklist und die zweite Zeile gibt den Typ an. Das Feld msg enthält die Nachricht, die dem Absender während des SMTP-Dialogs angezeigt wird. Das Feld mehtod legt fest, wie spamd-setup die Listen bezieht; unterstützte Methoden sind http, ftp, file und ein externes Programm via exec. Im letzten Feld gibt file den Namen der Datei an, die spamd erwartet.

    Die Definition der Whitelist ist ähnlich. Das Feld msg wird jedoch nicht definiert, da eine Meldung hier nicht erforderlich ist:

    whitelist:\
        :white:\
        :method=file:\
        :file=/var/mail/whitelist.txt

    Wählen Sie die Datenquellen mit Sorgfalt::

    Bei der Verwendung von sämtlichen Blacklists aus der Beispieldatei spamd.conf würden große Teile des Internets geblockt. Der Administrator muss diese Datei bearbeiten, um eine optimale Konfiguration zu erzielen. Dazu gehört auch die Auswahl von geeigneten Blacklists und, wenn nötig, die Erstellung von benutzerdefinierten Listen.

    Als nächstes fügen Sie folgenden Eintrag in /etc/rc.conf hinzu. Zusätzliche Optionen sind in der Manualpage beschrieben:

    spamd_flags="-v" # use "" and see spamd-setup(8) for flags

    Wenn Sie fertig sind, starten Sie spamd durch die Eingabe von service obspamd start. Führen Sie die weitere Konfiguration mit spamd-setup durch. Erstellen Sie zum Schluss einen cron(8)-Job, der spamd-setup in regelmäßigen Abständen aufruft, um die Listen zu aktualisieren.

Auf einem typischen Gateway vor dem Mailserver, werden Rechner innerhalb von wenigen Minuten geblockt.

PF unterstützt auch Greylisting, das Nachrichten von unbekannten Rechnern vorübergehend mit 45n-Codes ablehnt. Nachrichten von diesen Rechnern werden bei einem erneuten Versuch nach einer angemessenen Zeit durchgelassen. Nachrichten von Rechnern, die nach RFC 1123 und RFC 2821 konfiguriert sind, werden sofort durchgelassen.

Weitere Informationen über Greylisting finden Sie unter greylisting.org. Das Erstaunlichste an Greylisting ist, neben der einfachen Benutzung, dass es immer noch funktioniert. Spammer und Malware-Autoren gelingt es bislang nur schwer, diese Technik zu umgehen.

Die grundsätzliche Vorgehensweise zur Konfiguration von Greylisting ist wie folgt:

Prozedur 29.2. Konfiguration von Greylisting
  1. Stellen Sie sicher, dass fdescfs(5) eingehängt ist. Dies wird in Schritt 1 der vorherigen Prozedur beschrieben.

  2. Um spamd im Greylisting-Modus auszuführen, fügen Sie folgende Zeilen in /etc/rc.conf ein:

    spamd_grey="YES"  # use spamd greylisting if YES

    Lesen Sie die Manualpage von spamd für Beschreibungen von zusätzlichen Parametern.

  3. Starten Sie die Dienste, um die Konfiguration von Greylisting abzuschließen:

    # service obspamd restart
    # service spamlogd start

Hinter den Kulissen führen die spamdb-Datenbank und spamlogd wesentliche Aufgaben der Greylisting-Funktion aus. spamdb ist die Schnittstelle für den Administrator, der über den Inhalt der Datenbank /var/db/spamdb Blaklists, Whitelists und Greylists verwaltet.

29.3.3.7. Netzwerk-Hygiene

Dieser Abschnitt beschreibt die Verwendung von block-policy, scrub und antispoof, mit denen das Verhalten des Regelsatzes weiter optimiert werden kann.

Die Option block-policy kann im Teil options des Regelwerks konfiguriert werden, vor den Umleitungen und den eigentlichen Filterregeln. Diese Option legt fest, welche Rückmeldung PF an einen geblockten Rechner sendet. Es existieren zwei mögliche Werte: drop verwirft das Paket ohne Rückmeldung und return gibt eine Statusmeldung, wie etwa Connection refused zurück.

Die Voreinstellung ist drop. Geben Sie den gewünschten Wert ein, um die block-policy-Richtlinie zu ändern:

set block-policy return

scrub ist ein Schlüsselwort in PF, das die Paket-Normalisierung aktiviert. Dieser Prozess fügt fragmentierte Pakete wieder zusammen und blockt TCP-Pakete mit ungültigen Flag-Kombinationen. Ein aktiviertes scrub bietet einen gewissen Schutz gegen Angriffe, die auf die falsche Handhabung von fragmentierten Paketen aufbauen. Es stehen viele Optionen zur Verfügung, jedoch sollte die einfachste Form für die meisten Konfigurationen ausreichend sein:

scrub in all

Einige Dienste, wie beispielsweise NFS, erfordern eine bestimmte Handhabung von fragmentierten Paketen. Weitere Informationen finden Sie unter http://www.openbsd.org/faq/pf/scrub.html.

Dieses Beispiel fügt fragmentierte Pakete wieder zusammen, löscht das do not fragment-Bit und setzt die maximale Segmentgröße auf 1440 Bytes:

scrub in all fragment reassemble no-df max-mss 1440

Der antispoof-Mechanismus bietet einen Schutz gegen gefälschte IP-Adressen. Dabei werden hauptsächlich Pakete verworfen, die auf der falschen Schnittstellen ankommen.

Folgende Regeln verwerfen gefälschte Adressen, wenn sie aus dem Internet oder dem lokalen Netzwerk stammen:

antispoof for $ext_if
antispoof for $int_if

29.3.3.8. Handhabung von nicht-routebaren Adressen

Sogar bei einem richtig konfigurierten NAT-Gateway müssen Sie vielleicht die Fehlkonfiguration anderer Personen ausgleichen. Ein typischer Fehler besteht darin, nicht-routebare Adressen ins Internet zu lassen. Da der Verkehr von nicht-routebaren Adressen Teil eines DoS-Angriffs sein kann, sollten Sie in Betracht ziehen, diesen Verkehr explizit an der externen Schnittstelle des Netzwerks zu blockieren.

In diesem Beispiel wird ein Makro erstellt, das die nicht-routebaren Adressen enthält. Datenverkehr von und zu diesen Adressen wird dann an der externen Schnittstelle des Gateways verworfen.

martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
	      10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
	      0.0.0.0/8, 240.0.0.0/4 }"

block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians

Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an <de-bsd-questions@de.FreeBSD.org>.

Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an <de-bsd-translators@de.FreeBSD.org>.