DNSCrypt on OpenWRT

In response to one of my nameservers having an outage, I needed to figure out how to run multiple instances of dnscrypt-proxy in to provide DNSMasq with a fallback nameserver without losing the privacy benefits of DNSCrypt. Instead of doing a kludgey hard-coded init script for what I needed to do, I researched how to do this the elegant way - through OpenWRT's procd init system. This way, you still get the nice, clean syntax of a UCI configuration file, and the benefits of multiple proxies running at once.

NOTE: After having one of my resolvers go out, I have found that DNSMasq does NOT elegantly fall back to the secondary resolver, thus breaking DNS on my LAN. I'll revisit this later, but keep this in mind. I've personally switched to unbound for my dns and odhcpd for dhcp and have cut dnsmasq out of the picture completely.

References:

DNSCrypt Wiki Page
man page for resolv.conf
dnsmasq's documentation
Other procd-based initscripts as well as the related shell-script libraries for init. (cron, odhcpd, dnsmasq, and scripts in /lib/functions/)

Configuration:


It is assumed that you already have DNSCrypt set up as described in the wiki page prior to starting.
  1. Stop any running dnscrypt resolvers with /etc/init.d/dnscrypt-proxy stop
  2. Replace /etc/init.d/dnscrypt-proxy with the procd-based one that I've created. (wget/save this file)
  3. Add more resolver entries to your /etc/config/dnscrypt-proxy configuration. (example below)
  4. Create /etc/resolv-crypt.conf with a single line options timeout:1. This will reduce DNSMasq's upstream timeout to 1 second, so resolution will be more responsive if your primary nameserver is down.
  5. In your the config dnsmasq section of your /etc/config/dhcp, remove the line option noresolv 1 and add option resolvfile '/etc/resolv-crypt.conf' along with any other resolvers you created in step 3.
  6. Execute /etc/init.d/dnscrypt-proxy start and /etc/init.d/dnsmasq restart to apply your new configuration.
  7. If you did everything correctly, you should now see multiple dnscrypt-proxy resovlers if you run ps.

Example /etc/config/dnscrypt-proxy

Note that each resolver must be on a different port.
config 'dnscrypt-proxy' 'ns1'
        option address         '[::1]'
        option port            '2053'
        option resolver        'opennic-us-wa-ns1-ipv6'
        option resolvers_list  '/etc/dnscrypt-resolvers.csv'

config 'dnscrypt-proxy' 'ns2'
        option address         '[::1]'
        option port            '2054'
        option resolver        'opennic-us-ca-ns17-ipv6'
        option resolvers_list  '/etc/dnscrypt-resolvers.csv'

Example dnsmasq section for /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option authoritative '1'
        option leasefile '/tmp/dhcp.leases'
        list addnhosts '/etc/hosts.blacklist'
        option filterwin2k '1'
        option strictorder '1'
        option resolvfile '/etc/resolv-crypt.conf'
        list server '::1#2053'
        list server '::1#2054'
        list server '/pool.ntp.org/2604:180:1::8d12:1dc'

Init Script

#!/bin/sh /etc/rc.common
# dnscrypt-proxy init-script utilizing procd to support multiple instances at once

START=99
USE_PROCD=1

dnscrypt_instance() {
	config_get address         "$1" 'address'
	config_get port            "$1" 'port'
	config_get resolver        "$1" 'resolver'
	config_get resolvers_list  "$1" 'resolvers_list'

	procd_open_instance
	procd_set_param command "/usr/sbin/dnscrypt-proxy"
	procd_append_param command -a "${address}:${port}"
	procd_append_param command -u nobody
	procd_append_param command -L "${resolvers_list:-'/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv'}"
	procd_append_param command -R "${resolver:-'opendns'}"
	procd_close_instance
}

start_service () {
	config_load dnscrypt-proxy
	config_foreach dnscrypt_instance dnscrypt-proxy
}

service_triggers() {
	procd_add_reload_trigger "dnscrypt-proxy"
}