Archives / Search ›

Overriding DNS for domains in OS X Tiger

ACM@UIUC has a bunch of machines behind NAT, which are all in the non-publicly-served domain internal.acm.uiuc.edu, and as I started to need these machines more for my research, I got annoyed at having to do a nested ssh every time I wanted to access them.

The obvious solution was a VPN. I set up OpenVPN on our OpenBSD router box. I’ve done enough OpenVPN evangelism here, so I won’t do more than say it’s worth checking out.

Next came the client side. OpenVPN, at least on Unix platforms, doesn’t automatically modify your nameserver configuration (it used to not modify your routing table either), so I wrote a Python script which automatically edits resolv.conf to add the appropriate nameserver address for internal.acm when you connect, and remove it when you disconnect.

This worked wonderfully until I upgraded to Tiger. OS X 10.4, except for a few tools like host, no longer directly reads /etc/resolv.conf to determine nameservers and search domains. Instead, Tiger has a much more flexible (but rather poorly documented) DNS interface as part of the System Configuration framework. Not to worry, it does keep resolv.conf up to date with the current state of things, but if you change that file, don’t expect Safari, Mail, or even ssh or ping to recognize your changes.

After some poking around and help from a mailing list, I developed a solution. Try running scutil --dns, where you’ll see multiple resolvers, each resolver with its own set of (search) domains and nameservers—it’s like a routing table for DNS. This architecture solves my problem much more elegantly than editing resolv.conf. You can even be connected to multiple VPNs simultaneously, with a private nameserver and associated domains for each VPN, and everything just works™.

I rewrote the script to use this new mechanism, and had been using it a month or so without problems, when I found this article posted at macosxhints.com, which proposes a much more primitive method of altering the nameserver configuration. So I posted a comment saying anyone could email me and I’d send them my script—a sure-fire procrastination tactic, since I was sure nobody would email me immediately, and I planned on cleaning the script up for more general use, just not then because I had several deadlines.

I’ve now had a chance to clean up the script so it is more generically useful. Here is the script with its associated OpenVPN config file.

In order to get the script to work, you’ll need to install PyObjC (has an installer), then the SystemConfiguration wrapper.

If you’re not familiar with using Python’s distutils, to install the wrapper, cd into the SystemConfiguration-0.3 directory, then run python setup.py install. If you get permissions errors (which you shouldn’t on a default OS X install, but…) you might need to sudo python setup.py install instead.

When the VPN is up, OS X Tiger will use the OpenVPN DHCP option-provided nameserver(s) for the corresponding search domains, but will use your existing nameserver(s) for everything else. Use scutil --dns to see one or more resolvers appear after you’re connected, and disappear when you disconnect.

Version 3.0a2 of Tunnelblick, an OS X OpenVPN GUI, now seems to work with this script, as long as you change the cd command in the OpenVPN config file to use an absolute path. It’d be great if it or similar software could adopt this approach automatically, so I didn’t have to write a script at all.

26 comments on “Overriding DNS for domains in OS X Tiger”

  1. jrg
    28 August 2005 | 3:57 PM

    That solution didn’t worked for me.
    I’ve installed TunnerBrick and used this scripts:
    http://openvpn.net/archive/openvpn-users/2005-08/msg00123.html

    Now i’m happy!

  2. 28 August 2005 | 5:48 PM

    Glad it works for you. The method that script uses is extremely fragile, is not recommended by Apple, and does not work with multiple simultaneous VPN connections, however. I haven’t tried it, but it seems that dropping the current connection on VPN disconnect may interrupt existing connections.

    I tried Tunnelblick 3.0a2, and it works slightly better. openvpnstart process hangs, but once I kill it, it seems to connect. It doesn’t cd to my home directory so any relative paths don’t work either. Perhaps 3.0 final will be usable.

    I’ve updated my script so it reads the DHCP options from the environment and can be used as is.

  3. jth
    31 August 2005 | 12:16 AM

    This is excellent information.
    The link to your script is giving me a permission error, so I’m not able to try it. Have you modified it recently? (The link to the config file works fine.)

  4. 31 August 2005 | 8:54 AM

    Whoops. Permissions fixed, thanks.

  5. Wolfgang
    10 October 2005 | 4:20 PM

    can you specify which DHCP options the server should send?
    Thaks a lot,

    Wolfgang

  6. 17 October 2005 | 11:43 PM

    Two quick notes that weren’t obvious to me right away. I was setting this up on my wifes laptop, and you need to have the Developer Tools installed (included on the Mac OS X server). Also, don’t forget to change the permissions on the acm-client.py script. Tunnelblick kept disconnecting on me, and I could figure out why — it was due to acm-client.py not being executable. To fix this, just type “chmod 755 acm-clien.py” in terminal.

    Hope that helps someone! Thanks for the script — works great!

  7. 3 November 2005 | 1:42 PM

    Wolfgang:

    Sorry for the long delay in responding. The server-side options would be something like:

    push “dhcp-option DNS 128.174.251.34”
    push “dhcp-option DOMAIN internal.acm.uiuc.edu”

  8. 28 December 2005 | 1:02 PM

    […] Being a frequent OpenVPN/TunnelBlick user these days, I sometimes need to change my DNS resolver configuration after having established a tunnel. Until very recently I was under the impression any decent Un*x-like OS has its resolver configuration in /etc/resolv.conf. Well, this doesn’t apply for OSX. The resolv.conf in /etc is only a read-only mirror of the resolver configuration; changes in this file will not be propagated back to the resolver. No, this setting needs to be manipulated in a “dynamic store maintainted by configd(8)” [AFP548: Using scutil to set DNS server]. Thank you very much, Apple. UPDATE: The above apparently only applies to Tiger; the issue has been discussed in several other places before […]

  9. Sean Whitney
    9 February 2006 | 6:24 PM

    I’m really trying to get this working, I’ve installed the required software, set the correct permissions and it seems to do something….
    nslookup give the right results, /etc/resolv.conf looks modified.
    However scutils –dns doesn’t show the new DNS servers, and ping fails to resolve the hostname.
    Also what modifications to the script would be required to route ALL DNS queries through the tunnel? For me this is a better solution……

    Sean

  10. Wolfgang
    9 February 2006 | 6:57 PM

    Hi Sean,

    check if the python objc bridge has been installed correctly … there is a file text/test.py in the distributen that should give you at least the hostname (error later on does not matter).

    At (only one) of my systems the bridge was not installed correctly – same effects, no error messages, resolve.conf modified, but not scutil.

    Have fun, Wolfgang

  11. 10 February 2006 | 1:21 PM

    Yes, make sure you’re using the same copy of python to run the script as you did to install into. Fink and DarwinPorts like to install their own Pythons, but they live in their respective worlds and don’t do native Mac GUI stuff, preferring X11. For a Mac-friendly Python, either use the built-in Python 2.3.5 or a framework build of Python 2.4.1.

    The multiple-Python situation on the Mac is a bit confusing, but if you read the mailing list recently, people are working hard to get it better documented.

  12. Derek Spencer
    15 February 2006 | 6:46 PM

    I have your script setup and it always references the DNS server provided by the OpenVPN server. scutil –dns shows the following first 2 entries:

    boston:~ derek$ scutil –dns
    DNS configuration

    resolver #1
    domain : example.com
    search domain[0] : mydomain.com
    search domain[1] : example.com
    nameserver[0] : 192.168.0.1
    order : 200000

    resolver #2
    domain : mydomain.com
    nameserver[0] : 10.1.1.10
    nameserver[1] : 10.1.1.20
    order : 100400

    You state “When the VPN is up, OS X Tiger will use the OpenVPN DHCP option-provided nameserver(s) for the corresponding search domains, but will use your existing nameserver(s) for everything else.”

    Any ideas as to why I’m not experiencing that behavior? I experience this while using both 2.01 and 3.0 RC1 of tunnelblick on 10.4.4.

  13. 16 February 2006 | 10:47 PM

    Not sure. I just tried it on 10.4.5, and it still seems to work. Here’s my scutil output:

    [p7:1013] ~%scutil –dns 10:42PM
    DNS configuration

    resolver #1
    domain : cs.uiuc.edu
    search domain[0] : internal.acm.uiuc.edu
    search domain[1] : sabi.net
    search domain[2] : acm.uiuc.edu
    search domain[3] : cs.uiuc.edu
    search domain[4] : uiuc.edu
    nameserver[0] : 128.174.252.4
    nameserver[1] : 128.174.252.5
    nameserver[2] : 128.174.5.58
    nameserver[3] : 128.174.5.102
    order : 200000

    resolver #2
    domain : internal.acm.uiuc.edu
    nameserver[0] : 128.174.251.34
    order : 100400

    and here are some DNS queries as logged by ethereal:

    [p4:1083] ~%sudo tethereal -i en1 -f “port 53” 10:45PM
    […]
    29 91.827117 128.174.253.190 -> 128.174.251.34 DNS Standard query A wunderdog.internal.acm.uiuc.edu
    30 91.833328 128.174.251.34 -> 128.174.253.190 DNS Standard query response A 10.0.9.30
    31 101.892120 128.174.253.190 -> 128.174.252.4 DNS Standard query A ldap.uiuc.edu
    32 101.896095 128.174.252.4 -> 128.174.253.190 DNS Standard query response CNAME wizard.cso.uiuc.edu A 128.174.5.70

    So it definitely seems to be working. Make sure you’re using something that actually uses OS X’s native DNS stuff to test with (i.e., not ‘host’ or ‘dig’); I used ‘ping’ in my example above.

  14. dok
    15 April 2006 | 6:50 PM

    DId the 10.4.6 update break this for you? I can’t get any address on the network that I am connecting to resolve.

  15. 15 April 2006 | 7:50 PM

    Still works for me under 10.4.6 (though actually we moved all our machines to University-routable space, so I no longer need this!)

    Does the output of scutil –dns look OK?

  16. dok
    16 April 2006 | 1:16 PM

    No. scutil –dns still shows resolvers with DNS information from my cable modem, but not the DNS entries from my VPN connection. The correct DNS information is in “State:/Network/Service/DHCP-tap0/DNS”

    One thing to note is that when I connect to VPN I get error “write to TUN/TAP : Input/output error (code=5)” unless I explicitly set tap0 to use DHCP with “ipconfig set tap0 DHCP”. Any ideas on why I have to force it DHCP mode?

  17. Prasanna
    19 April 2006 | 11:34 PM

    Hey,

    I’m still on Panther :-(. Do you have a copy of the script you used to update resolv.conf? I’m just modifying with a shell script, but I’d like to take a look at what you did to make it work with multiple tunnels.

    Thanks,
    Prasanna.

  18. 20 April 2006 | 12:39 AM

    Hi Prasanna,

    Unfortunately, the Panther DNS stuff doesn’t handle multiple tunnels – you can only have a single list of nameservers.

  19. 18 May 2006 | 9:40 AM

    […] njr 3.0 » Overriding DNS for domains in OS X Tiger (tags: DNS VPN macosx OSX openvpn python) […]

  20. Erik Swanson
    18 July 2006 | 7:31 AM

    Suggestion:

    It should detect the version of OS X, and if it’s Tiger or later, failure to import SystemConfiguration should be considered an error condition. (It took me far too long to realize that although the script was exiting with a success code, it had actually failed to import SystemConfiguration, and hence failed to effect the desired DNS changes.)

  21. 24 January 2007 | 4:45 PM

    Actually, I am unable to install the SystemConfiguration framework on Python 2.4 on OSX 10.4.8 :(

    It doesn’t seem to be compatible with that version of OSX anymore. Anyone had the same problem and was able to fix it?

  22. 25 January 2007 | 12:23 AM

    Fred – you didn’t describe your problem adequately. “X doesn’t work” is almost never enough information. What error message do you get?

  23. dok
    25 January 2007 | 12:37 AM

    I believe the problem is related to the SystemCnnfiguration module not compatible with Intel version of OS X.

  24. 25 January 2007 | 12:51 AM

    Oh, that’s quite possible since I have an Intel Mac. I got the alternative shell script working that Nicholas also posted. Thanks for that as well!

  25. Joe
    27 June 2007 | 9:26 AM

    I use SystemConfiguration on a MacBook Pro (Intel) frequently. It required a hack to the installation. I sent the below to Bob Ippolito. I tried adding it to the Python wiki page he links to from his download page, but that wiki seems totally dysfunctional, so here it is for all who may benefit from it.

    Subject: Pythonic wrapper for the Apple’s SystemConfiguration API
    Date: Dec 16, 2006 12:59 PM

    Bob:

    I was trying to install the SystemConfig python wrapper in order to use Nicholas Riley’s OpenVPN helper script
    (http://njr.sabi.net/2005/08/04/overriding-dns-for-domains-in-os-x-tiger/ )

    I’m not a python expert by any stretch, but when I ran “setup.py install”, I got this error:

    warngin: no files found matching ‘*’ under directory ‘build/lib.macosx-10.3-fat-2.4/SystemConfiguration/UNSystemConfiguration.framework’

    Changing this line in the setup.py script:
    template = [‘recursive-include build/lib.%s-%s/%s/UNSystemConfiguration.framework *’ % (get_platform(), get_python_version(), NAME),],

    to this:
    template = [‘recursive-include build/lib.%s-%s/%s/Default/UNSystemConfiguration.framework *’ % (get_platform(), get_python_version(), NAME),],

    finds the files and eliminates the warning.

    Is this something which should be fixed? Or did I misunderstand something about the install process?
    If I just failed to read some instructions, then forgive my oversight.

  26. Xavier
    24 June 2008 | 4:09 PM

    Hi,

    user of openVPN for quite a long time, I recently changed from XP to OsX, and I’m the only one in the company :-(
    All of a sudden, openVPN does not work anymore, and it seems to be because of a DNS issue. After investigation, I do indeed connect to the office, can ping 192.168.0.2, but cannot go any further.

    I’ve therefore browsed the web and discovered the present article which seems to be exactly about what I need, but I can’t figure out what exactly to do with the provided scripts and how to have them executed (automatically or not). I’m using Leopard.

    Anyone to help a newbie like me have this to work?

    Thanks,
    Xavier

Leave a reply