My OS X “VPN only” Setup For #30C3
You should never let passwords or private data be transmitted over an untrusted network (your neighbor’s, the one at Starbucks or the company) anyway, but on a hacker congress like the #30C3, this rule is almost vital.
Hackers get bored easily, and when they’re bored, they’re starting to look for things to play with. And a network with several thousand connected users is certainly an interesting thing to play with. Some of them might start intercepting the data on the network or do other nasty things with the packets that they can get.
If these packets are encrypted, messing with them is much harder (but not impossible! – see the end of this article). So you want your packets to be always encrypted. And the best way to do that is by using a VPN.
Please note that this guide is written for users who are at least a bit tech-savvy. In case you don’t know what a VPN is or don’t feel comfortable doing the things I suggest in this guide (for example because you don’t understand anything at all), please contact a hacker you trust and ask her or him to make your connection more secure. Please don’t ask me (except if we know each other personally), because I don’t have the time to do user support for thousands of guests.
What’s being used?
The machine I’m using is a MacBook Air with OS X Mountain Lion, and the software I use is Tunnelblick for the OpenVPN connection and PF (“packet filter”) to transmit as little unencrypted traffic as possible when the VPN goes down. And the focus of this guide is definitely that second part, because setting up the VPN is pretty easy, but if the connection breaks, is blocked or for some other reason not running, your system will by default transmit data anyway, and unencrypted data will be interceptable. So what I basically do is to forbid nearly all network traffic (using PF), except for the things that are required to connect to the VPN. PF is OS X’s recommended way of filtering packets and available in Lion or higher, afaik.
The basic idea
So, what do we need in order to connect to the VPN?
- Internet access. This means we should allow DHCP (for getting an IP address) and ICMP (for being a nice internet citizen).
- DNS, if we don’t want to connect to the VPN by IP address. I’m using IPredator as the VPN provider, which uses several IP addresses and DNS Round Robin to spread the users across them, so I want to be able to use DNS.
- A connection to the VPN server(s) on whatever port the VPN uses. I’m using OpenVPN on UDP port 1194, and I highly recommend that you don’t use a TCP-based VPN connection. (Else, people might(?) be able to do evil things with DNS spoofing and SRV records.)
The config I use has several drawbacks:
- I completely disable IPv6. Yes, some of you will be outraged about that, and rightly so. But IPredator doesn’t support v6, and I didn’t find the time to configure my own VPN server. If I’d allow IPv6, all of my v6 connections would be bypassing the VPN!
- The DNS servers I use are Google’s, because I trust them a bit more than a shady Starbucks DNS. Note that rogue admins (or rogue users) can still try to spoof their IP address and impersonate Google’s servers, thus supplying you with incorrect DNS responses. That’s why in my setup TCP connections are only allowed over the VPN.
- There’s no automatic detection whatsoever whether OpenVPN is running, so if you want to use the Internet without the VPN again, you’ll need to explicitly disable PF.
There’s a nice guide that explains the PF setup on OS X, and I’m not doing anything more than that here. A tutorial on PF itself, which is a OpenBSD project, is available as well.
Set up OpenVPN and Tunnelblick
I’m using Tunnelblick as my OpenVPN GUI and IPredator as my VPN provider. Get an account, pay 6€ to activate it for a month (I’ve used PayPal, but you can also use BitCoin and other methods) and set it up according to the guides available on the IPredator website. Make sure that you enable “route all traffic through the VPN” in the “while connected” tab of the advanced settings. According to the Tunnelblick documentation, this is equivalent to the OpenVPN option
I found Tunnelblick (3.3) to be kind of unstable on my machine. Sometimes I had to terminate OpenVPN myself (using
sudo killall openvpn) because it wouldn’t reconnect and Tunnelblick wasn’t able to terminate it. But since the PF rules protect me from unencrypted communication, I don’t really care.
Having an exit strategy
If you fuck up your firewall rules, you might end up in a situation where you can’t even google how to fix things again. Therefore, keep this command in mind:
sudo pfctl -d. It will completely disable PF.
The PF config
There’s a default PF configuration file,
/etc/pf.conf, and I suggest you don’t modify it. Instead, write your own. Mine is
In that file, you need to define one or more anchors which contain the actual rules. Therefore, my config file only contains these two lines:
anchor "name.scy.pf" load anchor "name.scy.pf" from "/etc/pf.anchors/name.scy"
Use something unique for the anchor name. The suggested way is to use a reversed domain, so if you own flauschmett.de, your anchor could be named de.flauschmett.pf. I own scy.name, and thus it’s name.scy.pf for me.
The really interesting part of the configuration is what’s written in the anchor file,
/etc/pf.anchors/name.scy in my case. Since that file is a bit longer and contains lots of comments (which is a good thing), I won’t quote it here but instead link to a Gist containing it.
sudo pfctl -v -n -f /etc/pf.scy.conf to check the config for errors, and
sudo pfctl -e -f /etc/pf.scy.conf to actually load it.
Making sure it will be loaded after rebooting
I guess you’ll want your PF settings to survive a reboot, so you need to add a launchd item for it. This is simply an XML file that you place in
/Library/LaunchDaemons. Note that it must be owned by root in order to do anything at all.
Do something like
sudo nano -w /Library/LaunchDaemons/name.scy.pf.plist (pay attention to the
.plist extension) and paste the text you can find in the Gist there.
Please note that this might not work. The article I’ve linked to above suggests doing it this way, but on my machine
/var/log/pf.log contained the error message
pfctl: DIOCADDRULE: Resource busy
and the rules were not loaded. So please double-check if your PF rules have been loaded, and load them manually, if necessary. If you reboot and Dropbox starts syncing without the VPN running, you’re doing it wrong. ;)
I currently don’t know why this is happening, and certainly not how to fix it. Sorry.
I just took a look at the
DIOCADDRULE: Resource busyerror as I was experiencing it too. As it turns out, this has already been discussed in your linked article’s comments (see Danie Lord’s first comment). Waiting for all interfaces to come up using
ipconfig waitalldid the trick.
I also cleaned up the rest of
name.scy.pf.plista bit, maybe you want to merge some of my changes.