Mac OS X + OpenVPN bridge + SSH Tunnel = VPN goodness

Ah, the beauty of open source software. This is a long post that started with a simple problem. At work, we live behind the Great Olin Firewall (GOF). I checked out some code from an internal Subversion repository over HTTP. Now, I’m outside the GOF and I want to commit some important changes. We have an SSH gateway, and SSH has a built-in SOCKS5 proxy. Cool, that should work, right? Nope. While you can use tsocks or proxychains, or some other transparent proxy service to get most of the way there, it isn’t the whole thing. I learned a lot from this page, but proxychains doesn’t compile well on OS X. Ugh. And tsocks doesn’t tunnel DNS requests on OS X.

Now, the GOF is a Nortel piece that uses a custom hash function for the username running and standards-based IPSec VPN. Boo. That means it won’t work with any third-party VPN client. You have to use the Contivity piece of garbage. Bonus that this piece of garbage is written by Apani, and is now no longer actively supported. Double ugh. So here is what I need:

  • VPN access to the work network
  • Tunneled through SSH
  • Ability to redirect DNS lookups through the VPN
  • Free, as in speech

What you’ll need to do this:

  • Root on a linux server inside the firewall
  • Mac on the outside

There, not so hard? Thankfully, the Intarwebs and a little hacking last night delivered the goods. In short, I used OpenVPN, a few scripts, and a little luck. Here’s how.


  1. I used Ubuntu Dapper inside a Xen virtual machine. Worked great.
  2. apt-get install openvpn openssl bridge-utils
  3. cp -R /usr/share/doc/openvpn/examples/easy-rsa /etc/openvpn
  4. Follow the directions for generating keys at the OpenVPN HOWTO
  5. Copy the keys to the right places, as from the above HOWTO. I put my keys in /etc/openvpn/keys
  6. Use the following configuration file for your server. This assumes an ethernet bridge will be built (later) and that you have a DHCP server somewhere on the LAN to give out IP addresses to VPN clients

    proto tcp-server
    dev tap0
    ca /etc/openvpn/keys/ca.crt
    cert /etc/openvpn/keys/server.crt
    key /etc/openvpn/keys/server.key
    dh /etc/openvpn/keys/dh1024.pem
    mode server
    keepalive 10 120
    cipher BF-CBC
    status openvpn-status.log
    verb 3
  7. Create the ethernet bridge Debian-style by adding the following lines to your /etc/network/interfaces:

    auto br0
    iface br0 inet dhcp
    pre-up openvpn --mktun --dev tap0
    bridge_ports eth0 tap0
  8. Restart network with /etc/init.d/network stop and /etc/init.d/network start
  9. Now you should still have internet connectivity, and eth0 and a br0 bridge


  1. Get and install OpenVPN 2.x. I used Macports, so I just fired up ports like: sudo port install openvpn2
  2. Grab and install the tun/tap driver for Mac OS X
  3. Reboot, or force install the kernel extensions
  4. Make sure you have the client encryption keys from above, and use the following client configuration:
    dev tap
    proto tcp-client
    remote localhost
    ca /opt/local/etc/openvpn/keys/ca.crt
    cert /opt/local/etc/openvpn/keys/mchang.crt
    key /opt/local/etc/openvpn/keys/mchang.key
    cipher BF-CBC
    up /opt/local/etc/openvpn/
    down /opt/local/etc/openvpn/
    verb 3
  5. Grab the script from the bottom half of this mailing list posting, or download it right here:
  6. Install that in /opt/local/etc/openvpn and chmod +x it so it can be executed

Fire it up

Now we’re ready to start this thing up, over an SSH tunnel, kind of like describe in this post.

  1. Fire up your ssh, tunneling the OpenVPN port like
    ssh -L
  2. Where is your ssh gateway, and specifies the tunnel endpoint at the VPN server you have just set up.
  3. Turn on OpenVPN on the server like
    openvpn --conf /etc/openvpn/bridge.conf
  4. Turn on OpenVPN on the client like
    sudo openvpn2 --config /opt/local/etc/openvpn/client.conf
  5. You should see, on both sides, OpenVPN authenticating and then starting up.

Now, you should be able to ping any machine on the remote subnet. I wanted to be able to get to anything on the internal network through the VPN, so I manually added a route to all of the internal network ( through the VPN server’s default gateway. Like this:

route add

Of course, substitue the IP of the VPN server’s gateway to the rest of the network for That, combined with the handy script from Ben Low, allows all DNS lookups for the internal network to go to the internal DNS servers, routed through the VPN.

Some notes:

  1. On the server side, everything is pretty much normal. I can’t seem to use the “push redirect-gateway” through this system, as a) either the OS X port of OpenVPN doesn’t support it (unlikely), or b) the gateway information is a little borked as it is going over an SSH tunnel. Either way, I just manually add the route to the default gateway.
  2. For some reason, the tap0 driver does not go into DHCP mode by default. You have to force it using the ipconfig program, which is done for you in the script.
  3. On the client side, Mac OS X has a very complex name resolution service. Very cool if you get under the hood, as seen in these two threads on the Apple developer mailing list. The end result is that when you get the DHCP lease from the internal LAN DHCP server, the tap0 device gets some service keys that record the DNS servers pushed from the LAN DHCP server. After the tap0 driver comes up through OpenVPN, you can see this by running scutil from the command line, and executing
    get State:/Network/Service/DHCP-tap0
  4. Now, OS X needs to have a SupplmentalMatchDomains key associated with these DNS entries before it will actually consult them as part of the name resolution service. The script does that for you. You can see its effects after the VPN starts up by running scutil --dns

There you go. Now poke holes in your own Great Corporate Firewall!

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>