It works on your Mac, so it looks like something you can hand to users. That assumption is the first thing to lose. Apple's TN3165 states plainly that Packet Filter is not a supported API for generally distributed products, and it frames privileged, controlled site administration as the reasonable case. A VPN lock built on PF is therefore a local operational policy with an owner, not a feature you can ship inside an app.

PF is not a packaging surface

The distinction is not academic. A distributed product that depends on PF inherits every change Apple makes to it, every conflict with another tool that also writes rules, and no support commitment that the arrangement will keep working. A site that administers its own machines has a different relationship: it can document the dependency, test it against each OS release, and accept the maintenance.

Some installation advice crosses a line that is worth naming. Overwriting the entire system rule set to make one anchor load, editing Apple's system launch configuration, or disabling System Integrity Protection are not acceptable requirements for a reusable recipe. If a published guide needs any of those, it is describing a lab, not a deployable policy.

Write the egress intent before the syntax

Decide what is allowed before choosing a single keyword. Three paths usually need to exist: the VPN client's own transport to its server, the protected application traffic inside the tunnel, and whatever setup or local services genuinely require a direct path.

Give every proposed exception a destination, a transport, and an owner. Allowing every port to an endpoint permits far more than the one transport the tunnel uses, and the extra exposure arrives without anyone deciding it should. Allowing a public resolver is the same trap in a friendlier costume: it creates a direct DNS path even while the tunnel is down, so name lookups still leak.

PathDestinationOwner
VPN transportGateway address, one UDP portNetwork team
Tunnel trafficInternal prefixesApplication owner
Resolver exceptionNamed internal resolverSecurity team

The useful test for an exception is whether you can name the person who asked for it and the failure it prevents. If neither answer exists, the rule is probably a workaround for a misconfiguration that will otherwise stay invisible.

utun numbers are not an identity

MacOS creates tunnel interfaces as utun devices, and the number assigned to your VPN is not stable across reboots, reconnects, or the presence of another client. A rule that lists several guessed numbers is not a policy; it is a snapshot of one afternoon.

Anchor the rule to something that identifies the intended provider: the address plan the tunnel assigns, the process that owns the interface, or a configuration the VPN client itself maintains. Then record how that identification works, because the person reviewing the rule in a year will not have your shell history.

Make the loaded policy attributable

An anchor name organises rules; it does not prove that the effective policy is what you intended. Precedence between anchors, rules that other software loaded, and existing state can all change the outcome. Review the effective set with whoever else owns PF on that machine, and document the ownership in the same place as the configuration. Two owners editing different anchors with different assumptions is the ordinary way a lock develops a hole.

Keep the rule set in the site's supported configuration path, version it, and note which macOS releases were tested. A change that is applied by hand and remembered by one person is not maintained infrastructure, whatever the file header says.

Test the transitions, not the connected state

A screenshot taken while the tunnel is up proves almost nothing. Walk the states instead: startup before any connection, a working tunnel, a tunnel that drops without warning, a deliberate disconnect, and a move from Wi-Fi to a hotspot. Each state has its own answer, and the interesting ones are the failures rather than the happy path.

In each state, record what is intentionally reachable. Check IPv4 and IPv6 separately, test name resolution, and include a flow that was already established before the transition, since existing connections do not always follow the new policy. A browser that fails while the tunnel is down is one data point, not evidence that every other path is blocked.

Finally, keep a recovery method that does not depend on the network path you are changing. A known-good configuration on local storage and a documented way to load it turn a lock into a policy; without them, one bad revision locks the administrator out of the machine that was supposed to be protected.

ບົດຄວາມທີ່ກ່ຽວຂ້ອງ