Network Security – How to Block Specific IP Address When VPN is Not Connected

firewallNetworkSecurityvpn

I'm trying to configure a packet filter firewall to block access to a small range of IP addresses when a VPN is not connected.

My rules should allow https/http traffic apart from this small, specific list of IP addresses. It's important the device can not reach these IP address when the VPN is not connected.

I'm using the built in macOS VPN in 10.13

I've got some fairly standard stuff in my rules so far:

vpn_ifs = "{ utun1 ipsec0 }"
hw_ifs = "{ en0 en1 en2 }"
...
pass quick on $vpn_ifs

The reason for my request is that I have a web based resource I can only access once the VPN is connected. Allowing connectivity to this resource before the VPN is connected disables access to my account.

I need access to http and https traffic before the VPN is established to enable services that need to function before the VPN connection is up. Including, but not limited to captive portal support.

Best Answer

Each packet is evaluated against the filter ruleset from top to bottom and the last matching rule wins. The quick keyword is the only exception: the packet will be blocked or passed immediately unperturbed by any later rule.

Assuming you are using the deny all approach you have to explicitly allow packets to pass in or out.

Rule set:

block all
...

To meet your "web based resource" requirement you have to quick allow/deny packets depending on the interface:

pass out quick on $vpn_ifs from any to { <web_res1>, <web_res2> } no state  
block out quick on $hw_ifs from any to { <web_res1>, <web_res2> } no state

To meet your "http and https traffic" requirement you have to allow outgoing traffic to port 80/443:

pass out on $hw_ifs from any to any port { 80, 443 }

Using the deny all approach you have add some more rules (e.g. to allow traffic from/to local networks).

The final rule set looks like this then:

block all
...
rules
...
pass out quick on $vpn_ifs from any to { <web_res1>, <web_res2> } no state  
block out quick on $hw_ifs from any to { <web_res1>, <web_res2> } no state
...
pass out on $hw_ifs from any to any port { 80, 443 }
...
more rules

Here is an example of a deny all pf.conf with a lot of rules explained. The rules are used on a FreeBSD Gateway though: some mustn't be applied or aren't useful in your environment. Single rules don't work at all (e.g. ALTQ related features).