Das Firewall-Tool pf in Mac OS X 10.7 Lion

Mac OS X 10.7 kommt mit gleich drei Firewall-Systemen, die alle IPv6-fähig sind: die Anwendungs-Firewall, das Firewall-Utility ip6fw und das neue Tool "pf". Über das sehr mächtige Werkzeug "pf" könnte man ganze Bücher schreiben. Aber die wesentlichen Merkmale lassen sich natürlich kompakt zusammenfassen.

In Pocket speichern vorlesen Druckansicht 3 Kommentare lesen
Lesezeit: 5 Min.
Von
  • Carsten Strotmann

Ein Wort zur Vorsicht: Bei unseren Versuchen haben wir festgestellt, das Apples Implementierung auf Mac OS X 10.7.1 die Stabilität des Betriebssystems beeinträchtigen kann. Von Kernel-Abstürzen über "hängende" Anwendungen bis zu "eingefrorenen" Systemen haben wir alles erlebt. Derzeit hat "pf" in Mac OS X den Status "produktionsreif" nicht erreicht.

Die Haupt-Konfigurationsdatei "/etc/pf.conf" wird beim Start des Betriebssystems ausgelesen und die dort definierten Regeln werden in die "pf"-Firewall eingetragen. Diesen Vorgang löst der LaunchDaemon com.apple.pfctl.plist aus, den man in /System/Library/LaunchDaemons/ findet. Allerdings wird damit die "pf"-Firewall nicht aktiviert. Das müssen Anwendungen oder ein eigener LaunchDaemon übernehmen.

Die Firewall lässt sich mit dem Kommandozeilen-Programm pfctl steuern. Der Befehl pfctl -E aktiviert die Firewall mit dem aktuell geladenen Regelsatz. pfctl -X <referenztoken> deaktiviert die Firewall, oder besser gesagt, verringert einen Referenzzähler um eins. Dieser Referenzzähler wird bei jedem Aufruf von pfctl -E erhöht, und mittels pfctl -X <referenztoken> verringert. Ist der Referenzzähler bei Null angekommen, wird die Firewall ausgeschaltet. Auf diese Weise können mehrere Anwendungen die Firewall kontrollieren, aber erst wenn die letze ihren Bedarf abmeldet, wird die pf-Firewall auch tatsächlich abgeschaltet. pfctl -X benötigt ein Referenztoken. Dieses Referenztoken wird beim Einschalten der Firewall mittels pfctl -E erzeugt und kann mit pfctl -s References ausgelesen werden.

# sudo pfctl -s References
No ALTQ support in kernel
ALTQ related functions disabled
TOKENS:
PID Process Name TOKEN TIMESTAMP
671 pfctl 18446743524369253512 0 days 00:00:02
# sudo pfctl -X 18446743524369253512
No ALTQ support in kernel
ALTQ related functions disabled
pf disabled

pfctl -s info gibt den aktuellen Statusbericht zur Firewall, inklusive der Angabe, ob die Firewall aktiviert ist:

#  sudo pfctl -s info
No ALTQ support in kernel
ALTQ related functions disabled
Status: Disabled for 0 days 00:02:44 Debug: Urgent
[...]

Beim Logging beschreitet "pf" einen ungewöhnlichen Weg: Die Logausgaben werden an eine virtuelle Netzwerkschnitstelle gesendet. Von dort lassen sich die Daten mit einem beliebigen Programm auslesen, welches das Logging-Paket-Format versteht. Das geht beispielsweise mit tcpdump oder Wireshark.

Um das Logging einzuschalten, definiert man am Anfang der Konfigurationsdatei den Namen der virtuellen Netzwerkschnittstelle:

set loginterface pflog0

Diese Netzwerkschnittstelle erzeugt man per ifconfig – und zwar nach jedem Neustart. Dann kann man sie mittels tcpdump abfragen:

# sudo ifconfig pflog0 create
# sudo tcpdump tcpdump -n -e -ttt -i pflog0

Die Syntax für die Firewall-Regeln erscheint ähnlich gut lesbar wie die des ip6fw-Utilities. Zum besseren Vergleich haben wir die ip6fw-Regeln, die im Artikel "Vorsorgliches Umtopfen" aufgeführt sind, in das "pf"-Format übersetzt. Das IPv6-Regelwerk wird per "include" in die Hauptdatei eingebunden:

Datei "/etc/pf.conf":

--------
#
set loginterface pflog0
scrub in all fragment reassemble
#
# com.apple anchor point
#
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
include "/etc/pf-ipv6rules.conf"
--------

Datei "/etc/pf-ipv6rules.conf":

--------
#
# Macros
#
lan_if="en0"
wifi_if="en1"
ip6linklocal="fe80::/10"
ip6linklocalmc="ff02::/16"
# no rules on loopback and interface traffic
set skip on lo0
# Duplicate Address detection (DAD)
pass in inet6 proto ipv6-icmp from any to $ip6linklocalmc 
# Neighbor discovery solicitation out
pass out on {$lan_if, $wifi_if} inet6 proto ipv6-icmp all icmp6-type {neighbradv, neighbrsol}
# Neighbor discovery advertisement in
pass in on {$lan_if, $wifi_if} inet6 proto ipv6-icmp all icmp6-type {neighbradv, neighbrsol}
# ICMPv6 Pakete aus dem Netzwerk kommend erlauben
pass in on {$lan_if, $wifi_if} inet6 proto ipv6-icmp all icmp6-type {unreach, toobig, timex, paramprob, echoreq, echorep}
# Router solicitation kommend
pass in on {$lan_if, $wifi_if} inet6 proto ipv6-icmp all icmp6-type routersol
# NTP multicast
pass in on {$lan_if, $wifi_if} inet6 proto udp from any to ff02::1010 port 123 keep state
# mDNS (Rendezvous/Bonjour)
pass in on {$lan_if, $wifi_if} inet6 proto udp from any to ff02::fb port 5353 keep state
pass out on {$lan_if, $wifi_if} inet6 proto udp from any to ff02::fb port 5353 keep state
# DHCPv6
pass out on {$lan_if, $wifi_if} inet6 proto udp from any to ff02::1:2 port 547 keep state
pass in on {$lan_if, $wifi_if} inet6 proto udp from any to any port 546 keep state
# ausgehende IPv6 Verbindungen (UDP/TCP)
pass out on {$lan_if, $wifi_if} inet6 proto tcp all keep state
pass out on {$lan_if, $wifi_if} inet6 proto udp all keep state
#
# alles was nicht erlaubt wurde blocken und in das log schreiben
#
block return log on {$lan_if, $wifi_if} inet6 all
--------

(dz)