As concerns over privacy and government surveillance become more and more relevant, the use of VPNs have become increasingly popular. While VPN software such as OpenVPN encrypts and protects "first-hop" client traffic from the prying eyes of ISPs and the increasingly widespread use of HTTPS protects interception and/or eavesdropping of "last-hop" server-to-server web traffic, the venerable DNS protocol has been slow to follow suit from a security standpoint, until recently. With the advent of DNS-over-TLS, one can now make DNS queries with a level of security and encryption analogous to that of HTTPS, more or less eliminating the possibility that a third party (i.e. your VPN provider or VPS host on which you run your own VPN) can intercept, monitor, or manipulate DNS traffic.
In this how-to, I'll walk through how to install the latest version of the Unbound DNS server (1.8.2 at the time of this writing) on Ubuntu 18.04, configure it as a forwarder to Cloudflare and Quad9 DNS, and configure your OpenVPN server to push the DNS server to clients. It is assumed that you already have a functioning OpenVPN server in place. If not, Digital Ocean provides an excellent tutorial on setting one up.
I know what you're thinking - let's just apt-get unbound and call it a day. Unfortunately, the version of Unbound in the Ubuntu 18.04 repos is 1.6.7, way too old to support some of the directives in the Unbound configuration below. So we'll have to download and compile from source. Visit https://nlnetlabs.nl/projects/unbound/download/ and download the latest source tarball, untar, and run
make install as root. Note that you'll need the
libssl-dev package installed first, as Unbound links against the OpenSSL library. Assuming you didn't change the installation location from the default, the Unbound binaries and configurations should all be under
If you don't have the
ca-certificates package from the Ubuntu repos installed, now would be a good time to do so. Modify the
unbound.conf file so that it contains the following:
server: interface: 0.0.0.0 interface: ::0 tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt prefetch: yes verbosity: 1 access-control: 0.0.0.0/0 refuse access-control: 127.0.0.0/8 allow access-control: 10.0.0.0/8 allow forward-zone: name: "." forward-tls-upstream: yes # Cloudflare DNS forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com forward-addr: 184.108.40.206@853#cloudflare-dns.com forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com forward-addr: 220.127.116.11@853#cloudflare-dns.com # Quad9 forward-addr: 2620:fe::fe@853#dns.quad9.net forward-addr: 18.104.22.168@853#dns.quad9.net forward-addr: 2620:fe::9@853#dns.quad9.net forward-addr: 22.214.171.124@853#dns.quad9.net
This provides a basic but secure configuration of Unbound that tells it to listen on all interfaces (but limit access to localhost and the OpenVPN subnet), verify TLS certificates, and forward all DNS requests upstream to Cloudflare and/or Quad9 via DNS-over-TLS.
You'll also probably want to have Unbound start on boot as a service:
[Unit] Description=Unbound DNS server After=network.target Before=nss-lookup.target Wants=nss-lookup.target [Service] Environment="LD_LIBRARY_PATH=/usr/local/lib" Type=simple Restart=on-failure ExecStartPre=-/usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key -v ExecStart=/usr/local/sbin/unbound -d ExecReload=/usr/local/sbin/unbound-control reload PIDFile=/usr/local/etc/unbound/unbound.pid [Install] WantedBy=multi-user.target
Once your service file is created, run
systemctl daemon-reload to refresh your systemd and have it pick up the new service you just created, followed by
systemctl enable unbound to have it start on boot and
systemctl start unbound to start the service now. If everything is configured correctly,
systemctl status unbound should show something like the following:
root@server:~# systemctl status unbound ● unbound.service - Unbound DNS server Loaded: loaded (/etc/systemd/system/unbound.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2018-12-11 11:00:17 CST; 1 day 11h ago Process: 23369 ExecStartPre=/usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key -v (code=exited, status=0/SUCCESS) Main PID: 23381 (unbound) Tasks: 1 (limit: 2320) CGroup: /system.slice/unbound.service └─23381 /usr/local/sbin/unbound -d Dec 11 11:00:15 server systemd: Starting Unbound DNS server... Dec 11 11:00:17 server unbound-anchor: /usr/local/etc/unbound/root.key has content Dec 11 11:00:17 server unbound-anchor: success: the anchor is ok Dec 11 11:00:17 server systemd: Started Unbound DNS server. Dec 11 11:00:17 server unbound: [23381:0] notice: init module 0: validator Dec 11 11:00:17 server unbound: [23381:0] notice: init module 1: iterator Dec 11 11:00:17 server unbound: [23381:0] info: start of service (unbound 1.8.2).
If you have a firewall running, e.g. iptables, or ufw, make sure to allow access to Unbound on port 53 from your OpenVPN subnet.
Finally, we need to update the OpenVPN server configuration to use our shiny new Unbound DNS server. Open up
/etc/openvpn/server.conf (or wherever your OpenVPN config is stored), and add the line
push "dhcp-option DNS 10.8.0.1" (or whatever the local gateway IP of your OpenVPN server is). If you have any existing DNS servers configured to be pushed to the client, remove these to ensure that the client will only use your Unbound DNS server.
If you're running OpenVPN version 2.3.9 or newer, it's highly recommended to
push "block-outside-dns" as well to prevent DNS leaks.
If OpenVPN has been configured correctly, you should be able to your new DNS server in action on your client.
Enjoy your extra-secure, extra-private VPN!