Why and how I created a private DNS for my Android devices

Nothing really new here, no new findings, all I did just put things together from different tutorials and tips. I just collected the LEGO bricks here.
I like to use my own DNS resolver, which blocks some sites, such as known adware or tracking sites, but also most of the “adult” sites. This is easy in my home LAN, because I have here a dedicated server, which beside many other things, runs a dnsmasq. I have a long list of “bad hosts”, which dnsmasq refuses to resolve, querying them returns zero address immediately. Similar to what Pi-hole does. But dnsmasq’s upstream server isn’t Google DNS, but OpenDNS, so there’s a second layer of “filter”. This works awsome in my home LAN, I can browse adfree without browser addons for example :wink:
The first problem was that this works only “inhouse”, and not on a 4G/LTE internet on my phone on the road far away from my home. But I already have a very cheap VPS, which runs Ubuntu server 16.04.
I installed there my “layers of filters” too, and used it as DNS with our Android devices for a long time via the DNS Pipe app. Now the recent devices have newer Android, and I experience excessive battery drain when DNS pipe works. Fortunately they have a “private DNS” setting, that lets me to use my own DNS server…
But it does not take the IP of a server, it wants a DNS over TLS…
I need NGINX to handle TLS, and correct SSL certificates to be accepted by Android. Self signed doesn’t work here. :roll_eyes:
So I need a more recent version of NGINX, which has the stream plugin, and certification from Let’s encrypt. Everything is performed as root, so I omit the dozens of “sudos”.

add-apt-repository ppa:certbot/certbot
add-apt-repository ppa:nginx/stable
apt update
apt install nginx certbot python-certbot-nginx

That installs NGINX, and certbot. I need the SSL keys, for which I use certbot to acquire.

certbot certonly --nginx

Conversation went like:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter ‘c’ to
cancel): pelda@e.mail


Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory


(A)gree/(C)ancel: A


Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let’s Encrypt project and the non-profit
organization that develops Certbot? We’d like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.


(Y)es/(N)o: N
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated) (Enter ‘c’ to cancel): myexampledomain1.com myexampledomain1.hu
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for myexampledomain1.com
http-01 challenge for myexampledomain1.hu
Waiting for verification…
Cleaning up challenges
IMPORTANT NOTES:

  • Congratulations! Your certificate and chain have been saved at:
    /etc/letsencrypt/live/myexampledomain1.com/fullchain.pem
    Your key file has been saved at:
    /etc/letsencrypt/live/myexampledomain1.com/privkey.pem

I replaced my real domain names here with ‘myexampledomain1.com’ and ‘myexampledomain1.hu’. Of course it’s possible to ask for keys only for one domain, then provide only that one. I needed the SSL for 2 of my domains.

Now in /etc/nginx/sites-available/default sits an example config, I need to add to it:

stream {
server {
listen *:853 ssl;
proxy_pass 127.0.0.1:53;
proxy_connect_timeout 1s;
preread_timeout 2s;
}
ssl_certificate /etc/letsencrypt/live/myexampledomain1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myexampledomain1.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_session_cache shared:DoT:10m;
}

Because of “listen *:853 ssl;” Nginx listens on port 853, terminates there SSL, and
based on “proxy_pass 127.0.0.1:53;” forwards queries to my locally running dnsmasq instance, which handles the query according to my whishes :+1:
Of course my VPS has appropriate A record in the DNS, so dig @8.8.8.8 myexampledomain1.com returns the correct IP, and dig @8.8.8.8 AAAA myexampledomain1.com returns the correct IPv6.
Basically that’s my DNS over TLS server. Sometimes I’ll need to renew my certificates, certbot will help to do that. In case there’s anything other than NGINX using SSL, it has to be notified, restarted, etc. by certbot upon keys renewal, otherwise SSL errors will arise. As I have here a postfix running, I modified it to use Let’s encrypt keys too, but that means it has to be reloaded when certbot renews.
So I added the deploy hook, and forced a renewal immmediately:
certbot renew --force-renewal --deploy-hook "service postfix reload; service nginx reload"
This is a one-time todo, next time the certbot renew will autmatically reload both NGINX and Postfix. In my Android device I can setup private DNS by providing my VPS domain name (myexampledomain1.com in my example).

3 Likes