For the last 3 years I have been using djbdns on SmartOS and it has all been working great. Recently however, I started looking into DNSSEC and DNSCrypt, which ended up leading me to the OpenNIC Project.

I decided to change my home DNS server setup to forward OpenNIC DNS servers over an encrypted channel as opposed to using OpenDNS like I did with djbdns.

To set this up, I have a zone with dnsmasq and dnscrypt-proxy running

dnsmasq - listens globally on port 53 for incoming DNS requests, answers local domain DNS requests for my network, and forwards the rest to dnscrypt-proxy

- listens globally on port 53 for incoming DNS requests, answers local domain DNS requests for my network, and forwards the rest to dnscrypt-proxy - listens locally on port 5300 for incoming DNS requests from dnsmasq and forwards them securely to an OpenNIC DNS server

Install

To start, install dnsmasq with the following command:

pkgin in dnsmasq

Installing dnscrypt-proxy requires a little bit more work as it is currently not in pkgsrc. To install it, we need to pull in some dependencies.

pkgin in go git

Now we can build dnscrypt-proxy

mkdir -p ~/dnscrypt-proxy cd ~/dnscrypt-proxy && wget https://github.com/jedisct1/dnscrypt-proxy/archive/2.0.16.tar.gz && tar xf 2.0.16.tar.gz && cd dnscrypt-proxy-2.0.16/dnscrypt-proxy && go get go build

We can verify it built by running it with -version

$ ./dnscrypt-proxy -version 2.0.16

And finally we can install it with

cp ./dnscrypt-proxy /opt/local/bin

Configure dnscrypt-proxy

Use the following config to configure dnscrypt-proxy to work with OpenNIC DNS servers. This server will listen on 127.0.0.1:5300 for plaintext requests forwarded from dnsmasq which we’ll configure next.

Save this configuration file to /opt/local/etc/dnscrypt.conf

listen_addresses = ['127.0.0.1:5300'] ipv4_servers = true dnscrypt_servers = true doh_servers = true require_dnssec = true require_nolog = true require_nofilter = true timeout = 2500 keepalive = 30 cert_refresh_delay = 240 fallback_resolver = '9.9.9.9:53' ignore_system_dns = true cache = true cache_size = 512 cache_min_ttl = 600 cache_max_ttl = 86400 cache_neg_min_ttl = 60 cache_neg_max_ttl = 600 [sources] [sources.'opennic'] url = 'http://download.dnscrypt.info/resolvers-list/v2/opennic.md' minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3' cache_file = '/var/tmp/opennic.md' # log queries #[query_log] # format = 'tsv' # file = '/dev/stdout'

Note that I personally don’t log any DNS requests on my home network for privacy reasons, but I’ve left the configuration lines there commented-out as turning on logging can be valuable for debugging purposes when setting up these services.

Because dnscrypt-proxy wasn’t in pkgsrc we will have to create the SMF service ourselves.

~/dnscrypt-proxy.xml

<?xml version='1.0'?> <!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> <service_bundle type= 'manifest' name= 'export' > <service name= 'application/dnscrypt-proxy' type= 'service' version= '0' > <create_default_instance enabled= 'true' /> <dependency name= 'dep0' grouping= 'require_all' restart_on= 'error' type= 'service' > <service_fmri value= 'svc:/milestone/multi-user:default' /> </dependency> <exec_method name= 'start' type= 'method' exec= 'dnscrypt-proxy -config /opt/local/etc/dnscrypt.conf &' timeout_seconds= '10' > <method_context working_directory= '/var/empty' > <method_credential user= 'nobody' group= 'nobody' /> <method_environment> <envvar name= 'PATH' value= '/opt/local/sbin:/opt/local/bin:/opt/custom/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' /> </method_environment> </method_context> </exec_method> <exec_method name= 'stop' type= 'method' exec= ':kill' timeout_seconds= '30' /> <template> <common_name> <loctext xml:lang= 'C' > dnscrypt-proxy </loctext> </common_name> </template> </service> </service_bundle>

And then run the following command to import the service and start it.

svccfg import ~/dnscrypt-proxy.xml

You can verify it’s running with:

$ svcs -p dnscrypt-proxy STATE STIME FMRI online 23:02:53 svc:/application/dnscrypt-proxy:default

Or check the logs with:

$ tail "$(svcs -L dnscrypt-proxy)" [ Jul 7 03:02:53 Executing start method ("dnscrypt-proxy -config /opt/local/etc/dnscrypt.conf &"). ] [ Jul 7 03:02:53 Method "start" exited with status 0. ] [2018-07-07 03:02:53] [NOTICE] Source [/var/tmp/opennic.md] loaded [2018-07-07 03:02:53] [NOTICE] dnscrypt-proxy 2.0.16 [2018-07-07 03:02:53] [NOTICE] Now listening to 127.0.0.1:5300 [UDP] [2018-07-07 03:02:53] [NOTICE] Now listening to 127.0.0.1:5300 [TCP] [2018-07-07 03:02:53] [NOTICE] [opennic-onic] OK (crypto v1) - rtt: 32ms [2018-07-07 03:02:55] [NOTICE] [publicarray-au] TIMEOUT [2018-07-07 03:02:55] [NOTICE] Server with the lowest initial latency: opennic-onic (rtt: 32ms) [2018-07-07 03:02:55] [NOTICE] dnscrypt-proxy is ready - live servers: 1

And finally you can query the server directly with:

$ dig @127.0.0.1 -p 5300 +short daveeddy.com A 165.225.131.147

Configure dnsmasq

Now that dnscrypt-proxy is setup to securely forward requests to OpenNIC DNS servers, we can configure dnsmasq to answer local requests and forward requests to dnscrypt-proxy .

Save this configuration file to /opt/local/etc/dnsmasq.conf

port=53 listen-address=0.0.0.0 bind-interfaces domain-needed proxy-dnssec filterwin2k no-resolv no-poll no-hosts neg-ttl=3600 # Rapture hosts domain=rapture.com local=/rapture.com/ # A records address=/host1.rapture.com/10.0.1.1 address=/host2.rapture.com/10.0.1.2 # PTR records ptr-record=1.1.0.10.in-addr.arpa,"host1.rapture.com" ptr-record=2.1.0.10.in-addr.arpa,"host2.rapture.com" # Forward everything else to dnscrypt server=127.0.0.1#5300 # log queries to stderr #log-queries #log-facility=-

rapture.com is my personal home domain - swap this out for your own personal domain. The address lines will become local A records and the ptr-record lines will become local PTR records. Again, I personally don’t log any queries on my network but the config lines are there commented-out to help with debugging when setting up the service initially.

Start the service with

svcadm enable dnsmasq

You can verify it’s running with:

$ svcs -p dnsmasq STATE STIME FMRI online Jul_05 svc:/pkgsrc/dnsmasq:default Jul_05 39838 dnsmasq

To check that this is working, we can run the same command from configuring dnscrypt-proxy , but this time using port 53 instead of 5300.

$ dig @127.0.0.1 -p 53 +short daveeddy.com A 165.225.131.147 $ dig @127.0.0.1 -p 53 +short host1.rapture.com A 10.0.1.1 $ dig @127.0.0.1 -p 53 +short host2.rapture.com A 10.0.1.2

Conclusion

With these two services in place, any server on the network can set their DNS servers to this zone and get local records as well as public records over an encrypted channel. I have 2 zones (on 2 different physical machines) on my network running these DNS services, and I give out both of their IP addresses as part of DHCP. If you want to test out these servers manually on a different machine you’d have to edit /etc/resolv.conf to look like:

search rapture.com domain rapture.com nameserver 10.0.1.2 nameserver 10.0.1.3

Where 10.0.1.2 and 10.0.1.3 are the IP addresses of the DNS zones on the network. With this config in place your local machine will use these zones as their default resolvers.

Lookup local addresses

$ nslookup host1.rapture.com Server: 10.0.1.2 Address: 10.0.1.2#53 Name: host1.rapture.com Address: 10.0.1.2 $ nslookup 10.0.1.2 2.1.0.10.in-addr.arpa name = host1.rapture.com.

Lookup a bogus local address

$ nslookup foo.rapture.com Server: 10.0.1.2 Address: 10.0.1.2#53 ** server can't find foo.rapture.com: NXDOMAIN

Lookup a public address

$ nslookup daveeddy.com Server: 10.0.1.2 Address: 10.0.1.2#53 Non-authoritative answer: Name: daveeddy.com Address: 165.225.131.147

Lookup an OpenNIC specific address