Encrypted DNS with dnsmasq and dnscrypt-proxy on SmartOS
Posted by Dave Eddy on Jul 07 2018 - tags: techFor 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 todnscrypt-proxy
dnscrypt-proxy
- listens locally on port 5300 for incoming DNS requests fromdnsmasq
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
$ nslookup grep.geek
Server: 10.0.1.2
Address: 10.0.1.2#53
Name: grep.geek
Address: 161.97.219.84
Name: grep.geek
Address: 2001:470:4212:1::254