In diesem Artikel geht es um den "IP Packet Filter" von Mac OS X. Ursprünglich für FreeBSD entwickelt, ist ipfw seit erscheinen von OS X verantwortlich für das Filtern von IP Paketen auf dem selbigen. Die Firewall stellt in aller erster Linie eine logische und kontrollierte Schnittstelle zwischen zwei oder mehreren Netzwerken dar. Ihre Aufgabe ist es, auf Basis eines definerten Regelwerkes, den Netzwerkverkehr zu überwachen, kontrollieren und zu delegieren. Sie dient damit in erster Linie dem Schutz vor Angriffen, nicht jedoch der Erkennung und Abwehr solcher. Hierfür sind sogenannte Intrusion Detection und Prevention Systeme verantwortlich.
Es gibt drei verschiedene Arten von Firewall-Systemen:
- die Bridging-Firewall
- die Routing-Firewall
- und die Proxy-Firewall
Die genauen Unterschiede zwischen den verschiedenen Arten sollen nicht Thema dieses Artikels sein, doch soviel sei gesagt:
Die Bridging-Firewall wird innerhalb eines Netzwerkes transparent realisiert (als zusätzliche Funktion einer Bridge oder einem Switch zum Beispiel) und ist damit weitestgehend unsichtbar für die Arbeitsstationen oder Server. Die Routing-Firewall (ipfw und Linux' iptables sind solche) wird meist als Erweiterung der Transport-Schicht betrachtet und ergänzt den Router um die oben genannten Funktionalitäten. Die Proxy-Firewall fungiert als Stellvertreter, nimmt Anfragen aus dem Netz entgegen und leitet diese selbständig an das Ziel weiter, womit dann nur diese für das angefragte Ziel in Erscheinung tritt.
Weiterhin wird zwischen drei verschiedenen Funktionsweisen einer Firewall unterschieden:
- dem einfachen, aber doch effektiven Paketfilter
- einer Stateful-Inspection-Firewall
- und einer Application-Layer-Firewall
Auch hierzu nur soviel: ein Paketfilter betrachtet und bewertet Pakete an Hand Ihrer Internet-Ports und entscheided dann, ob der Netzwerkverkehr zum Ziel oder von der Quelle erlaubt oder eben untersagt ist. Die Stateful-Inspection-Funktionen einer Firewall stellt nun eine Erweiterung dieses Paketfilters dar, in dem es Buch über die ein- und ausgehenden Verbindungen führt und auf diese Weise zusammenhängenden Netzwerkverkehr wesentlich gezielter bewerten und steuern kann. Darüber hinaus ist eine solche Firewall in der Lage Netzwerkverkehr, durch Umschreiben von Teilen des IP-Headers, umzuleiten. Die Application-Layer-Firewall wiederrum, erlaubt den Aufbau sogenannter 'dedicated-Proxies' über welche sich zum Beispiel dienstbezogene Inhalte im Detail filtern lassen (ein etwas ungenaues, doch erklärendes Beispiel hier für ist zum Beispiel das vielen sicherlich bekannte Programm LittleSnitch aber auch die in Mac OS X eingebaute Firewall unter Sicherheit).
ipfw in der Theorie
Leider lässt sich die Firewall von OS X eher eingeschränkt mit Apple's grafischen Hausmitteln nutzen und konfigurieren. Mir sind einzig die Möglichkeiten aus den 'Systemenstellungen -> Sicherheit -> Firewall (eine Application-Level Firewall) und Systemeinstellung -> Sharing' bekannt, welche zusammen eine Mischung zwischen dem Freischalten von Programmen und bei letzterm, der gleichzeitigen Konfiguration dieser darstellen. Aktiviert man so zum Beispiel das Filesharing, so findet sich anschließend auch eine dazugehörige AFP-Regel in der Sicherheit. Über die eingebaute 'Firewall' kann man den Traffic also noch etwas gezielter auf und für einzelne Programme beschränken. Sicherlich: die Funktionen mögen dem einen reichen, sie ist möglicherweise transparenter und leichter zu konfigurieren, aber bietet einem auch nicht annähernd die Möglichkeiten einer richtigen (und vor allem richtig konfigurierten) ipfw-Firewall auf den Netzwerkverkehr einzugehen und einzuwirken.
Die Entwicklerwelt war aber auch nicht faul und inzwischen gibt es einige wenige Programme die versuchen, diesen Mißstand zu beheben. Ich kenne die hier (kennt Ihr weitere, dann gerne eine PM an uns Mods und wir ergänzen die Liste dann):
- NoobProof (UB, kostenlos)
- SunShield Pro (UB, kostenpflichtig)
- WaterProof (UB, kostenlos)
Ich habe aber ehrlich gesagt bisher noch keines der Programme ausprobiert und gebe desgen ich auch keinerlei Empfehlungen ab - von Erfahrungen einmal ganz zu schweigen und überlasse das dann besser Euch. Ich zeige Euch hier vielmehr den ganz klassischen Weg - den mit der Konsole - weshalb der Rest der Howto's auch eher für diejenigen von Euch gedacht ist, die sich in der Konsole schon irgendwie wohl fühlen.
Wichig ist aber, dass die Verwendung von 'Systemeinstellungen -> Sicherheit -> Firewall' so konfiguriert werden muß, dass immer alle Verbindungen erlaubt sind - was einer Deaktivierung gleichkommt. Ansonsten kann es unter Umständen dazu kommen, dass irgend etwas zur Laufzeit des Systems dafür sorgt, dass die eigenen Regeln mit anderen überschrieben werden. Little Snitch und andere ähnliche Programme können trotz allem, da sie schon auf Kernelebene den Traffic zu fassen bekommen, weiter benutzt werden.
QUOTE
Todo: kann Sharing überhaupt weiter benutzt werden? Nie ausprobiert, weil bisher nie benutzt - testen!
Aus dem Grund, sind ist alles was jetzt kommt, auch nur für jene von Euch geeignet, die sich in der Konsole zumindest etwas wie zu Hause fühlen und sowieso - überhaupt Interesse haben :)
ipfw in der Praxis
Da die per Hand gesetzten ipfw-Regeln keinen Neustart überstehen, schadet es nichts, dass wir uns gleich einen kleinen LaunchDaemon bauen, der bei einem Neustart auch unsere Regeln als systemweit gültige Firewall-Policy lädt. Hierzu brauchen wir eine Property List, die in das Verzeichnis /Library/LaunchDaemons muss - zum Beispiel 'net.pcosx.ipfw_firewall.plist'.
CODE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST
1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ipfw_firewall</string>
<key>ProgramArguments</key>
<array>
<string>/sbin/ipfw_firewall.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
</dict>
</plist>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST
1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ipfw_firewall</string>
<key>ProgramArguments</key>
<array>
<string>/sbin/ipfw_firewall.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
</dict>
</plist>
Label: setzt den Namen unseres Daemons. Hier sollten kurze aber berzeichnende Namen verwendet werden.
ProgramArguments: definiert das auszuführende Programm und, wenn benötigt, dessen Argumente.
RunAtLoad: weist den LaunchDaemon an, den Daemon beim Systemstart zu laden.
UserName und GroupName: definiert die Besitzerverhältnisse, die der LauchDaemon vom Programm erwartet.
Das Ergebnis: künftig lädt der LaunchDaemon bei Neustarts Euren Daemon automatisch und legt die Firewall-Policy mit Eurem Regelwerk an.
Doch erst brauchen wir das auszuführende Shell-Skript das, über den LaunchDaemon konfiguriert, jetzt aufgerufen werden soll:
CODE
#!/bin/sh
# /sbin/ipfw_firewall.sh - osX-Firewall, Stage-1
IPFW=/sbin/ipfw
SYSCTL=/usr/sbin/sysctl
. /etc/rc.common
ConsoleMessage "Configuring Firewall"
# Log-Ausgaben der Firewall in die Datei /var/log/ipfw.log schreiben
#
/usr/libexec/ipfwloggerd
$SYSCTL -w net.inet.ip.fw.verbose=1
$SYSCTL -w net.inet.ip.fw.verbose_limit=20
# Aktiviere 'Blackholes'
#
$SYSCTL -w net.inet.tcp.blackhole=2
$SYSCTL -w net.inet.udp.blackhole=1
# Lösche das eventuell vorhandene Regelwerk (Todo: Achtung Regelsatz 31 wird hierdurch nicht angerührt!)
#
$IPFW -f flush
# Lade das Regelwerk aus der Datei /etc/ipfw.conf
#
$IPFW -q /etc/ipfw.conf
exit 0
# /sbin/ipfw_firewall.sh - osX-Firewall, Stage-1
IPFW=/sbin/ipfw
SYSCTL=/usr/sbin/sysctl
. /etc/rc.common
ConsoleMessage "Configuring Firewall"
# Log-Ausgaben der Firewall in die Datei /var/log/ipfw.log schreiben
#
/usr/libexec/ipfwloggerd
$SYSCTL -w net.inet.ip.fw.verbose=1
$SYSCTL -w net.inet.ip.fw.verbose_limit=20
# Aktiviere 'Blackholes'
#
$SYSCTL -w net.inet.tcp.blackhole=2
$SYSCTL -w net.inet.udp.blackhole=1
# Lösche das eventuell vorhandene Regelwerk (Todo: Achtung Regelsatz 31 wird hierdurch nicht angerührt!)
#
$IPFW -f flush
# Lade das Regelwerk aus der Datei /etc/ipfw.conf
#
$IPFW -q /etc/ipfw.conf
exit 0
Anschließend noch ein
CODE
chown root:wheel /sbin/ipfw_firewall.sh /Library/LaunchDaemons/net.pcosx.ipfw_firewall.plist & chmod 0755 /sbin/ipfw_firewall.sh /Library/LaunchDaemons/net.pcosx.ipfw_firewall.plist
und wir sind fast am Ziel.
Wer sich das Skript einmal etwas genauer angeschaut hat, der stellt fest, dass weiter unten der Befehl 'ipfw -q /etc/ipfw.conf' ausgeführt wird. Hier wird jetzt endlich die Firewall-Tabelle mit unserem künftigen Regelwerk gefüttert.
Also erstellt Ihr Euch jetzt eine weitere Textdatei, per 'nano /etc/ipfw.conf' oder sonstwas, und denkt hier erstmal nur folgendes rein (als Überschriften für die Spalten mag sich das jeder von Euch so vorstellen:
Nummer, Berechtigung (allow, deny, reject), Protokoll (Protokollnummer oder -name aus /etc/services), Quelle (IP, Hostname, (CIDR-)Subnetz/Maske), Ziel (wie Quelle) und Optionen (siehe auch, Manpage zu ipfw)
CODE
65535 deny ip from any to any
Mit der einen Regel würdet Ihr aber eigentlich nur erreichen, dass der Status Quo nochmal hergestellt wird, d.h. Ihr verbietet einfach jeglichen Traffic, aber es soll Euch am Ende auch nur als Beispiel dienen, da diese Regel sowieso schon vorhanden ist und vom Kernel - ohne Euer Zutun - immer automatisch gesetzt wird. Sicherheit ist damit zu Beginn das absolute und oberste Gebot bei einer ipfw-Firewall - eben zumindest solange, bis Ihr jetzt die Finger im Spiel haben werdet :D
Da ipfw-Regeln einer Tabelle nacheinander abgearbeitet werden, muss man beim Aufbau aber darauf achten, dass eine der 'späteren' Regeln, nicht etwa eine vorangehende überschreibt! Weiter: die Regeln eines Satzes werden von 1 bis 65535 hochnummeriert - das heißt aber nicht, dass wir immer stur plus eins zählen müssen. Ihr könnt auch gleich mit der 500 anfangen, bis 510 prima definieren und dann mit 1000 weitermachen. Auf diese Art bekommt Ihr aber etwas Ordnung in das per einfachem Text definierte Regelwerk. In größeren Netzen (aber auch in kleineren) können schnell ein paar hundert Zeilen zusammenkommnen! Aber es gibt natürlich noch ein paar Besonderheiten:
Regeln oberhalb von 100 und ohne Angabe einer Nummer werden automatisch hochgezählt und dann stur nacheinander verarbeitet. Die Regeln von 1-99 verlangen die Angabe der Reihenfolge von Euch - aber Ihr könnt natürlich trotzdem auch die Regeln oberhalb 100 selber durchnummerieren. Das +100-Auto-Increment-Feature soll mehr als mögliche Erleichterung beim Schreiben von Regeln verstanden werden. Die am Ende, oberhalb von 65435 werden allesamt gleich behandelt nach der Idee: möge die passendste gewinnen. Damit passiert ein Paket im höchstfall 65535 Regeln pro Satz, wovon sich wiederrum maximal 32 definieren lassen. Die Idee verschiedener Regelsätze ist wieder mehr aus organisatorischen Gründen sinvoll, aber Ihr könnt natürlich auch alle Regeln in einen einzelnen Satz pressen.
Zurück zu unseren Regeln: da die oben definierte jetzt aber allen Traffic verbietet, seid Ihr mal eben schnell zum Fort Knox geworden - rien ne vas plus - nichts geht mehr. Surfen? Keine Chance, der geliebte Instant Messenger - getrennt. Es ist jetzt eben an Euch, die Firewall eben soweit zu durchzukonfigurieren, dass sie stets nur Euren Anforderungen entspricht (die Textdatei /etc/services spielt hierbei eine kleine aber entscheidene Rolle - Ihr könnt mit Hilfe der Namensgebung Port- und Protokollgruppen bilden). Im Übrigen: das genaue Gegenteil vom oben erwähnten Standard wäre, wenn Ihr die nachfolgende Regel definiert:
CODE
1 IPFIREWALL_DEFAULT_TO_ACCEPT
Hierdurch wird aus dem 'drop' der Standard-Regel (65535) ein 'accept' und sämtlicher Traffic darf auf einmal das Netzwerk passieren.
Startet Ihr künftig neu, dann wird der Daemon automatisch geladen - künftig reicht ein /sbin/ipfw_firewall.sh um die Policy zurückzusetzen. Für den ersten Start und um sich den Reboot zu sparen, klar, ein '/sbin/ipfw_firewall.sh' muß her.
Wenn ich mal Zeit hab, dann bringe ich Ordnung in meine /etc/ipfw.conf und poste sie anschließend kommentiert hier. Bis dahin muß der Rest hier für Euch reichen:
Lose Sammlung fertiger Regelwerken: 1, mehr, und nochmehr...
Traffic Shaping (Bandbreitenmanagement) mit ipfw
Die Bandbreite für bestimmte Anwendungen oder Dienste zu begrenzen, kann aus ganz verschiedenen Gründen sinnvoll sein. Ein paar Beispiele vorweg:
- den Up- und Downstream eingehendem|ausgehendem FTP- oder sonstiger Dateitransfer- oder Web-Anwendungen beschränken
- es Time Machine und Time Capsule untersagen, immer die gesamte zur Verfügung stehende WiFi-Bandbreite zu nutzen
- und viele andere Dinge mehr
Los gehts:
CODE
pipe 1 config bw 500kbit/s # erstellt einen ipfw-Kanal mit einer maximalen Bandbreite von 500kbit/s
add pipe 1 dst-port-afpovertcp # TCP/UDP, Port 548 Traffic (/etc/services) gen Pipe 1
add pipe 1 dst-port-afpovertcp # TCP/UDP, Port 548 Traffic (/etc/services) gen Pipe 1
Damit haben wir den AFP-Traffic ultimativ auf 500kbit/s begrenzt. Der Rest steht dann für anderes zur Verfügung. Natürlich begrenzt diese Regel so den gesamten AFP-Traffic der über das Netz geht, aber will man eben zum Beispiel nur den AFP-Traffic zu seiner Time-Capsule begrenzen, dann müssen eben der ipfw-Konvention entsprechend Werte für Quelle und Ziel mitangeben werden (ansonsten gilt stets: from any to any).
Ein etwas anschaulicheres Beispiel: root betreibt einen Mail Server (SMTP, IMAP, POP), dann kann es immer mal passieren, dass eine eigehende Mail mit einem großen Attachment sich die gesamte zur Verfügung stehende Bandbreite zu Nutze macht und damit den eigehenden Mail-Client-Traffic blockiert. Damit das eben nicht passiert, definieren wir das Regelwerk jetzt wie folgt:
CODE
pipe 1 config bw 500kbit/s
queue 1 config pipe 1 weight 50 # innerhalb der Pipe 1 definieren wir den ersten Traffic-Queue mit der Priorität 50
queue 2 config pipe 1 weight 50 # jetzt einen zweiten Queue, ebenfalls mit der Priorität 50
add queue 1 dst-port smtp # SMTP-Traffic passiert innerhalb der ersten den Pipe, den erste Queue
add queue 2 dst-port pop3 # Pipe 1, Queue 2
add queue 2 dst-port pop3s # Pipe 1, Queue 2
add queue 2 dst-port imap # Pipe 1, Queue 2
add queue 2 dst-port imaps # Pipe 1, Queue 2
queue 1 config pipe 1 weight 50 # innerhalb der Pipe 1 definieren wir den ersten Traffic-Queue mit der Priorität 50
queue 2 config pipe 1 weight 50 # jetzt einen zweiten Queue, ebenfalls mit der Priorität 50
add queue 1 dst-port smtp # SMTP-Traffic passiert innerhalb der ersten den Pipe, den erste Queue
add queue 2 dst-port pop3 # Pipe 1, Queue 2
add queue 2 dst-port pop3s # Pipe 1, Queue 2
add queue 2 dst-port imap # Pipe 1, Queue 2
add queue 2 dst-port imaps # Pipe 1, Queue 2
Damit erreichen wir, dass innerhalb der ersten Pipe, welchem eine maximale Bandbreite von 500kbit/s zur Verfügung steht, sämtlicher SMTP/(POP/IMAP)-Traffic zu jeweils gleichen Teilen im Maximum passieren darf. Die klassische Art unnötige Latenzen auf Nutzerseite zu vermeiden, die bei Abrufen von E-Mails und gleicheitigem SMTP-Transfer auf ein- und derselben Maschine sonst schell entstehen können, aber das Beispiel hier lässt sich, und muss oft noch um viele Nuancen erweitert und verfeinert werden. Es soll für Euch in dieser Form mehr eine Idee der Möglichkeiten sein.
Ein weiteres Beispiel für den Einsatz von Traffic-Shaping wäre: als Web-Entwickler fragt man sich ja doch manchmal, wie lange wohl ein Aufruf der Seiten zum Beispiel mit 7k/s (1-B-Kanal, ISDN-Geschwindigkeit) dauern würde. Ein Kinderspiel, dank ipfw:
CODE
pipe 1 config bw 70kbit/s
add pipe 1 dst-port http
add pipe 1 dst-port https
add pipe 1 dst-port http
add pipe 1 dst-port https
Mac OS X Internet Sharing per ipfw und natd per Hand aktivieren
/usr/sbin/sysctl -w net.inet.ip.forwarding=1
- aktiviert die IP-Forwarding Funktionalität von OS X, die über den Kernel zur Verfügung gestellt wird
/usr/sbin/natd -same_ports -use_sockets -log -deny_incoming -interface en0
- startet den natd-Dienst (Network Address Translation) von OS X und bindet diesen an das Interface en0
/sbin/ipfw add divert natd ip from any to any via en0
- leitet sämtlichen IP-Traffic, der das Interface en0 passiert, über den natd-Dienst um
Zur Info: Linux nutzt und kennt keinen Dienst mit dem Namen natd. Anstelle dessen genügt es, die IP-Forwarding Funktionalität des Kernels zu aktivieren und eine einfache Masquerading-Regel auf dem gewünschten Interface wie folgt zu definieren:
CODE
/sbin/iptables -a postrouting -t nat -o eth0 -j masquerade