DNSCrypt on the EdgeRouter Lite

Introduction:

ISP: Comcast
Modem: Motorola SB6141
Router: Ubiquiti EdgeMax Router Lite
EdgeOS System Image: v1.9.7+hotfix.3
WAN Interface: eth0
LAN Interface: eth1 (172.16.0.1)

For this guide, I use the IP address 172.16.0.1 as the one I want dnscrypt-proxy bound to. At this time, I have not found a way to configure dnscrypt-proxy to natively bind to only eth1 and eth2.

Installation:

  1. ssh into your EdgeRouter Lite and sudo to root
  2. mkdir dnscrypt-proxy && cd dnscrypt-proxy
  3. curl --remote-name https://download.dnscrypt.org/dnscrypt-proxy/binaries/linux/dnscrypt-proxy-mips-linux-musl.tar.gz
  4. curl --remote-name https://download.dnscrypt.org/dnscrypt-proxy/binaries/linux/dnscrypt-proxy-mips-linux-musl.tar.gz.minisig
  5. TODO: Figure out how to get minisign working on the ERL, https://jedisct1.github.io/minisign/
  6. tar xvfz dnscrypt-proxy-mips-linux-musl.tar.gz
  7. cd dnscrypt-proxy
  8. ./installer.sh
  9. cd .. && rmdir dnscrypt-proxy/

Configuration:

First off, run /opt/dnscrypt-proxy/mips-linux-musl/bin/dnscrypt-update-resolvers.sh to update your local cache of DNSCrypt Resolvers.

Create the dnscrypt-proxy system user which will run the actual proxy:

useradd --comment 'dnscrypt-proxy system user' --no-create-home --system --user-group --shell /usr/sbin/nologin --home-dir /opt/dnscrypt-proxy dnscrypt-proxy

The following config will start the dnscrypt-proxy on port 153 for us to verify everything works.
/opt/dnscrypt-proxy/dnscrypt-proxy.conf:

ResolverName random
ResolversList /opt/dnscrypt-proxy/mips-linux-musl/.sw/dnscrypt-proxy/share/dnscrypt-proxy/dnscrypt-resolvers.csv
Daemonize no
PidFile /var/run/dnscrypt-proxy.pid
User dnscrypt-proxy
LocalAddress 172.16.0.1:153
LocalCache on
EphemeralKeys off
MaxActiveRequests 250
EDNSPayloadSize 1252
IgnoreTimestamps no
TCPOnly no
QueryLogFile /var/log/dnscrypt-queries.log
LogFile /var/log/dnscrypt-proxy.log
LogLevel 6
# Syslog       off
# SyslogPrefix dnscrypt
BlockIPv6 yes
chown -R dnscrypt-proxy:dnscrypt-proxy /opt/dnscrypt-proxy/
/opt/dnscrypt-proxy/mips-linux-musl/bin/dnscrypt-proxy /opt/dnscrypt-proxy/dnscrypt-proxy.conf

Now, on a client host that can hit the ERL on port 153, issue the following:

dig techsmix.net @172.16.0.1 -p 153

You should see a completed response:

❯ dig techsmix.net @172.16.0.1 -p 153

; <<>> DiG 9.8.3-P1 <<>> techsmix.net @172.16.0.1 -p 153
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55928
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1252
;; QUESTION SECTION:
;techsmix.net.			IN	A

;; ANSWER SECTION:
techsmix.net.		300	IN	A	192.241.192.75

;; Query time: 318 msec
;; SERVER: 172.16.0.1#153(172.16.0.1)
;; WHEN: Sun Sep 17 12:19:55 2017
;; MSG SIZE  rcvd: 57

And in /var/log/dnscrypt-queries.log see a line like:

Sun Sep 17 16:36:16 2017	172.16.0.65	techsmix.net	A

Now, that the test is working let's reconfigure our ERL to only expose dnscrypt-proxy as the internal DNS server.

Edit /opt/dnscrypt-proxy/dnscrypt-proxy.conf so that LocalAddress is 172.16.0.1:53 and Daemonize is yes.

At this point, you should see that the ERL has a dnsmasq process running:

root@main-router:~/dnscrypt-proxy# netstat -tulnp | grep 53
tcp        0      0 0.0.0.0:53              0.0.0.0:*               LISTEN      1489/dnsmasq
tcp        0      0 172.16.0.1:153          0.0.0.0:*               LISTEN      12584/dnscrypt-prox
tcp6       0      0 :::53                   :::*                    LISTEN      1489/dnsmasq
tcp6       0      0 :::49153                :::*                    LISTEN      1497/upnpd
udp        0      0 0.0.0.0:53              0.0.0.0:*                           1489/dnsmasq
udp        0      0 172.16.0.1:153          0.0.0.0:*                           12584/dnscrypt-prox
udp6       0      0 :::53                   :::*                                1489/dnsmasq

Remove all DNS configuration via the ERL's WebUI: https://address.to.erl.here/#Services/DNS

Verify that there's no processes holding port 53 open:

root@main-router:~/dnscrypt-proxy# netstat -tulnp | grep 53
tcp        0      0 172.16.0.1:153          0.0.0.0:*               LISTEN      12584/dnscrypt-prox
tcp6       0      0 :::49153                :::*                    LISTEN      1497/upnpd
udp        0      0 172.16.0.1:153          0.0.0.0:*                           12584/dnscrypt-prox

Kill the old test process and start the real one

ps aux | grep dnscrypt && kill $(cat /var/run/dnscrypt-proxy.pid) &&  /opt/dnscrypt-proxy/mips-linux-musl/bin/dnscrypt-proxy /opt/dnscrypt-proxy/dnscrypt-proxy.conf

And now, verify that dnscrypt-proxy has taken over port 53:

root@main-router:~/dnscrypt-proxy# netstat -tulnp | grep 53
tcp        0      0 172.16.0.1:53           0.0.0.0:*               LISTEN      13493/dnscrypt-prox
tcp6       0      0 :::49153                :::*                    LISTEN      1497/upnpd
udp        0      0 172.16.0.1:53           0.0.0.0:*                           13493/dnscrypt-prox

Assuming your clients on your network are already using your router for DNS, you should immediatly start seeing the queries in /var/log/dnscrypt-queries.log

It's reccomended that you add the following cronjob to keep the local list of DNSCrypt servers updated:

0 * * * * /opt/dnscrypt-proxy/mips-linux-musl/bin/dnscrypt-update-resolvers.sh > /dev/null 2>&1

Finally, to ensure that dnscrypt-proxy starts automatically on your ERL, you'll need to create and enable the SysVinit script. I've modified the files located here: https://github.com/axujen/dnscrypt-install

/etc/init.d/dnscrypt-proxy: https://gist.github.com/jaredledvina/f76d72404df3fb5e3fe9ba978755ac7f

/etc/default/dnscrypt:

# Defaults for dnscrypt initscript
# Optional arguments passed to dnscrypt-proxy
DAEMON_OPTS=""

Now, you'll want to make sure the new init script works so, let's kill the manually run daemon and start it cleanly with this init script:

kill $(cat /var/run/dnscrypt-proxy.pid)
service dnscrypt-proxy start

Again, you should see the dnscrypt-proxy service running and listening on the configured interface and port.

Finally, I use chkconfig to managing the services to run at boot (install it with apt-get install chkconfig):

root@main-router:~# chkconfig -l dnscrypt-proxy
dnscrypt-proxy            0:off  1:off  2:off  3:off  4:off  5:off  6:off

Enable it to start at boot:

root@main-router:~# chkconfig --add dnscrypt-proxy
dnscrypt-proxy            0:off  1:off  2:on   3:on   4:on   5:on   6:off

Optionally, if you'd like some simple Ad blocking via dnscrypt-proxy perform the following:

cd /opt/dnscrypt-proxy && curl --remote-name https://download.dnscrypt.org/blacklists/domains/mybase.txt'

Add this line to /opt/dnscrypt-proxy/dnscrypt-proxy.conf:

'BlackList domains:/opt/dnscrypt-proxy/mybase.txt logfile:/var/log/dnscrypt-blocked.log

Finally, restart dnscrypt-proxy and you should start seeing the deny actions logged to /var/log/dnscrypt-blocked.log.
/var/log/dnscrypt-blocked.log:

root@main-router:/opt/dnscrypt-proxy# tail /var/log/dnscrypt-blocked.log
Sun Sep 17 19:01:55 2017	172.16.0.65	msec.xp1.ru4.com	*ru4.com
4Sun Sep 17 19:01:55 2017	172.16.0.65	msec.xp1.ru4.com	*ru4.com
5Sun Sep 17 19:01:55 2017	172.16.0.65	msec.xp1.ru4.com	*ru4.com
6Sun Sep 17 19:01:55 2017	172.16.0.65	sync-tm.everesttech.net	*everesttech.net
7Sun Sep 17 19:01:55 2017	172.16.0.65	sync-tm.everesttech.net	*everesttech.net
8Sun Sep 17 19:01:55 2017	172.16.0.65	aa.agkn.com	*agkn.com
9Sun Sep 17 19:01:55 2017	172.16.0.65	ag.innovid.com	*innovid.com
10Sun Sep 17 19:01:55 2017	172.16.0.65	ag.innovid.com	*innovid.com

You can add a simple cronjob like the following to keep that file always up to date:

0 1 * * * /usr/bin/curl -s --remote-name https://download.dnscrypt.org/blacklists/domains/mybase.txt > /dev/null 2>&1

There you have it! All of your local network DNS queries are automatically proxied securely using DNSCrypt. Enjoy!

So followups to this article I will hopefully find time to track down and implement:

  • Build ERL .deb packages that include a proper SysVInit script and manages all of the install
  • Configure dnsproxy-crypt to bind to multiple internal only interfaces
  • Locking down DNS traffic such that it must use dnscrypt-proxy to leave the internal network
  • Deploying something like https://github.com/Phillipmartin/gopassivedns to monitor all DNS traffic for anything that doesn't use dnscrypt-proxy.
  • Setup logrotate for the various dnscrypt-proxy log files.