Saturday, April 15, 2006

OpenVPN + DNS + OS X

OpenVPNOS X has a very cool feature built into to its resolver: /etc/resolver. It allows you to specify different DNS servers for different domains. After creating the /etc/resolver directory, I can create a /etc/resolver/ file with "nameserver" in it. Now, my Mac will use for resolving and whatever my ISP assigned me for everythying else.

When is this useful? runs a MySQL server. My firewall blocks attempts from the Internet to port 3306. But suppose I want to just run a MySQL admin tool from my PowerBook and don't want to mess around with SSH tunnels.

With OpenVPN & /etc/resolver/, I can seemless move from external user to internal user with two clicks.

So, I pop open my PowerBook at Starbucks (their coffee sucks, but they're a common example). I get some DNS server. When I connect to, I'm connecting via its public IP address and connect as anyone else would. But I want to manage my MySQL server or VNC to my wife's computer or access some internal, private web site. But these features are blocked at the firewall.

OpenVPN gets me halfway there. I can connect to my web server's private IP address directly, but I cannot resolve it's IP address. Starbucks' DNS server knows nothing of it.

Using OpenVPN's "up" and "down" script options, I add "up ~/bin/" and "down ~/bin/" to my ~/Library/openvpn.conf file. Then I create my and scripts...
#!/bin/bash -x
mkdir -p /etc/resolver 2> /dev/null
echo "nameserver" > /etc/resolver/
# Added to script Mar 7 2007
killall lookupd
# Removed from script Mar 7 2007
# /usr/sbin/lookupd -flushcache
exit 0
#!/bin/bash -x
rm -Rf /etc/resolver/
# Added to script Mar 7 2007
killall lookupd
# Removed from script Mar 7 2007
# /usr/sbin/lookupd -flushcache
exit 0

NOTE: If /etc/resolver didn't exist since the last time lookupd started (probably when you last rebooted), you have to restart it: sudo killall lookupd ; sudo /usr/sbin/lookupd.

I thought I'd mention, if you haven't tried it yet, Tunnelblick is an AWESOME OpenVPN GUI for Mac. It's what I use.

When I make my OpenVPN connection, I see that a new file, /etc/resolver/, is created. And when I ping, I now see it's private IP address instead of its public IP address. Assuming that I've allowed connections through OpenVPN to my MySQL server, I can just fire up my admin tool and connect directly to my MySQL server -- through the VPN.

Then, when I disconnect, the file automatically gets deleted. The "lookupd -flushcache" command tells OS X's resolver to forget about my internal network and it goes about using the ISP assigned DNS server for again. Talk about easy.

There's only one problem: Suppose my Mac or the OpenVPN connection shutdown un-gracefully. The /etc/resolver/ file does not get deleted. Then, DNS to my domain is fubar'd. To prevent this from happening (only happened once that I can remember), I added "rm -rf /etc/resolver/" to /etc/rc.local (I did have to create rc.local, but /etc/rc is configured to run commands from it upon bootup if the file exists).



  1. [...] I was curious about how to make this go though, and what general solutions people had when I came across a post by Mike Erdely titled OpenVPN + DNS + OS X.  That is exactly what I wanted to do!  As a bonus ge’s even using Tunnelblick. [...]

  2. Just an update:

    I added a "sleep 2" line before the "lookupd -flushcache" lines in each file. It seems that lookupd flushes the cache too soon and sometimes it doesn't "take" without pausing a second.

  3. A method of doing this dynamically, and thus avoiding stale files, is:

    The recommended method for 10.4 ( is:
    * create State:/Network/Service//IPv4, containing at least one sub-key (e.g. Addresses)
    * create State:/Network/Service//DNS, containing the keys ServerAddresses and SupplementalMatchDomains set to the VPN name server and domain, respectively

    e.g. the following example from an example OpenVPN “up” script creates a new Service/.../IPv4 key by “cloning” the physical interface settings (which for the OpenVPN tun interface should only be two keys: Addresses and DestAddresses), and adding the InterfaceName (why?). Then it creates the DNS key as above.

    - $tun_dev ⇒ tun interface (e.g. “tun0”)
    - $ns ⇒ list of name servers, space separated (e.g. “”)
    - $domain ⇒ list of “supplemental” domain names, space separated (e.g. “A.private.lan B.private.lan”)

    $ sudo scutil

  4. Oookay, the actual code went missing... try this:

    Feed this to scutil (e.g. via a shell heredoc):
    get State:/Network/Interface/$tun_dev/IPv4
    d.add InterfaceName $tun_dev
    set State:/Network/Service/openvpn-$tun_dev/IPv4
    d.add ServerAddresses * $ns
    d.add SupplementalMatchDomains * $domain
    set State:/Network/Service/openvpn-$tun_dev/DNS

  5. Thanks for the info, Ben. I've had severe problems when relying on the default "up" script with Tunnelblick. Both PPC and Intel. CPU goes through the roof. Have to kill openvpn and Tunnelblick.

    Creating a simple file in /etc/resolver and then deleting it seems like a much clearer approach even if it isn't the Apple way. It is, however, documented in the man pages.

  6. I've found that sometimes lookupd doesn't reliably see the new DNS host and I end up having to kill and restart lookupd to get that to happen. Actually, I don't have to restart it, the OS will do that for me. So I just changed the script to have a killall lookupd rather than /usr/sbin/lookupd -flushcache. You can take out the sleep that way as well.

  7. And actually, since openvpn by default downgrades itself after initialization to run as nobody/nobody, the script can't delete the /etc/resolver/* file because that's owned by root/wheel. You could have openvpn continue to run as root (it can be configured in openvpn.conf), but that strikes me as a somewhat bad idea...

    So instead, I set up multiple locations in the Network preference panel, then added a Location submenu to the Apple menu using Fruitmenu. Connect to VPN using TunnelBlick, then switch to the other network location using Fruitmenu. All the DNS then goes through the VPN, which is modestly non-optimal, but it's good enough.

  8. I've been using Tunnelblick (actually as you describe with the "killall lookupd" instead of flushcache for exactly the reason you describe. It's always worked in deleting my /etc/resolver/ file.

    So, I connected to a VPN endpoint and ran "ps -auxww | grep openvpn" and it's running as root. My configuration file says "User nobody" so I'm not sure why.

  9. Mike
    These scripts work great.
    I just substituted the killall lookupd line for the flushcache and omitted the sleeps like you've done...

    What would be the best way to temporarily change the machine's own domain name during the connection to equal the new dns name?

    I just want to access remote services in the same way as when at the remote site, so all my connect to server bookmarks would work, etc? ie:

    ping hostname
    instead of

    i tried manually changing it in/etc/resolv.conf and killall lookupd but it remains the same. i think a change of network settings might have to happen from the network gui but i want to integrate this into your script.


  10. Steve,

    If you're like me and really only care about two domains, you can go into your Network System Preferences and add a static domain search string. I have both my work domain and my home domain in there so that I can access resources with just the hostname.

    System Preferences -> Network -> Built-in Ethernet or Airport -> Search Domains.
    List them ",, ..."

  11. thanks mike... the Search Domains trick works perfectly for being able to access simultaneously more than 1 DNS server.

    i also found another up script that works for me that i wanted to share for temporarily assigning a different VPN tunnel DNS:

    it pulls the DNS server from the VPN server config file's push directive.
    i havent yet tested whether i can simultaneously use tunnelblick to connect to 2 different VPNS with 2 different DNS servers.

  12. OK. I can confirm that this method:

    only seems to add 1 DNS to your table at a time.
    upon connection to a simultaneous 2nd tunnel, the 1st tunnel's DNS server gets replaced.

    So... I think that if you only will connect to 1 tunnel at a time, this method is better in that you don't have to hardcode any domain in your script.

    but if you want more than one domain, adding them to OS X's Search Domain GUI seems best

  13. What about this scenario:

    1. I'm using Tunnelblick to connect to the internet from another network. I'm using the DNS setting I received from my remote server connection rather than my local ISP to surf the net, etc.
    2. I'm connecting on a Mac laptop OS 10.4.x using airport.
    3. I've turned on sharing in the Preferences Pane....and I connect my Vonage box to it.
    4. Vonage doesn't work.

    note: when I have the tunnel turned off, the vonage box connects to it's server via the shared connection, but when I turn on the vpn tunnel, it can't "find" it's host server and forever tries to negotiate and "find" the server....

    Is there something with the DNS settings that I need to do so that the Vonage box can resolve it's host server and connect?

  14. For all your DNS needs.

  15. Try the Viscosity OpenVPN client for the Mac, works fantastically!

  16. Thanks for info, but my openvpn connection usually drop about 1-2 hours (not stable connection). How to fix this ?

  17. You probably want to check with the actual OpenVPN group.