pages tagged dnssecFeeding the Cloudhttps://feeding.cloud.geek.nz/tags/dnssec/Feeding the Cloudikiwiki2023-10-13T18:03:00ZUsing DNSSEC and DNSCrypt in Debianhttps://feeding.cloud.geek.nz/posts/using-dnssec-and-dnscrypt-in-debian/
<a href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
2023-10-01T21:42:31Z2016-04-26T03:00:00Z
<p>While there is real progress being made towards
<a href="https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/">eliminating insecure HTTP traffic</a>,
<a href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a> is a fundamental
Internet service that still usually relies on unauthenticated cleartext.
There are however a few efforts to try and fix this problem. Here is the
setup I use on my Debian laptop to make use of both
<a href="http://www.dnssec.net/">DNSSEC</a> and <a href="https://dnscrypt.info/">DNSCrypt</a>.</p>
<h1 id="DNSCrypt">DNSCrypt</h1>
<p>DNSCrypt was created to enable end-users to encrypt the traffic between
themselves and their chosen DNS resolver.</p>
<p>To switch away from your ISP's default DNS resolver to a DNSCrypt resolver,
simply install the <a href="https://packages.debian.org/stretch/dnscrypt-proxy"><code>dnscrypt-proxy</code> package</a> and then
set it as the default resolver either in <code>/etc/resolv.conf</code>:</p>
<pre><code>nameserver 127.0.2.1
</code></pre>
<p>if you are using a static network configuration or in
<code>/etc/dhcp/dhclient.conf</code>:</p>
<pre><code>supersede domain-name-servers 127.0.2.1;
</code></pre>
<p>if you rely on dynamic network configuration via <a href="https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol">DHCP</a>.</p>
<p>There are two things you might want to keep in mind when choosing your
<a href="https://github.com/DNSCrypt/dnscrypt-resolvers/tree/master/v1">DNSCrypt resolver</a>:</p>
<ul>
<li>whether or not they keep any logs of the DNS traffic</li>
<li>whether or not they support DNSSEC</li>
</ul>
<p>I have personally selected a resolver located in Iceland by setting the
following in <code>/etc/default/dnscrypt-proxy</code>:</p>
<pre><code>DNSCRYPT_PROXY_RESOLVER_NAME=ns0.dnscrypt.is
</code></pre>
<h1 id="DNSSEC">DNSSEC</h1>
<p>While DNSCrypt protects the confidentiality of our DNS queries, it doesn't
give us any assurance that the results of such queries are the right ones.
In order to authenticate results in that way and prevent DNS poisoning, a
hierarchical cryptographic system was created: DNSSEC.</p>
<p>In order to enable it, I have <a href="https://feeding.cloud.geek.nz/posts/setting-up-your-own-dnssec-aware/">setup a local unbound DNSSEC
resolver</a>
on my machine and pointed <code>/etc/resolv.conf</code> (or
<code>/etc/dhcp/dhclient.conf</code>) to my unbound installation at <code>127.0.0.1</code>.</p>
<p>Then I put the following in <code>/etc/unbound/unbound.conf.d/dnscrypt.conf</code>:</p>
<pre><code>server:
# Remove localhost from the donotquery list
do-not-query-localhost: no
forward-zone:
name: "."
forward-addr: 127.0.2.1@53
</code></pre>
<p>to stop unbound from resolving DNS directly and to instead go through the
encrypted DNSCrypt proxy.</p>
<h1 id="Reliability">Reliability</h1>
<p>In my experience, unbound and dnscrypt-proxy are fairly reliable but they
eventually get confused (presumably) by network changes and start returning
errors.</p>
<p>The ugly but dependable work-around I have found is to create a cronjob at
<code>/etc/cron.d/restart-dns.conf</code> that restarts both services once a day:</p>
<pre><code>0 3 * * * root /usr/sbin/service dnscrypt-proxy restart
1 3 * * * root /usr/sbin/service unbound restart
</code></pre>
<h1 id="Captive_portals">Captive portals</h1>
<p>The one remaining problem I need to solve has to do with
<a href="https://en.wikipedia.org/wiki/Captive_portal">captive portals</a>. This can be
quite annoying when travelling because it requires me to use the portal's
DNS resolver in order to connect to the splash screen that unlocks the wifi
connection.</p>
<p>The
<a href="https://packages.debian.org/stable/dnssec-trigger"><code>dnssec-trigger</code> package</a>
looked promising but when I tried it on my <code>jessie</code> laptop, it wasn't
particularly reliable.</p>
<p>My temporary work-around is to comment out this line in
<code>/etc/dhcp/dhclient.conf</code> whenever I need to connect to such annoying wifi
networks:</p>
<pre><code>#supersede domain-name-servers 127.0.0.1;
</code></pre>
<p>If you've found a better solution to this problem, please leave a comment!</p>
Setting up your own DNSSEC-aware resolver using Unboundhttps://feeding.cloud.geek.nz/posts/setting-up-your-own-dnssec-aware/
<a href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
2023-10-13T18:03:00Z2010-09-12T06:00:00Z
<p>Now that the root DNS servers are <a href="http://www.root-dnssec.org/2010/07/16/status-update-2010-07-16/">signed,</a> I thought it was time I started using <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Dnssec">DNSSEC</a> on my own PC. However, not wanting to wait for my ISP to enable it, I decided to setup a private recursive DNS resolver for myself using <a href="http://unbound.net/">Unbound</a>.</p>
<h2 id="Installing_Unbound">Installing Unbound</h2>
<p>Being already packaged in <a href="http://packages.debian.org/source/unstable/unbound">Debian</a> and <a href="https://launchpad.net/ubuntu/+source/unbound">Ubuntu</a>, unbound is only an <code>apt-get</code> away:</p>
<pre><code>apt install unbound ca-certificates
</code></pre>
<h2 id="Optional_settings">Optional settings</h2>
<p>In <code>/etc/unbound/unbound.conf.d/francois.conf</code>, I enabled the following security options:</p>
<pre><code>server:
harden-below-nxdomain: yes
harden-referral-path: yes
harden-algo-downgrade: no # false positives with improperly configured zones
use-caps-for-id: no # makes lots of queries fail
hide-identity: yes
hide-version: yes
private-address: 10.0.0.0/8
private-address: 100.64.0.0/10
private-address: 127.0.0.0/8
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: fc00::/7
private-address: fe80::/10
private-address: ::ffff:0:0/96
module-config: "validator iterator" # disable EDNS client subnet support
</code></pre>
<p>and turned on prefetching to hopefully keep in cache the sites I visit regularly:</p>
<pre><code>server:
prefetch: yes
prefetch-key: yes
msg-cache-size: 128k
msg-cache-slabs: 2
rrset-cache-size: 8m
rrset-cache-slabs: 2
key-cache-size: 32m
key-cache-slabs: 2
cache-min-ttl: 3600
num-threads: 2
</code></pre>
<p>Finally, I also restricted the server to the local machine:</p>
<pre><code>server:
interface: 127.0.0.1
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.1/32 allow
</code></pre>
<p>and increased the amount of debugging information:</p>
<pre><code>server:
val-log-level: 2
use-syslog: yes
verbosity: 1
</code></pre>
<p>before running <code>sudo unbound-control-setup</code> to generate the necessary keys.</p>
<p>Once unbound is restarted (<code>sudo service unbound restart</code>) stats can be queried to make sure that the DNS resolver is working:</p>
<pre><code>unbound-control stats
</code></pre>
<h2 id="Overriding_DHCP_settings">Overriding DHCP settings</h2>
<p>In order to use my own unbound server for DNS lookups and not the one received via <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Dhcp">DHCP</a>, I added this line to <code>/etc/dhcp/dhclient.conf</code>:</p>
<pre><code>supersede domain-name-servers 127.0.0.1;
</code></pre>
<p>and restarted dhclient:</p>
<pre><code>sudo killall dhclient
sudo killall dhclient
sudo /etc/init.d/network-manager restart
</code></pre>
<p>If you're not using DHCP, then you simply need to put this in your <code>/etc/resolv.conf</code>:</p>
<pre><code>nameserver 127.0.0.1
</code></pre>
<p>or on more recent distros, the following in <code>/etc/systemd/resolved.conf</code>:</p>
<pre><code>[Resolve]
DNS=127.0.0.1
DNSSEC=no
</code></pre>
<p>Yes, you need <code>DNSSEC=no</code> because otherwise it will break insecure
delegations and you'll see messages like this one in your logs:</p>
<pre><code>systemd-resolved[1161]: DNSSEC validation failed for question dyn.fmarier.org IN SOA: no-signature
</code></pre>
<p>You can test that
<a href="https://www.freedesktop.org/wiki/Software/systemd/resolved/">systemd-resolved</a>
is configured properly using:</p>
<pre><code>systemd-resolve --status
</code></pre>
<h2 id="Testing_DNSSEC_resolution">Testing DNSSEC resolution</h2>
<p>Once everything is configured properly, the best way I found to test that this setup was actually working is to use a web browser to visit these sites:</p>
<ul>
<li><a href="http://www.dnssec.cz/">http://www.dnssec.cz/</a> should show a green key</li>
<li><a href="http://www.rhybar.cz/">http://www.rhybar.cz/</a> should not be reachable</li>
<li><a href="https://wander.science/projects/dns/dnssec-resolver-test/">https://wander.science/projects/dns/dnssec-resolver-test/</a></li>
</ul>
<p>and using dig:</p>
<pre><code>$ dig +dnssec A www.dnssec.cz | grep ad
;; flags: qr rd ra <b>ad</b>; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1
</code></pre>
<p>Are there any other ways of making sure that DNSSEC is fully functional?</p>
<h2>Using DNS-over-TLS using Cloudflare's <code>1.1.1.1</code></h2>
<p>In order to make use of <a href="https://en.wikipedia.org/wiki/DNS_over_TLS">DNS over
TLS</a> and effectively hide DNS
queries from anybody looking at your network traffic, one option is to
forward your queries to <a href="https://cloudflare-dns.com">Cloudflare's
<code>1.1.1.1</code></a>:</p>
<pre><code>server:
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
forward-zone:
name: "."
forward-tls-upstream: yes
# Cloudflare DNS
forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
</code></pre>
<p>While Unbound appears to support DNS over TLS natively, it's not clear to me
that it will connect to DNS servers over TLS while doing a recursive name
resolution. Additionally, it will leak queries to non-encrypted servers to
your ISP and other potential on-path attackers. Therefore, forwarding
traffic to a non-logging trusted recursive resolver appears to be the best
solution at the moment.</p>
<p>To test that DNS queries are being correctly forwarded to Cloudflare, use
their <a href="https://1.1.1.1/help">official test page</a>.</p>
<h2 id="Integration_with_OpenVPN">Integration with OpenVPN</h2>
<p>If you are <a href="https://feeding.cloud.geek.nz/posts/creating-a-linode-based-vpn-setup-using_openvpn_on_debian_or_ubuntu/">running your own OpenVPN server</a>,
you can tell clients to connect to the local unbound DNS client by putting the following in <code>/etc/unbound/unbound.conf.d/openvpn.conf</code>:</p>
<pre><code>server:
interface: 127.0.0.1
interface: 10.8.0.1
interface: 10.8.0.1@853
access-control: 127.0.0.1 allow
access-control: 10.8.0.1/24 allow
tls-service-key: /etc/letsencrypt/live/hafnarfjordur.fmarier.org/privkey.pem
tls-service-pem: /etc/letsencrypt/live/hafnarfjordur.fmarier.org/fullchain.pem
tls-port: 853
</code></pre>
<p>and acquiring the necessary <a href="https://letsencrypt.org/">Let's Encrypt</a> TLS
certificates using <a href="https://certbot.eff.org/">Certbot</a>. The
<a href="https://en.wikipedia.org/wiki/DNS_over_TLS">DNS over TLS</a> option is
used automatically by certain VPN clients (e.g. Android) who will try to
upgrade to secure DNS automatically.</p>
<p>If you are using AppArmor, then you'll need to put the following in
<code>/etc/apparmor.d/local/usr.sbin.unbound</code> to ensure that Unbound can read
the TLS cert it needs:</p>
<pre><code>/etc/letsencrypt/archive/** r,
/etc/letsencrypt/live/** r,
</code></pre>
<p>and then run this:</p>
<pre><code>apparmor_parser --replace /etc/apparmor.d/usr.sbin.unbound
</code></pre>
<p>Then put the following in <code>/etc/openvpn/server.conf</code>:</p>
<pre><code>push "dhcp-option DNS 10.8.0.1"
push "register-dns"
</code></pre>
<p>and open the following ports on your firewall (typically <code>/etc/network/iptables.up.rules</code> on Debian):</p>
<pre><code>-A INPUT -p udp --dport 53 -s 10.8.0.0/24 -d 10.8.0.1 -j ACCEPT
-A INPUT -p tcp --dport 53 -s 10.8.0.0/24 -d 10.8.0.1 -j ACCEPT
-A INPUT -p tcp --dport 853 -s 10.8.0.0/24 -d 10.8.0.1 -j ACCEPT
</code></pre>
<p>before restarting both services:</p>
<pre><code>systemctl restart unbound.service openvpn.service
</code></pre>
<h2 id="Work-around_for_systemd-networkd">Work-around for systemd-networkd</h2>
<p>If you're having problems with unbound attempting to start before
systemd-networkd has finished bringing up the network interfaces, then you
may find this work-around useful.</p>
<p>Start by installing these packages:</p>
<pre><code>apt install networkd-dispatcher moreutils
</code></pre>
<p>and then put the following script in <code>/etc/networkd-dispatcher/routable.d/unbound-local</code>:</p>
<pre><code>#!/bin/sh
LOGFILE=/var/log/unbound-local.log
if [ "$IFACE" = lo ]; then
echo "$0: ignoring $IFACE for \`$STATE'" | ts >> $LOGFILE
exit 0
fi
case "$STATE" in
routable)
echo "$0: restarting unbound because of $IFACE" | ts >> $LOGFILE
systemctl stop unbound.service 2>&1 | ts >> $LOGFILE
sleep 5 # hack around unbound's rate limiter
systemctl start unbound.service 2>&1 | ts >> $LOGFILE
;;
*)
echo "$0: nothing to do with $IFACE for \`$STATE'" | ts >> $LOGFILE
;;
esac
exit 0
</code></pre>
<p>before making it executable:</p>
<pre><code>chmod a+x /etc/networkd-dispatcher/routable.d/unbound-local
</code></pre>
<p>Finally, create a new <code>/etc/logrotate.d/unbound-local</code> file to ensure that
the log file does not grow unbounded:</p>
<pre><code>/var/log/unbound-local.log {
monthly
rotate 1
nocreate
nomail
noolddir
notifempty
missingok
}
</code></pre>