Op macOS is een killswitch alleen zo goed als het pakketfilterbeleid erachter, en pf is het onderdeel dat dat beleid afdwingt. Het beleid moet twee tegenovergestelde dingen tegelijk doen: de client bij zijn server laten zodat er een tunnel kan bestaan, en al het andere tegenhouden om er direct uit te gaan. De meeste gepubliceerde regelsets lukken in het tweede en vergeten het eerste, dus de machine verliest verbinding in plaats van bescherming te winnen. De versie die werkt noemt het eindpunt, de resolver en de tunnel, en niets meer.

Wat een pakketfilter kan beslissen, en wat niet

pf kijkt naar interfaces, adressen, protocolnummers en poorten, en daar stopt het. Het leert nooit welk proces een socket heeft geopend, dus een toestemming die voor één programma is geschreven wordt een toestemming voor elk programma dat dezelfde poort kan bereiken. Zo verandert een ruime UDP-uitzondering een killswitch weer in een gewoon netwerk. Applicatiebewust blokkeren hoort bij de client of bij een systeemextensie, niet bij dit bestand.

Het filter maakt ook geen routes. Als de client wegvalt, kan de standaardroute nog naar een dode tunnel wijzen, en pf maakt daar geblokkeerde pakketten van in plaats van een stille terugval naar Wi-Fi. De blokkade is het bedoelde resultaat, geen storing om omheen te routeren.

De volgorde beslist vaker de uitkomst dan de tekst van de regel. Zonder quick blijft pf na een overeenkomst doorzoeken en de laatste passende regel wint, dus een brede doorgang onderaan overstemt de zorgvuldige uitzonderingen erboven. De doorgangen met quick markeren maakt de eerste overeenkomst definitief, wat een beleidsauteur meestal bedoelt.

Lees eerst de feiten over interface en eindpunt

Draadloze interfaces zijn niet op elke Mac hetzelfde genummerd, en het tunnelapparaat krijgt een nummer dat tussen opstarts kan veranderen. Een regel die een niet-bestaand apparaat noemt faalt bij het parsen, wat luid en makkelijk te vangen is, terwijl een regel die het verkeerde bestaande apparaat noemt schoon parst en verkeer doorlaat dat je wilde stoppen.

Verzamel de feiten terwijl de client verbonden is, want het tunnelapparaat bestaat alleen dan. Je hebt de interface nodig die de standaardroute houdt, het apparaat dat de client heeft gemaakt, en het adres en de poort van de server waarnaar hij belt. Juist die laatste waarde laten de meeste regelsets weg, en dat weglaten is waarom een gekopieerde set zijn eigen tunnel blokkeert.

route -n get default
ifconfig -l
scutil --nc list

Het eerste commando noemt de interface die nu wint, het tweede somt elk apparaat op dat de kernel kent, en het derde toont de VPN-configuraties die het systeem kan starten. Schrijf die antwoorden op en lees daarna het serveradres en de poort uit de eigen configuratie van de client.

Schrijf de regelset rond het tunnelpad

Het bestand hieronder weigert standaard en laat vijf dingen weer toe: loopback, het tunnelapparaat, het pad van de client naar zijn server, de resolver die gebruikt wordt voordat de tunnel bestaat, en de twee uitwisselingen die een draadloze verbinding bruikbaar houden. Interfacenamen en het serveradres komen van jouw machine, dus vervang de waarden die je hebt bevestigd. Het tunnelnummer zal ook niet met het jouwe overeenkomen.

set skip on lo0
block all
pass out quick on utun0
# maintain this line when the server address changes
pass out quick on en1 inet proto udp to 203.0.113.10 port 1194
# only the resolver the client uses before the tunnel exists
pass out quick on en1 inet proto udp to 192.0.2.53 port 53
pass out quick on en1 inet proto udp from any port 68 to any port 67
pass out quick on en1 inet proto udp to 224.0.0.251 port 5353

De DHCP-regel bestaat omdat een Mac om een adres vraagt voordat hij er een heeft, dus het verzoek verlaat poort 68 richting poort 67 zonder bruikbaar bronadres. De multicast-regel laat het systeem beoordelen of de draadloze verbinding de moeite waard is, en hem verwijderen levert een machine op die geen verbinding meldt op een werkend netwerk. De eindpuntregel is wat OpenVPN überhaupt bij zijn server laat komen; zonder die regel gooit block all de buitenste handdruk weg en komt de tunnel nooit op. De resolverregel is smaller dan een algemene DNS-toestemming, omdat hij één adres en één poort noemt in plaats van elke query die de machine doet.

Die eindpunt- en resolveruitzonderingen zijn de twee die moeten bestaan; al het andere blijft geblokkeerd. Als de client een letterlijk adres belt, laat de resolverregel dan weg en houd de eindpuntregel. Als het serveradres verandert, werk die regel dan bij voor de volgende verbinding, of vul een pf-tabel vanuit de eigen hook van de client en match in plaats daarvan de tabel.

Haal de tunnel neer en herstart daarna

Parse voordat je laadt, want een syntaxfout is goedkoop in een droge run en duur in een externe sessie. Laad daarna het bestand, zet het filter aan als het uit stond, en lees terug wat werkelijk van kracht is. Het teruglezen is geen ceremonie: een ander anker kan regels toevoegen die je niet hebt geschreven, dus vergelijk de afgedrukte set met je bestand.

sudo pfctl -n -v -f /etc/pf.conf
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
sudo pfctl -s rules
sudo pfctl -s Anchors
sudo pfctl -a <anchor> -s rules
sudo pfctl -s info

Haal met de regels geladen de tunnel neer en probeer een pagina te laden. Gewoon verkeer hoort te stoppen, en het hoort gestopt te blijven nadat de machine slaapt en wakker wordt, want juist bij die overgang kruipt oude toestand terug. Verbind opnieuw en bevestig dat verkeer hervat zonder de regelset aan te raken. Als een browser nog werkt met de tunnel neer, is een regel breder dan je dacht, en het teruglezen noemt hem meestal.

De herstart is het deel dat mensen overslaan. macOS leest /etc/pf.conf bij het opstarten, maar het bestand lezen is niet hetzelfde als het filter aanzetten, dus een regelset die nu werkt kan na een herstart inactief zijn. Voer daarna sudo pfctl -s info uit en bevestig dat de statusregel het filter als aangezet meldt.

Houd de uitgang en het herstartpad eerlijk

Een regelset die alles blokkeert kan je ook buiten een machine sluiten die je op afstand beheert. Bewerk vanuit een lokale sessie wanneer je kunt, houd het vorige bestand naast het nieuwe, en ken de terugweg voordat je hem nodig hebt. De commando's hieronder schakelen het filter uit en laden de kopie die je bewaarde opnieuw, en dat is de reeks voor een console, niet over de tunnel die je net hebt doorgesneden. Een terugweg van vijf seconden verslaat elke keer een slimme regel.

sudo pfctl -d
sudo pfctl -f /etc/pf.conf.previous
sudo pfctl -s rules

Voor persistentie is een launch daemon nodig die pfctl -e uitvoert en je bestand bij het opstarten laadt. Behandel hem als elke andere bevoorrechte taak: absolute paden, een log dat je achteraf kunt lezen, en een test op een machine die je fysiek kunt bereiken. Een daemon die stil faalt laat de machine onbeschermd en normaal lijkend achter.

Documenteer de gaten, te beginnen met een tweede VPN-client die zijn eigen tunnelapparaat maakt. Je regels noemen één tunnelinterface, dus een tweede blijft geblokkeerd of wordt gevangen door een brede regel die later wordt toegevoegd. Tethering- en hotspotinterfaces verschijnen maar soms en verdienen dezelfde noot. Geen van die gaten is een reden om het beleid over te slaan; het zijn de voorwaarden waaronder het nog steeds geldt.

Gerelateerde artikelen