Solving OpenVPN DNS Issues on Android Clients

(posted in tech)

Out of Date Notice (Nov. 2019): I’m honestly not sure if this is still an issue. My current phone (Moto G7 running Android O) seems to use my VPN’s DNS server when connected without having to do anything special. It could be that newer versions of Android behave better, or (less likely) perhaps Motorola fixed something on their own outside of vanilla Android.

The Problem

OpenVPN servers and clients can configure what DNS server the client should use while connected using a dhcp-option DNS setting (either set in the client config, or pushed to the client from the server). This can be necessary for a number of reasons:

  • you want to use an internal DNS server that resolves local hostnames to local machines on the VPN,
  • you want to avoid potential privacy leakage issues with your normal DNS servers while connected,
  • or your normal DNS servers are inaccessible from the VPN.

The problem is that at present, no OpenVPN clients on Android successfully apply the dhcp-option DNS configuration when connecting to a VPN on Android 5 and above (I haven’t tested on 7/Nougat yet). This is very obvious when connecting to a VPN using a data connection from a carrier that assigns DNS servers that are not accessible from outside of the carrier’s network (such as T-Mobile).

To confirm the issue, with wifi turned off and not connected to your VPN, run the following command in a terminal emulator on the phone (I use ConnectBot): getprop | grep 'net\.dns'. You should see output similar to the following:

[net.dns1]: []
[net.dns2]: []
[net.dns3]: [fd00:976a::9]
[net.dns4]: [fd00:976a::10]

These are the DNS servers assigned to your phone by your mobile carrier (in my case, T-Mobile). They are internal network addresses and cannot be accessed from outside of T-Mobile’s network. If you connect your phone to a wifi network and re-run the command, you’ll see that the values change to reflect the DNS servers assigned by the DHCP server on your wifi network.

[net.dns1]: []
[net.dns2]: []
[net.dns3]: []
[net.dns4]: []

Now turn off wifi, connect to your VPN, and run the command once more. You’ll notice that the values do not update to reflect the DNS servers in your OpenVPN config—they just remain the same as whatever their value was before connecting. In the case of T-Mobile, if you route all traffic through your VPN you’ll notice that DNS lookups take a very long time to resolve, or maybe never resolve at all. This is because your phone is attempting to query the T-Mobile internal DNS servers through the VPN which cannot access them. In my case, the DNS would eventually resolve after several seconds, I think because my VPN ignored IPv6 traffic and the DNS query would eventually fall back on the IPv6 nameservers (net.dns3 and net.dns4 in my example output above). I later added push "route-ipv6 ::/128 ::1" to my OpenVPN server config which seems to successfully force the client into IPv4 mode while connected, and at that point DNS lookups would simply time out.

This is a big problem due to all of the issues listed above. It may be due to a bug in Android’s built-in VPN APIs that most OpenVPN clients use to connect. If you use an OpenVPN client that requires a rooted phone and your own OpenVPN binary this may not be an issue, but it’s not something I tested. All of the currently maintained OpenVPN clients for Android do not require a rooted phone because they use the officially sanctioned VPN APIs.

The most frightening aspect of this problem is that there are probably users on a carriers that use publicly available DNS servers. Those users can connect to their VPNs and merrily go about their way, never realizing that all of their DNS queries are hitting their carrier’s DNS instead of the one configured by their VPN, compromising their assumed privacy without even realizing it.

If you do have a rooted phone, you can further confirm this issue by connecting to your VPN, then issuing the command setprop net.dns1 x.x.x.x as root from your terminal emulator app, where x.x.x.x is your VPN’s preferred DNS server. You may need to restart your browser app, but you’ll find that DNS lookups are fast and your internal domain names will start working.

The Solution

Unfortunately I’m not aware of anything that you can do if your phone is not rooted. My solution involves having a rooted phone plus three apps:

The short version is that Tasker detects when I’m connected to my VPN, then runs a shell command as root that saves the current net.dns1 value and overwrites it with my VPN’s DNS address. Then when Tasker detects that I’ve disconnected, it restores the old net.dns1 value. It essentially works around the bug with the Android VPN service by brute-forcing my DNS settings when I’m connected.

The following guide assumes you already have a basic understanding of how Tasker works.

  1. Create the OpenVPN profile. Use the OpenVPN Tasker Plugin and set the configuration to “Connected.”

  2. Name the new task triggered by the profile something like “Set DNS.” The new task will have (at least) two “Run Shell” actions. The first should run the command getprop net.dns1, and put a variable name in the “Store Output In” field (I used %SAVED_DNS). The second action should run the command setprop net.dns1 x.x.x.x where x.x.x.x is the DNS server you want to use while connected to your VPN. You will need to check the “Use Root” box on this action.

  3. Add an exit task to your OpenVPN profile. Name this something like “Unset DNS.” This new task will only have one “Run Shell” action, with the command setprop net.dns1 %SAVED_DNS. This will need the “Use Root” checkbox checked as well.

  4. Optionally save, set, and restore net.dns2 through net.dns4 in the two tasks you already created by adding more similar “Run Shell” actions. This might be important, because if you only replace net.dns1, the other three servers will still be populated by your carrier’s settings while connected to your VPN, so you could still get some privacy leakage in the case where your DNS queries fail on the primary server and fall back on the others. Be sure to use different variable names for each saved value.

My hope is that at some point there will be an OpenVPN client for Android that properly updates your nameservers based on the actual VPN configuration, but until then this has been working fine for me.

Short Permalink for Attribution: