19

I've been able to deny all connections to external networks unless my OpenVPN connection is active using pf.conf. However, I lose Wi-Fi connectivity if the connection is broken by closing and opening the laptop lid or toggling Wi-Fi off and on again.

  • I'm on Mac OS 10.8.1.
  • I connect to the Web via Wi-Fi (from varying locations, including public Wi-Fi).
  • The OpenVPN connection is set up with Viscosity.

I have the following packet filter rules set up in /etc/pf.conf

# Deny all packets unless they pass through the OpenVPN connection
wifi=en1
vpn=tun0

block all

set skip on lo
pass on $wifi proto udp to [OpenVPN server IP address] port 443
pass on $vpn

I start the packet filter service with sudo pfctl -e and load the new rules with sudo pfctl -f /etc/pf.conf.

I have also edited /System/Library/LaunchDaemons/com.apple.pfctl.plist and changed the line <string>-f</string> to read <string>-ef</string> so that the packet filter launches at system startup.

This all seems to works great at first: applications can only connect to the web if the OpenVPN connection is active, so I'm never leaking data over an insecure connection.

But, if I close and reopen my laptop lid or turn Wi-Fi off and on again, the Wi-Fi connection is lost, and I see an exclamation mark in the Wi-Fi icon in the status bar. Clicking the Wi-Fi icon shows an "Alert: No Internet connection" message:

No Internet connection message

To regain the connection, I have to disconnect and reconnect Wi-Fi, sometimes five or six times, before the "Alert: No Internet connection" message disappears and I'm able to open the VPN connection again. Other times, the Wi-Fi alert disappears of its own accord, the exclamation mark clears, and I'm able to connect again. Either way, it can take five minutes or more to get a connection again, which can be frustrating.

Removing the line block all resolves the problem (but allows insecure connections), so it seems there's a service I'm blocking that Apple requires in order to regain and confirm a Wi-Fi connection. I have tried:

  • Enabling icmp by adding pass on $wifi proto icmp all to pf.conf
  • Enabling DNS resolution by adding pass on $wifi proto udp from $wifi to any port 53
  • Trying to learn more by logging blocked packets (by changing block all to block log all), but logging seems to be disabled under OS X, because doing sudo tcpdump -n -e -ttt -i pflog0 to see the log results in "tcpdump: pflog0: No such device exists".

None of this helps re-establish a Wi-Fi connection any faster.

What else can I do to determine what service needs to be available to regain Wi-Fi connectivity, or what rule should I add to pf.conf to make Wi-Fi reconnections more reliable?

1

5 Answers 5

15

By monitoring network connections using Little Snitch, I've found that Apple uses the mDNSResponder app in the background to check if the Wi-Fi connection is available. mDNSResponder sends UDP packets to nameservers to check connectivity and resolve hostnames to IPs.

Changing the UDP rule I had previously to allow all UDP packets over Wi-Fi allows mDNSResponder to connect, which means Wi-Fi now reconnects first time after a disconnection. In case it helps others in future, my final pf.conf including Apple's default rules for Mountain Lion looks like this:

#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"as
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

#
# Allow connection via Viscosity only
#
wifi=en1 #change this to en0 on MacBook Airs and other Macs without ethernet ports
vpn=tun0
vpn2=tap0

block all

set skip on lo          # allow local traffic

pass on p2p0            #allow AirDrop
pass on p2p1            #allow AirDrop
pass on p2p2            #allow AirDrop
pass quick proto tcp to any port 631    #allow AirPrint

pass on $wifi proto udp # allow only UDP packets over unprotected Wi-Fi
pass on $vpn            # allow everything else through the VPN (tun interface)
pass on $vpn2           # allow everything else through the VPN (tap interface)

This means that data can now be leaked over Wi-Fi by the small number of applications that use the UDP protocol, unfortunately, such as ntpd (for time synchronisation) and mDNSResponder. But this still seems better than allowing data to travel unprotected over TCP, which is what the majority of applications use. If anyone has any suggestions to improve on this setup, comments or further answers are welcome.

11
  • This is something I've casually been interested in, seeing your results has inspired me to go home and try it! thanks!
    – jakeva
    Commented Sep 5, 2012 at 22:52
  • @SixSlayer It seems to work pretty well! I have Viscosity set up to autoconnect on startup and on dropped connections, which makes the whole thing pretty much seamless. Main thing to note is that pf.conf and com.apple.pfctl.plist get reset to the default after OS updates, apparently, so it's worth keeping a backup of both.
    – Nick
    Commented Sep 5, 2012 at 22:53
  • IMHO the UDP thing is kind of a bummer. I'm not a network guy but this kinda thing helps me learn, and I have a fascination with having control over these kinds of details. I'll spend some time looking for a work around, but if anyone beats me to it, just as well.
    – jakeva
    Commented Sep 6, 2012 at 1:51
  • this is awesome - exactly what I was looking for. Thank you!
    – keo
    Commented Sep 7, 2012 at 19:04
  • Have you maybe managed to have many OpenVPN connections open at the same time, and routing through them in parallel? (to gain and add up bandwidth)
    – keo
    Commented Sep 7, 2012 at 19:04
12

You don't need to allow all UDP. The 'm' in mDNS means 'multicast', and it uses a specific multicast destination IP address called the "link local multicast address", and a UDP port number 5353.

This means in your solution above, you are unnecessarily allowing traffic to all 65535 UDP ports to all 3.7 Billion routable IP addresses in the world to bypass your VPN. You'd be surprised how many applications use UDP, so you are significantly defeating the purpose of your original idea to prevent outgoing traffic when the VPN is down.

Why not use this rule instead:

pass on $wifi proto udp to 224.0.0.251 port 5353

A very important rule of thumb with firewall configuration - when making exceptions through your firewall, always try to use the most specific rule possible. The specificity sometimes comes at the expense of convenience & ease of use, i.e. you might then find there's some other link-local protocol that needs to be let through, and add yet another specific rule.

If you swap in the above rule and find that the original wifi problem returns, then your PF may be blocking DHCP, the protocol used to autoconfigure your network devices' IP addresses. (in a home network, typically your broadband router would be your DHCP server). The rule you'd need to allow DHCP, would be:

pass on $wifi proto udp from 0.0.0.0 port 68 to 255.255.255.255 port 67

*Note: you may need to substitute 0.0.0.0 for any. The DHCPREQUEST packet your computer first sends, has a source address 0.0.0.0 because at that stage, your computer doesn't have an IP address yet.
To be honest, I would lean more towards using any. Another option is to rip out any source specification, i.e. pass on $wifi proto udp to 255.255.255.255 port 67, but that means we lose the source-port part of the rule, and being as specific as possible is always the most secure option.

Hope that helps. Here are some useful links:

mDNS: http://en.wikipedia.org/wiki/Multicast_DNS#Packet_structure

DHSP: http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol#DHCP_discovery

0
1

With the aim to create the PF rules in an "easy" way, identifying existing active interfaces including the current (vpn) interfaces this small killswitch program may be used,

Still in progress but could be a good start for identifying external IP, and active interfaces in order to properly create the firewall rules.

example or output using the -i (info) option:

$ killswitch -i
Interface  MAC address         IP
en1        bc:57:36:d1:82:ba   192.168.1.7
ppp0                           10.10.1.3

public IP address: 93.117.82.123

Passing the server ip -ip:

# --------------------------------------------------------------
# Sat, 19 Nov 2016 12:37:24 +0100
# sudo pfctl -Fa -f ~/.killswitch.pf.conf -e
# --------------------------------------------------------------
int_en1 = "en1"
vpn_ppp0 = "ppp0"
vpn_ip = "93.117.82.123"
set block-policy drop
set ruleset-optimization basic
set skip on lo0
block all
pass on $int_en1 proto udp to 224.0.0.251 port 5353
pass on $int_en1 proto udp from any port 67 to any port 68
pass on $int_en1 inet proto icmp all icmp-type 8 code 0
pass on $int_en1 proto {tcp, udp} from any to $vpn_ip
pass on $vpn_ppp0 all

Is far from perfect but work is in progress more info/code can be found here: https://github.com/vpn-kill-switch/killswitch

0

This gave me sufficient background info to make the big leap and use pf.conf. Here's what I use on my 10.8 to make it reconnect after the VPN connection drops :

(I only use ethernet but you can change $lan for $wifi and it should work)

lan=en0
wifi=en1
vpn=tun0
block all
set skip on lo
pass on $lan proto { udp,tcp } to 8.8.8.8
pass on $lan proto tcp to vpn.btguard.com port 1194
pass on $vpn
0

-- As addition --

You might want to add this line:

pass on $wifi inet6 proto udp from any to FF02:0000:0000:0000:0000:0000:0000:00FB port 5353

to allow for mDNS to operate on ipv6

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .