Blog indexRolling🥎blogPermalink

How to host your own Private DNS for the Android Operating System (Google doesn't want you to know this!)

Jake Thoughts — 07 Dec 2024 21:21:40 -0500

Congratulations, you now have decided to do your own private DNS! Alas, no guide exists that works and in due time (check date) this one also will not work, courtesy of the piece of shit known as Google.

This guide won't hold your hand through every step, but I'll mention specific things you should look out for.

Contents:

  1. Try Quad9 instead
  2. Ok, ok, here's the actual guide
  3. Aquire a VPS

Try Quad9 instead. "dns.quad9.net"

I recommend quad9 when things don't work. And if you're doing private dns, likely you don't want to disclose stuff to Google but quad9 is an ... well, it's not Google. Again, "dns.quad9.net", Android's private dns only does DOT which requires a FQDN. A 'raw' IP address isn't going to work, despite what other outdated guides may tell you.

Ok, ok here's the actual guide

Here are some key points

  1. You MUST have a fully qualified domain name (FQDN).
  2. You MUST serve DNS over TLS on port 853, the default port.
  3. The FQDN MUST link to a 'public' IP address that changes rarely (Android caches the address) and not a Wireguard IP Address or something 'private' even if that address is accessible via a VPN.
  4. If using Let's Encrypt, preferredChain can be set to "ISRG Root X1" which may break accessibility from older devices (short certificate vs long default) which means... 😪 Look, preferredChain is a thing you should look into if you've definitely done everything right but shitty android is giving you useless error message.
  5. (One idea that you could entertain is having two FQDN, one just for shitty Android and one for all your normal fully-updated devices)
  6. If you have VPN on and Andriod gives you a useless error message when trying to put your private DNS in it, try turning the VPN off, then putting in quad9 address, then putting in your address.
  7. Disregard Android's useless error messages.

Basically, you must spend money to do Private DNS on Android. Thanks Google!

This should get you going. No? You need more??? Ok, ok, hope you have a cheap VPS on hand or something and have a static IP Address and have super user access. Also a domain name. You can probably get a free sub domain from freedns.afraid.org.

You have everything you needed to get started?

You have a domain name? You've aquired a VPS with a static ip address? You have super user access? You can install unbound? Good. Good...

So I use unbound, so that's what I'm going to tell you to install. Install it now.

While you're at it, install an HTTP server like Apache or NGINX, configure it so that it can serve your FQDN. Go to your domain's registar and point the A/AAAA records at your VPS. Try connecting to your domain name with a browser, (http://your-domain/) if success you're ready for the next step (be careful with this step as DNS servers cache ip addresses, including bad ones. You'll have to wait a while for it to expire.)

Install certbot AND the plugin for your HTTP server. I recall previously that I had an issue with certbot where it's default Certificate Authority it tried to use was ZeroSSL who requires an account just to create a cert... I did not like that at all. There is a way to switch if you want. Run certbot (maybe/maybe not with `--preferred-chain "ISRG Root X1"` if using Let's Encrypt) and let it do it's thing. Pick the right choices. Your domain should now be accessible via https (that is, https://your-domain/).

Unbound

Unbound has pretty good defaults..... on Debian I mean. Uncertain about other distros. (Debian users absolutely should read the AppArmor section)

Create this file: /etc/unbound/unbound.conf.d/enable-tls.conf and put the following in it:

/etc/unbound/unbound.conf.d/enable-tls.conf
server:
        tls-service-key: /etc/letsencrypt/live/your-domain/privkey.pem
        tls-service-pem: /etc/letsencrypt/live/your-domain/fullchain.pem
        tls-port: 853
        interface: ::0@853
        interface: 0.0.0.0@853

Unfortnately, your service must be accessible to the entire world so you can do Private DNS. Thanks Google! (You likely cannot predict what your IP address will be when using 4G/5G.) Create /etc/unbound/unbound.conf.d/world-access.conf

/etc/unbound/unbound.conf.d/world-access.conf
server:
        access-control: 0.0.0.0/0 allow
        access-control: ::0/0 allow

Because unbound is now world accessible, rate-limiting it is a good idea: /etc/unbound/unbound.conf.d/rate-limit.conf (maybe lower it when you've got something good)

/etc/unbound/unbound.conf.d/rate-limit.conf
server:
        ratelimit: 1000
        ip-ratelimit: 10

You might also run into an issue where unbound can't start because systemd is listening on the ports you want to listen on. Or maybe systemd is listening on 127.0.0.53:53... In that case, don't worry about it I guess.

Ok, on your phone, put your-domain in the Private DNS option and cross your fingers.

... If using Let's Encrypt, try without/with the --preferred-chain option if it doesn't work. Different phones have vendors that update it properly or don't give a shit (to make you buy the next phone). Yeah, you'll have to --force certbot to create a new cert for a domain you JUST created a cert for. Or have two FQDNs, one just for Android and the other for everything else... Thanks Google/shit Vendors!

Now that've done all that... Perhaps you should look into locking down unbound. Make it so that only port 853 is accessible to the world, if it isn't by default to reduce the chance of your service being used in an amplification attack for normal DNS (with DNS over TLS this basically cannot happen). I personally put mine in a chroot using unbound's config options.

To be clear; installing unbound doesn't mean that your VPS will use unbound by default. Your system typically uses what is in /etc/resolv.conf. You can point that at 127.0.0.1 or something but I found that something kept modifying that file so I had to decided to make it immutable. For my use case this was needed, for yours, maybe not!

AppArmor

On Debian, if you install unbound, it comes with an AppArmor config! Nifty suprise! While nice, it also prevents reading the .pem needed for DOT.

/etc/apparmor.d/usr.sbin.unbound has some stuff in it, at the bottom before the '}' add the following:
/etc/apparmor.d/usr/sbin/unbound (partial)
	...

	/etc/letsencrypt/live/** r,    # feel free to lock this further down to your-domain
	/etc/letsencrypt/archive/** r, # feel free to lock this further down to your-domain
}

One thing I strongly dislike about apparmor is that if a program runs into an issue where it can't read a file, you just get a 'permission denied' which makes you feel like a schizophenic person when the path absolutely exists and unbound, before dropping permissions, is root.

Make sure to run apparmor_parser --replace /etc/apparmor.d/usr.sbin/unbound so unbound can read those .pem files.