Ich hatte es bereits angekündigt und nun wollen wir das Ganze mal aufsetzen. Ich orientiere mich hier an einem schönen Artikel von Heise.de, wo die Einrichtung der Forwarder Stubby und DNScrypt für PiHole auf einem Raspberry gezeigt wird. Ich mache das hier im Prinzip analog für den Nameserver Bind9 auf unserem Gateway.

Warum nicht auf dem Raspberry? Nun, Heise hat das schon schön gezeigt und es gibt durchaus Gründe, die gegen den Raspberry sprechen können.
Pi-Hole ist eine feine Sache im Heimnetz, im produktiven Einsatz im Büro mit zentraler Benutzerverwaltung kommt man an einem echten Nameserver wie Bind9 nicht immer vorbei.

Also:

Das Prinzip hatte ich schon unter DNS erläutert, hier nochmal das Schema dazu:

Um Bind zwei Resolver auf der gleichen Maschine zu übergeben müssen wir ein paar Vorbereitungen treffen. Wir haben in der Datei /etc/bind/named.conf.options schon dafür gesorgt, daß Bind nur noch auf der lokalen Schnittstelle ins Heimnetz und dem Loopbackinterface auf Anfragen vom Gateway selber lauscht und dazu Anfragen nur aus den von uns bestimmten Netzen annimmt.

Dort hatten wir auch die uns genehmen Forwarder eingetragen, die für externe Anfragen kontaktiert werden. Die müssen nun auf unsere neuen Resolver Stubby und DNScrypt geändert werden.

Bevor wir aber vorübergehend ohne Nameserver dastehen, installieren wir die beiden schonmal:

root@gateway: ~ # apt install stubby dnscrypt-proxy

Nun startet Debian regelkonform alles was lauffähig ist sofort nach der Installation. In diesem Fall stört das mal wieder und wir halten beide erstmal wieder an:

systemctl stop stubby dnscrypt-proxy

Fangen wir mit der Konfiguration von stubby an und bearbeiten

/etc/stubby/stubby.yml

eine recht lange Datei die uns hier nicht komplett interessiert, deshalb nur die relevanten Auszüge: ( wieder grün kommentiert )

# EDNS0 option for ECS client privacy as described in Section 7.1.2 of
# https://tools.ietf.org/html/rfc7871
edns_client_subnet_private : 1  Zeile 63; hier wird mit 1 ( = eingeschaltet ) der Schutz der Privatsphäre erhöht indem die Lokalisierung der Anfrage verhindert wird. Streamingdienste nutzen dies z.B. um nähergelegene Server für den angeforderten Stream auszuwählen, es kann also durchaus auch sinnvoll sein diesen Wert auf 0 ( = ausgeschaltet ) zu setzen. Deine Entscheidung.

Direkt darunter:

############################# CONNECTION SETTINGS ##############################
 # Set to 1 to instruct stubby to distribute queries across all available name
 # servers - this will use multiple simultaneous connections which can give
 # better performance is most (but not all) cases.
 # Set to 0 to treat the upstreams below as an ordered list and use a single
 # upstream until it becomes unavailable, then use the next one.
 round_robin_upstreams: 1 Sagt mit 1 wir wollen alle Forwarder gleichzeitig ansprechen, könnte schneller sein. Heise behauptet hier, es wäre auch der Privatsphäre zuträglich, da alle Forwarder reihum angesprochen würden und damit mein Nutzerverhalten von einem einzelnen Forwarder schlechter ausgewertet werden könnte. round_robin würde diese These stützen, aber hier steht etwas anderes; nämlich das alle angesprochen werden und die schnellste Antwort genommen wird. DeFacto hiesse das aber, jeder Forwarder könnte mein Nutzerverhalten super auswerten....
0 wiederum fragt zunächst immer nur den ersten Forwarder in der Liste, erst wenn der nicht ( bzw. zu spät, siehe weiter unten timeout ) antwortet, den nächsten Forwarder in der Liste, usf. DeFacto hiesse das also, immer wenn der erste Forwarder langsam oder garnicht antwortet wird meine Namensauflösung auf jeden Fall langsam.

Der nächste wichtige Eintrag in Zeile 120

################################ LISTEN ADDRESS ################################
 # Set the listen addresses for the stubby DAEMON. This specifies localhost IPv4
 # and IPv6. It will listen on port 53 by default. Use <IP_address>@<port> to
 # specify a different port
 listen_addresses:
   - 127.0.0.1
   - 0::1

Damit haben wir ein Problem. 0::1, IPv6 brauchen wir nicht und 127.0.0.1 ohne Port, also Standard UDP 53 ist schon vergeben, dort lauscht unser Bind9 auf Anfragen unseren Servers gateway, z.B. für Updates oder wie gerade eben um die Pakete für die Installation von stubby und dnscrypt zu holen. Wir erinnern uns: In der /etc/apt/sources.list stehen keine IP-Adressen, sondern Namen der Debian Repos.
Wir könnten nun einfach 127.0.0.1@irgendeinPort daraus machen, wissen aber nicht genau auf welchen Ports gateway dort bereits quatscht oder ob 127.0.0.1 überhaupt geht. Ausprobieren zeigt, stubby startet nicht mit der gewünschten Konfig wenn Bind bereits läuft. Aber wir können auch einfach einen neuen, zusätzlichen Loopback aufmachen. Heise macht daraus 127.0.2.2@10053, was ich, da es wunderbar funktioniert, so übernehme

 listen_addresses:
   - 127.0.2.2@10053

Unser Bind9 hatte die Option DNSsec bereits eingeschaltet, bei stubby schalten wir dies in Zeile 129 ein, später in der /etc/bind/named.conf.options aus. Dazu kommentieren wir die Zeile aus und prüfen ob die Datei
/usr/share/dns/root.key
vorhanden ist.

############################### DNSSEC SETTINGS ################################
# Require DNSSEC validation. This will withhold answers with BOGUS DNSSEC
# status and answers that could not be validated (i.e. with DNSSEC status
# INDETERMINATE). Beware that if no DNSSEC trust-anchor is provided, or if
# stubby is not able to fetch and validate the DNSSEC trust-anchor itself,
# (using Zero configuration DNSSEC) stubby will not return answers at all.
# If DNSSEC validation is required, a trust-anchor is also required.
dnssec: GETDNS_EXTENSION_TRUE

Zu DNSsec hatte ich bereits bei der Einrichtung von Bind ausgeführt. Man sollte es nutzen, auch wenn die Umsetzung durch die Provider ein echtes Trauerspiel ist. Wir können dies nur unterstützen, aktiv, indem wir mit unseren Domains konsequent den Provider wechseln wenn er nicht mitmacht. Und? Eben, ich mach das auch nicht, zu faul....

Nun kommen wir zu den Forwardern ( hier Upstreams genannt ) ab Zeile 177:

##################################  UPSTREAMS  ################################
# Specify the list of upstream recursive name servers to send queries to
# In Strict mode upstreams need either a tls_auth_name or a tls_pubkey_pinset
# so the upstream can be authenticated.
# The list below includes all the available test servers but only has the subset
# operated the stubby/getdns developers enabled. You can enable any of the
# others you want to use by uncommenting the relevant section. See:
# https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
# If you don't have IPv6 then comment then out those upstreams.
# In Opportunistic mode they only require an IP address in address_data.
# The information for an upstream can include the following:
# - address_data: IPv4 or IPv6 address of the upstream
#   port: Port for UDP/TCP (default is 53)
#   tls_auth_name: Authentication domain name checked against the server
#                  certificate
#   tls_pubkey_pinset: An SPKI pinset verified against the keys in the server
#                      certificate
#     - digest: Only "sha256" is currently supported
#       value: Base64 encoded value of the sha256 fingerprint of the public
#              key
#   tls_port: Port for TLS (default is 853)
upstream_recursive_servers:
 
Behalten wir den Port 853 im Hinterkopf und weiter:

############################ DEFAULT UPSTREAMS  ################################
####### IPv4 addresses ######
### Test servers ###
# The Surfnet/Sinodun servers
  - address_data: 145.100.185.15
    tls_auth_name: "dnsovertls.sinodun.com"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
  - address_data: 145.100.185.16
    tls_auth_name: "dnsovertls1.sinodun.com"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
# The getdnsapi.net server
  - address_data: 185.49.141.37
    tls_auth_name: "getdnsapi.net"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
####### IPv6 addresses ######
 
Wir sehen hier drei Testserver mit ihren IPv4-Adressen und den benötigten Anmeldedaten für den verschlüsselten Datenaustausch. Alles ab IPv6 addresses kommentieren wir wieder - IPv6 können wir nicht, bzw. will ich nicht solange mein Provider mir keine feste IP gibt....

Dann kommt die Liste der optional Upstreams, mit Erläuterungen zur Funktion: Filtert, kann DNSsec etc. Aktiviere was Dir gefällt, von Let's Encrypt und self-signed Zertificaten würde ich absehen wenn man sich darum nicht wieder kümmern möchte.
Meine Wahl sieht jetzt mal so aus:

## unclear if it honours the edns_client_subnet_private request from stubby)
#  - address_data: 9.9.9.10
#    tls_auth_name: "dns.quad9.net"
## Cloudflare 1.1.1.1 and 1.0.0.1
  - address_data: 1.1.1.1
    tls_auth_name: "cloudflare-dns.com"
  - address_data: 1.0.0.1
    tls_auth_name: "cloudflare-dns.com"
## The Uncensored DNS servers
  - address_data: 89.233.43.71
    tls_auth_name: "unicast.censurfridns.dk"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
## Fondation RESTENA (NREN for Luxembourg)
  - address_data: 158.64.1.29
    tls_auth_name: "kaitain.restena.lu"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: 7ftvIkA+UeN/ktVkovd/7rPZ6mbkhVI7/8HnFJIiLa4=
## Google
#  - address_data: 8.8.8.8

So, damit könnte man stubby starten, aber wir haben da noch einen Port im Hinterkopf.
Damit stubby auch nach Namen fragen kann muss der aufgemacht werden, also 

root@gateway: ~ # nano /etc/shorewall/rules

# rules for stubby
# DNS(ACCEPT)  $FW   net   udp   53  Bind kann jetzt nicht mehr raus
ACCEPT    $FW    net     tcp    853,8443 die 8443 tragen wir gleich für dnscrypt mit ein....
ACCEPT    $FW    net     udp    853.8443

Nun Bind noch die neuen Forwarder eintragen:

root@gateway: ~ # systemctl restart shorewall
root@gateway: ~ # nano /etc/bind/named.conf.options

        forwarders {
         127.0.2.2 port 10053; 127.0.2.1; Auch hier tragen wir den Loopback für dnscrypt gleich mit ein, Heise meint wie gesagt daß Port 53 für Pihole nicht ginge, hier, mit bind9 geht das ganz prima, deshalb lasse ich es so stehen um beides zu zeigen...
          };

und die DNSsec Optionen schalten wir bei Bind jetzt aus, dafür ist ab sofort stubby und dnscrypt zuständig:

        //dnssec-enable yes;
        //dnssec-validation auto;

root@gateway: ~ # systemctl start stubby

gateway: ~ # systemctl status stubby
  stubby.service - DNS Privacy Stub Resolver
    Loaded: loaded (/lib/systemd/system/stubby.service; enabled; vendor preset: enabled)
    Active: active (running) since Mon 2021-02-15 21:16:38 CET; 4s ago
      Docs: https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Daemon+-+Stubby
  Main PID: 10890 (stubby)
     Tasks: 1 (limit: 4915)
    Memory: 1.2M
    CGroup: /system.slice/stubby.service
            └─10890 /usr/bin/stubby
 

 Feb 15 21:16:38 gateway.erstes.netz systemd[1]: Started DNS Privacy Stub Resolver.
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.104929] STUBBY: Read config from file /etc/stubby/stubby.yml
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106094] STUBBY: DNSSEC Validation is ON
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106103] STUBBY: Transport list is:
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106106] STUBBY:   - TLS
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106109] STUBBY: Privacy Usage Profile is Strict (Authentication required)
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106112] STUBBY: (NOTE a Strict Profile only applies when TLS is the ONLY transport!!)
 Feb 15 21:16:38 gateway.erstes.netz stubby[10890]: [20:16:38.106115] STUBBY: Starting DAEMON....

Ok, stubby läuft, nun ein restart von Bind mit der geänderten Konfiguration für den Forwarder

root@gateway: ~ # systemctl restart bind9

und die Kiste läuft und unsere Nameserveranfragen nach draussen sind ab sofort verschlüsselt. Testen wir ein wenig:
Dazu brauchen wir das Paket dnsutils

apt install dns-utils

und setzen eine Anfrage mit dig ab:

gateway: ~ # dig @127.0.2.2 -p 10053 wh1te-rabbit.de
 

 ; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> @127.0.2.2 -p 10053 wh1te-rabbit.de
 ; (1 server found)
 ;; global options: +cmd
 ;; Got answer:
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28550
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 

 ;; OPT PSEUDOSECTION:
 ; EDNS: version: 0, flags: do; udp: 1232
 ;; QUESTION SECTION:
 ;wh1te-rabbit.de. IN A
 

 ;; ANSWER SECTION:
 wh1te-rabbit.de. 150 IN A 81.169.232.111
 

 ;; Query time: 21 msec
 ;; SERVER: 127.0.2.2#10053(127.0.2.2)
 ;; WHEN: Mo Feb 15 22:05:54 CET 2021
 ;; MSG SIZE  rcvd: 75

So soll das sein. Testen wir weiter ob Bind auch brav macht was er soll: Bind lauscht für uns weiter auf Port 53 von 127.0.0.1 auf Anfrage des Servers, also:

gateway: ~ # dig @127.0.0.1 wh1te-rabbit.de
 

; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> @127.0.0.1 wh1te-rabbit.de
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11742
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 27
 

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 4dbd9026d317debc96efc6ff602ae491d70c69186ab8fe1b (good)
;; QUESTION SECTION:
;wh1te-rabbit.de. IN A
 

;; ANSWER SECTION:
wh1te-rabbit.de. 30 IN A 81.169.232.111

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mo Feb 15 22:16:01 CET 2021
;; MSG SIZE  rcvd: 871

Weiter lauscht Bind auf Anfragen des lokalen Netzwerks auf der internen Netzwerkschnittstelle und Port 53, Dein Browser auf deinem PC sollte alle Anfragen ausserhalb der RPZ wie gewohnt beantworten.

Nun zum dnscrypt-proxy. Er fragt ebenfalls verschlüsselt aber via HTTP und auf Port 8443. Sollte stubby ausfallen, fungiert er damit als Fallback.

Installiert haben wir den Dienst schon, starten wollte er auch, sehen wir uns den Status an:

gateway: ~ # systemctl status dnscrypt-proxy
  dnscrypt-proxy.service - DNSCrypt client proxy
    Loaded: loaded (/lib/systemd/system/dnscrypt-proxy.service; enabled; vendor preset: enabled)
    Active: active (running) since Mon 2021-02-15 22:28:32 CET; 5s ago
      Docs: https://github.com/jedisct1/dnscrypt-proxy/wiki
  Main PID: 11705 (dnscrypt-proxy)
     Tasks: 9 (limit: 4915)
    Memory: 9.5M
    CGroup: /system.slice/dnscrypt-proxy.service
            └─11705 /usr/sbin/dnscrypt-proxy -config /etc/dnscrypt-proxy/dnscrypt-proxy.toml
 

 Feb 15 22:28:32 gateway.erstes.netz systemd[1]: Started DNSCrypt client proxy.
 Feb 15 22:28:32 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:32] [NOTICE] Source [/var/cache/dnscrypt-proxy/public-resolvers.md] loaded
 Feb 15 22:28:32 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:32] [NOTICE] dnscrypt-proxy 2.0.19
 Feb 15 22:28:32 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:32] [NOTICE] Wiring systemd TCP socket #0, dnscrypt-proxy.socket, 127.0.2.1:53
 Feb 15 22:28:32 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:32] [NOTICE] Wiring systemd UDP socket #1, dnscrypt-proxy.socket, 127.0.2.1:53
 Feb 15 22:28:33 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:33] [NOTICE] [cloudflare] OK (DoH) - rtt: 52ms
 Feb 15 22:28:33 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:33] [NOTICE] Server with the lowest initial latency: cloudflare (rtt: 52ms)
 Feb 15 22:28:33 gateway.erstes.netz dnscrypt-proxy[11705]: [2021-02-15 22:28:33] [NOTICE] dnscrypt-proxy is ready - live servers: 1

Das sieht gut aus, er läuft direkt auf 127.0.2.1 und Port 53. Die Konfigurationsdatei finden wir unter /etc/dnscrypt-proxy/dnscrypt-proxy.toml

und wir schreiben noch 4 Zeilen dazu:

 # Empty listen_addresses to use systemd socket activation Heisst, wenn wir das leer lassen, nimmt er die besagte 127.0.2.1:53, wollen wir das ändern funktioniert das hier nicht, dazu müssen wir die Datei /lib/systemd/system/dnscrypt-proxy.socket bearbeiten. Im Heise Artikel wird der die Schnittstelle auf 127.0.2.3 und auch Port 10053 geändert, dort soll es aber mit PiHole laufen. Hier geht das auch so ganz wunderbar. Nach einer Änderung ist ein “systemctl deamon-reload” und ein “systemctl restart dnscrypt-proxy.socket” nötig.
 listen_addresses = []
 

 lb_strategy = 'p2' es werden alle 4 Stunden die Resolverlisten aktualisiert und nur die zwei schnellsten genommen, ganz vereinfacht gesagt...mehr infos dazu findest Du auf github, siehe unten...
 require_dnssec = true DNSsec ist aktiviert
 require_nolog = true wir nehmen nur Resolver die nicht loggen
 require_nofilter = true wir nehmen nur Resolver die nicht filtern
 
 server_names = ['cloudflare'] wir nehmen die Nameserver von cloudflare, hier können weitere nach Gusto eingetragen werden, Listen dazu auf https://github.com/DNSCrypt/dnscrypt-proxy
 Beispiel für weitere Eintragungen: server_names = ['cloudflare', 'Name'] 

 [query_log]
   file = '/var/log/dnscrypt-proxy/query.log' Schau hier was dnscrypt so loggt.....
 

 [nx_log]
   file = '/var/log/dnscrypt-proxy/nx.log'
 

 [sources]
   [sources.'public-resolvers']
   url = 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md'
   cache_file = '/var/cache/dnscrypt-proxy/public-resolvers.md'
   minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
   refresh_delay = 72
   prefix = ''

Damit starten wir dnscrypt einmal neu und tragen den Port 8443 in die /etc/shorewall/rules ein, sowie den Forwarder in die /etc/bind/named.conf.options
Beides Neustarten und testen:

gateway: ~ # dig @127.0.2.1 -p 53 wh1te-rabbit.de
 

 ; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> @127.0.2.1 -p 53 wh1te-rabbit.de
 ; (1 server found)
 ;; global options: +cmd
 ;; Got answer:
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64301
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 

 ;; OPT PSEUDOSECTION:
 ; EDNS: version: 0, flags:; udp: 1232
 ;; QUESTION SECTION:
 ;wh1te-rabbit.de. IN A
 

 ;; ANSWER SECTION:
 wh1te-rabbit.de. 149 IN A 81.169.232.111
 

 ;; Query time: 21 msec
 ;; SERVER: 127.0.2.1#53(127.0.2.1)
 ;; WHEN: Mo Feb 15 23:15:49 CET 2021
 ;; MSG SIZE  rcvd: 75

Das sieht gut aus. Wer mag, kann jetzt mit tcpdump schauen was so los ist auf den Loopbackinterfaces:

tcpdump -vv -i lo >dns-watch.txt

Schreibt nun den ganzen Verkehr in die Datei dns-watch.txt bis man den tcpdump wieder mit control+c abbricht. Danach kann man sich die Datei anschauen und sehen wer was auflöst. Die Fehlermeldungen [bad udp cksum 0xfe43 -> 0x9208!] lassen sich mit dem Schalter -K im tcpdump-befehl unterdrücken wenn sie einen stören. Der Fehler rührt daher, das der Mitschnitt vor der Prüfung durch die Schnittstelle erfolgt.

Damit sind wir nun auch auf der DNS-seite voll verschlüsselt und weder unser Internetprovider noch sonst Jemand unbefugtes kann unser Surfverhalten mitschneiden oder auswerten.

Das gefällt mir, und – wo wir schon dabei waren….

next > tcpdump