Er draait op dit moment een DNS-resolver op je machine. Je hebt hem niet geïnstalleerd, je hebt hem waarschijnlijk nooit geconfigureerd, en hij beantwoordt elke naamlookup die de bak doet. Op Fedora, Ubuntu en de meeste desktop-Linux is dat systemd-resolved, en hij zit daar stilletjes sinds de dag dat het systeem werd geïnstalleerd.

Hij kan drie dingen die het waard zijn om te hebben. Hij cachet, zodat dezelfde lookup niet twee keer het netwerk over hoeft. Hij valideert DNSSEC, zodat een vervalst antwoord wordt afgewezen in plaats van geloofd. En hij spreekt DNS over TLS, zodat het lokale netwerk niet elke naam kan meelezen die je opvraagt.

Standaard doet hij precies één daarvan. De andere twee staan uit, en op sommige distributies staan ze al op compileertijd uit, wat betekent dat de instelling die jij zou veranderen niet eens de instelling is die het besliste. Een validator die nooit valideert heeft geen enkel nut.

Dit is wat het ding werkelijk doet, gemeten op een draaiende Fedora 44-bak met systemd 259, en wat er verandert als je de andere twee aanzet. Inclusief wat je distributie op compileertijd voor je besloot, en hoeveel daarvan je gewoon kunt overrulen.

Wat je lookups eigenlijk beantwoordt

Begin met het eerlijke plaatje, want “het gebruikt /etc/resolv.conf” is op deze systemen al jaren niet meer waar.

systemd-resolved biedt zichzelf op vier verschillende manieren aan, en welke een programma gebruikt bepaalt wat het terugkrijgt.1 Het glibc-pad is nss-resolve, ingehaakt via /etc/nsswitch.conf. De native paden zijn D-Bus en Varlink, die het DNSSEC-oordeel en de interface-scope meedragen die getaddrinfo op geen enkele manier kan uitdrukken. En dan de stub-listener, een echte DNS-server op de loopback voor alles wat rauwe DNS spreekt en van niets hierboven weet.

Vier deuren, één daemon.

Op deze machine ziet die bedrading er zo uit:

$ ls -l /etc/resolv.conf
lrwxrwxrwx. 1 root root 39 May 30 13:56 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

$ cat /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trust-ad
search damiendye.uk

$ grep ^hosts: /etc/nsswitch.conf
hosts:      files myhostname mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns

resolve in die regel is nss-resolve. dns erachter is de traditionele nss-dns, die daar als terugval zit en alleen afgaat als resolved helemaal niet draait.

Vier manieren naar binnen, één daemon, en een routeringsbeslissing voordat er iets de bak verlaatHOE EEN PROGRAMMA VRAAGTWAT DE DAEMON DOETWAAR HET HEEN GAATnss-resolveglibc, geen oordeelD-Bus, Varlinknative, volledig oordeel127.0.0.53de volledige stub127.0.0.54de proxystubsystemd-resolved1. hosts en synthetisch2. cache3. DNSSEC-validator4. routering5. transportde proxystub slaat 2 en 3 overLLMNR op 5355upstream-DNS
Vier manieren naar binnen, één daemon, en een routeringsbeslissing voordat er iets de bak verlaat

Er zijn twee stub-listeners, niet één

Iedereen kent 127.0.0.53. Minder mensen weten dat er een tweede is.

$ ss -lntup | grep ':53 '
udp   UNCONN 0  0     127.0.0.54:53   0.0.0.0:*
udp   UNCONN 0  0  127.0.0.53%lo:53   0.0.0.0:*
tcp   LISTEN 0  4096  127.0.0.54:53   0.0.0.0:*
tcp   LISTEN 0  4096 127.0.0.53%lo:53 0.0.0.0:*

Het zijn niet twee adressen voor hetzelfde ding. De tweede doet met opzet minder:2

127.0.0.53127.0.0.54
RolDe volledige lokale resolverAlleen proxymodus
CacheJaNee
DNSSEC-validatieJa, als het aanstaatNooit
LLMNR en Multicast DNSJaNee
Synthetische namen (localhost, _gateway)JaNee
Upgradet naar DNS over TLSJaJa
Synthetische naam ervoor_localdnsstub_localdnsproxy
Je krijgt terugWat resolved beslootWat de upstream zei

De documentatie is bot over de tweede kolom. Hij zal “pass most DNS messages relatively unmodified to the current upstream DNS servers and back, but not try to process the messages locally, and hence does not validate DNSSEC, or offer up LLMNR/MulticastDNS”.2

Dat maakt 127.0.0.54 het juiste doel voor een programma dat zijn eigen validatie wil doen, of dat het rauwe upstream-antwoord nodig heeft in plaats van resolveds interpretatie ervan.

Beide adressen hebben synthetische namen, _localdnsstub en _localdnsproxy, die zonder enige configuratie resolven.

Vier modi voor /etc/resolv.conf, niet drie

De modus wordt automatisch afgeleid uit wat het bestand is, en er zijn er vier.1

/etc/resolv.conf isWat het betekentClients die NSS omzeilen
symlink naar run/systemd/resolve/stub-resolv.confDe aanbevolen modus. Noemt 127.0.0.53 en de actuele zoekdomeinenGaan via resolved
symlink naar /usr/lib/systemd/resolv.confStatisch, noemt 127.0.0.53, draagt geen zoekdomeinenGaan via resolved
symlink naar run/systemd/resolve/resolv.confNoemt de echte upstreamservers, wordt actueel gehoudenOmzeilen resolved volledig
een echt bestand dat iets anders beheertresolved leest het als afnemer, niet als leverancierOmzeilen resolved volledig

Die derde rij zet mensen op het verkeerde been. Het lijkt de nette optie, het wordt bijgehouden, en het betekent stilletjes dat elk programma dat resolv.conf rechtstreeks leest met de resolver van je ISP praat zonder cache, zonder validatie en zonder versleuteling, wat je ook in resolved.conf hebt ingesteld.

De optie trust-ad in het bestand hierboven doet er ook toe. Zonder die optie haalt glibc de AD-bit van het antwoord af voordat jouw programma het ziet, op de redelijke grond dat de bewering van een willekeurige resolver dat hij iets gevalideerd heeft niets waard is. Met 127.0.0.53 als naamserver komt die bewering van je eigen machine, dus trust-ad klopt hier en resolved schrijft hem voor je erin.

Over de systemd-complottheorieën

Vóór alle details, want dit is het onderwerp waar het altijd opduikt.

Als je bezwaar tegen wat volgt een theorie is over Lennart Poettering persoonlijk, of over de motieven van Red Hat, of over systemd als complot om je iets af te pakken, dan kun je ophoepelen, want ik heb geen zin om naar je verzinsels te luisteren.

Alles in dit bericht kwam van een draaiende machine: de broncode, de buildvlaggen, de geleverde binary, de manpages en het gemeten gedrag. Bij elke bewering staat een commando dat je zelf kunt draaien om het na te kijken. De buildvlaggen zijn openbaar. De broncode is openbaar. De ene standaardwaarde die ik werkelijk verkeerd vind, is gekozen door mensen die hun redenering hebben opgeschreven waar iedereen hem kan lezen en er ruzie over kan maken, wat precies is wat ik verderop doe.

Dat is een stuk meer transparantie dan je krijgt van de meeste software die je zonder een woord van klacht draait.

Kom met bewijs of hou op.

De cache is het deel dat gewoon werkt

Dit is de ene functie die overal standaard aanstaat, en het is degene die zonder enige configuratie zijn brood verdient.

De meting, op deze bak, over 32 domeinen. Leeg de cache, vraag de hele lijst op, vraag ze nog eens op, en lees de querytijd die dig meldt in plaats van het proces te klokken:

$ resolvectl flush-caches
$ for n in $NAMES; do dig +tries=1 @127.0.0.53 "$n" A | grep 'Query time'; done
mediaangemiddeldep90maxtotaal voor 32 namen
koude cache103,5 ms106,7 ms184 ms238 ms3.414 ms
warme cache0,0 ms0,5 ms1 ms3 ms16 ms

3.414 milliseconden tegen 16. Dat is het hele argument voor een lokale cache, en het is waarom dit de standaard is.

Wel eerlijk zijn over wat dat getal is, overigens. Het is de besparing op een reeks van tweeëndertig namen die nog nooit waren opgezocht, tegen dezelfde reeks nog eens gedraaid, en geen enkele echte werklast lijkt op een van beide. Het nuttige getal is de staart en niet de mediaan: de slechtste lookup in die set kostte koud 238 ms en warm 3 ms. Een pagina die acht hostnamen binnenhaalt geeft niets om je mediaan, hij wacht op je traagste.

De knoppen

[Resolve]
Cache=yes                 # yes | no-negative | no
CacheFromLocalhost=no     # default: do not cache answers from 127.0.0.1
StaleRetentionSec=0       # serve expired records when upstream is down
InstellingStandaardWat het doet
Cache=yesaanCache positieve en negatieve antwoorden
Cache=no-negativeAlleen positieve antwoorden, voor als je het zat bent een negatieve TTL uit te zitten
CacheFromLocalhost=noaanHelemaal niet cachen als de upstream op 127.0.0.1 zit
StaleRetentionSec=0uitVerlopen records blijven serveren als de upstream niet meer antwoordt
DNSCacheSize=40964096Records per scope. Alleen systemd 261 en later

Die derde rij is degene die bijt. Is je upstream een dnsmasq of een unbound op de loopback, dan cachet resolved zijn antwoorden helemaal niet, op de grond dat het ding waarmee hij praat zelf al een cache is. Richt resolved op een filterende resolver op een andere host en je krijgt twee lagen caching. Richt hem op een op de loopback en je krijgt er één.

StaleRetentionSec= is de interessante, en die staat standaard uit. Zet hem aan, en als de upstream niet meer antwoordt blijft resolved records serveren voorbij hun TTL in plaats van te falen.2 Hij probeert altijd eerst de upstream. Het geldt niet voor NXDOMAIN, want een naam die niet bestaat is een volstrekt geldig antwoord en daar is niets ouds aan. Voor een laptop die in en uit dekking rijdt, of een machine die door een DNS-storing heen moet blijven werken, is dit het waard:

[Resolve]
StaleRetentionSec=1d

systemd 261 voegde daar cachegroottes per protocol aan toe, met DNSCacheSize=, MulticastDNSCacheSize= en LLMNRCacheSize=, elk standaard 4096 records en afgetopt op 2^24.3 Niet op deze bak, die 259 draait, en op geen enkele huidige stabiele distributierelease. Het is het waard te weten dat het eraan komt, want tot nu toe was de cachegrootte helemaal niet instelbaar.

De daemon gooit ook alles weg bij geheugendruk, wat verstandig is en af en toe verrassend als je probeert uit te vogelen waarom een cachehitratio er slecht uitziet.

Split DNS is de reden om hem te houden

Neem je één ding mee uit dit bericht, neem dan deze sectie, want dit is de functie die werkelijk iets doet wat de oude stub-resolver niet kon.

De traditionele resolv.conf heeft één lijst met naamservers voor de hele machine. Eén lijst. Breng een VPN op en iets moet hem overschrijven, wat betekent dat ofwel je interne namen werken en de rest van DNS via de bedrijfsresolver gaat, ofwel andersom. Er is geen derde optie. Het bestand kan er geen uitdrukken.

resolved routeert per query, per interface.1 Elke link heeft zijn eigen servers en zijn eigen domeinen, en een lookup gaat naar de link waarvan het domein het best bij de naam past, op labelaantal.

ConfiguratieGeschreven alsEffect
Zoekdomeindamiendye.ukAchtervoegsel voor namen van één label, en routeert passende queries naar deze link
Alleen-routeren-domein~internal.exampleRouteert passende queries naar deze link, wordt nooit als achtervoegsel gebruikt
Vangnetroute~.Stuur alles wat nergens anders past naar deze link
StandaardrouteDNSDefaultRoute=yesNeemt queries zonder match, zonder ~. te claimen

Dus een laptop met een werk-VPN op krijgt dit:

resolvectl domain tun0 '~corp.example' '~10.in-addr.arpa'
resolvectl dns    tun0 10.0.0.53

Namen onder corp.example en reverse lookups voor 10.0.0.0/8 gaan de tunnel in. Al het andere blijft over de lokale link naar buiten gaan zoals eerst. Niemands resolv.conf is herschreven, en valt de tunnel weg, dan gaan de routes mee.

Eén query, twee links, en een routeringsbeslissing op labelaantalDE QUERYDE BESLISSINGDE LINKdb01.corp.example14.0.10.in-addr.arpablogs.damiendye.ukMeeste labels wintElke link heeft zijn eigenservers en domeinen.tun0~corp.examplewlp4s0DefaultRouteEen tilde routeert alleen. Zonder tilde is het ook achtervoegsel voor namen van één label.
Eén query, twee links, en een routeringsbeslissing op labelaantal

Eén regel is het waard onomwonden te zeggen, want het is degene waar mensen naar grijpen en die ze verkeerd doen. ~. op een link betekent geef deze link de voorkeur voor alles. Het zorgt er ook impliciet voor dat geen enkele andere link nog standaardroute kan zijn. Wil je dat een link de restjes krijgt zonder de hele naamruimte te claimen? Zet DNSDefaultRoute=yes en laat ~. met rust.

Kijk wat hij besloten heeft in plaats van het aan te nemen:

$ resolvectl status
Link 3 (wlp4s0)
    Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6
         Protocols: +DefaultRoute LLMNR=resolve -mDNS -DNSOverTLS
                    DNSSEC=no/unsupported
Current DNS Server: 192.0.2.53
       DNS Servers: 192.0.2.53 2001:db8:1::53
        DNS Domain: damiendye.uk
     Default Route: yes

Elk adres in dit bericht komt uit de documentatiereeksen, 192.0.2.0/24 en 2001:db8::/32, dus plak ze niet in een configuratie en verwacht een antwoord.4 5 De cijfers en het gedrag zijn echt, van een draaiende machine. De adressen zijn plaatsvervangers, want een globaal IPv6-adres dat op de gebruikelijke manier is opgebouwd draagt het MAC-adres van de interface in zijn onderste helft, en er een publiceren geeft meteen een stuk hardware-inventaris weg.

Die -DNSOverTLS en die DNSSEC=no zijn de volgende twee secties.

Hij kan DNSSEC valideren

De daemon wordt upstream beschreven als “a caching and validating DNS/DNSSEC stub resolver”.1 De validerende helft is echt, het is geen omhulsel om iets anders heen, en het werkt. Hij staat alleen niet aan.

Hem per link aanzetten heeft geen sudo nodig, want resolvectl gaat via polkit en een actieve lokale sessie mag het:

$ resolvectl dnssec wlp4s0 yes
$ resolvectl status wlp4s0 | grep DNSSEC
                    DNSSEC=yes/supported

Dat achtervoegsel /supported is resolved die meldt wat hij vond toen hij de upstream aftastte, los van wat jij vroeg. yes/supported betekent dat je om validatie vroeg en dat de server het kan dragen. no/unsupported op een standaardinstallatie betekent dat niemand erom vroeg, dus dat niemand heeft afgetast.

Zodra het aanstaat komt elke lookup terug met een oordeel, en dat zijn er drie.

Secure, insecure en bogus zijn drie verschillende antwoordenPUBLICEERT DE OUDER EEN DS, EN KLOPPEN DE SIGNATUREN?SECUREdamiendye.ukOndertekend, en deketen klopt.Data teruggegeven.INSECUREsystemd.ioGeen DS. Niet ondertekend,dus niets te controleren.Data teruggegeven.BOGUSdnssec-failed.orgBeweert ondertekend te zijn.Het bewijs faalt.Lookup geweigerd.Twee van de drie geven data terug. Validatie aanzetten breekt niet-ondertekende sites niet.
Secure, insecure en bogus zijn drie verschillende antwoorden, en maar één ervan is een mislukking

Hier zijn ze alle drie op deze machine, tegen echte namen:

$ resolvectl query damiendye.uk
-- Data is authenticated: yes; Data was acquired via local or encrypted transport: no

$ resolvectl query systemd.io
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no

$ resolvectl query dnssec-failed.org
dnssec-failed.org: resolve call failed: DNSSEC validation failed: missing-key
  (DNSKEY Missing: no SEP matching the DS found for dnssec-failed.org.)

damiendye.uk is ondertekend, dus de keten vanaf de root valideert en de data is geauthenticeerd. systemd.io is niet ondertekend, dus er valt niets te controleren en resolved zegt dat eerlijk in plaats van te doen alsof. Dat is het eigen domein van het systemd-project, en daar kom ik op terug. dnssec-failed.org wordt gepubliceerd met opzettelijk kapotte signaturen. Die faalt hard, met een diagnose die het precieze probleem benoemt.

Hier is het onderscheid dat verloren gaat. Insecure is geen mislukking. Een niet-ondertekende zone geeft data terug en vertelt je dat ze niet geverifieerd kon worden. Alleen een zone die beweert ondertekend te zijn en het dan niet kan bewijzen wordt afgewezen.

De keten zelf is zichtbaar als je hem wilt zien:

$ dig +short @127.0.0.53 uk DS
43876 8 2 A107ED2AC1BD14D924173BC7E827A1...

$ dig +short @127.0.0.53 damiendye.uk DS
2371 13 2 A5B2825C57899A5A15EE9703832C8358E0D19EF29DC72DD83C691ED77C33BD7F

$ dig +short @127.0.0.53 damiendye.uk DNSKEY
256 3 13 oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWz...
257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d...

De root staat garant voor uk, uk staat garant voor damiendye.uk, en de 257-sleutel ondertekent de 256-sleutel die de records ondertekent. Algoritme 13 daar is ECDSA P-256, wat je op een nieuwe zone wilt in plaats van de RSA die de uk-DS hierboven nog gebruikt.

Via de gewone stub krijgt een programma dat nog nooit van resolved heeft gehoord hetzelfde oordeel, als een AD-vlag:

$ dig @127.0.0.53 ietf.org A | grep flags
;; flags: qr rd ra ad;

$ dig @127.0.0.53 dnssec-failed.org A | grep status
;; ->>HEADER<<- status: SERVFAIL

En de daemon houdt een lopende telling bij, wat de snelste manier is om te zien of validatie iets doet:

$ resolvectl statistics
DNSSEC Verdicts
                    Secure:  134
                  Insecure:   62
                     Bogus:    0
             Indeterminate:    2

Wat validatie kost

Hier ga ik je teleurstellen, want ik heb geprobeerd dit fatsoenlijk te meten en dat lukte niet.

Warm is het schoon en is het niets. Dezelfde 32 namen tegen een volle cache kostten 16 ms zonder validatie en 23 ms met. Noem het een afrondingsfout.

Koud is waar de kosten zouden moeten opduiken, en één client kan het niet isoleren. Ik draaide de reeks beide kanten op en kreeg tegenstrijdige antwoorden: in de ene volgorde leek validatie 74% trager, in de andere leek het sneller, wat onmogelijk is en je vertelt wat er werkelijk wordt gemeten. Welke reeks als tweede draait profiteert ervan dat de upstream-resolver alles al had opgehaald waar de eerste om vroeg. De variabele die domineert is niet validatie, het is wiens cache warm was.

Dus ik geef je geen getal waar ik niet achter kan staan. Wat waar is, is het mechanisme, en de documentatie stelt de vorm ervan onomwonden: validatie “requires retrieval of additional DNS data, and thus results in a small DNS lookup time penalty”.2 Een koude gevalideerde lookup loopt de delegatieketen af en haalt op elk niveau DS en DNSKEY op voordat hij kan antwoorden, dus hij kost extra retourtjes op namen waar nog niemand om heeft gevraagd, en helemaal niets op namen waar iemand wel om heeft gevraagd.

Wat ook de reden is dat dezelfde pagina waarschuwt dat de cache uitzetten “comes at a performance penalty, which is particularly high when DNSSEC is used”.2 De twee functies staan niet los van elkaar. Validatie is juist betaalbaar omdat de cache betekent dat je er één keer voor betaalt.

Wil je het echte getal voor je eigen netwerk, meet het daar. Eén client op één verbinding kan het je niet vertellen.

Waarom staat validatie dan uit

Omdat je distributie het uitzette toen ze het pakket bouwde, en dat deed ze om een reden die ze heeft opgeschreven.

Upstream systemd levert validatie aan. De buildoptie zegt het:

option('default-dnssec', type : 'combo',
       choices : ['yes', 'allow-downgrade', 'no'],
       value : 'allow-downgrade')

en de upstream-handleiding is het ermee eens: DNSSEC= “Defaults to allow-downgrade”.6

Kijk nu wat Fedora aan die build meegeeft:7

-Ddefault-dnssec=no
-Ddefault-dns-over-tls=no
-Ddefault-mdns=no
-Ddefault-llmnr=resolve

En wat Debian en Ubuntu meegeven:8

-Ddefault-dnssec=no
-Ddefault-llmnr=no
-Ddefault-mdns=no
-Ddns-over-tls=openssl

Dus het bestand op /usr/lib/systemd/resolved.conf op een Fedora-bak, degene met de kop “Entries in this file show the compile time defaults”, zegt #DNSSEC=no. Lees dat nog eens, want het doet iets sluws: de regel ziet eruit als de software die je zijn eigen standaard vertelt, en wat hij je werkelijk terugleest is de buildvlag van Fedora, met de echte standaard van upstream nergens op de pagina.

InstellingUpstream-standaardFedora-buildDebian/Ubuntu-buildResultaat op deze bak
DNSSEC=allow-downgradenonoDNSSEC=no
DNSOverTLS=nonono (gecompileerd met OpenSSL)-DNSOverTLS
MulticastDNS=yesnono-mDNS
LLMNR=yesresolvenoLLMNR=resolve

Elk daarvan komt overeen met wat resolvectl status op deze machine afdrukt. Broncode, buildvlag, draaiend systeem, alle drie zijn het eens.

De redenering van Fedora staat in het wijzigingsvoorstel en is verfrissend bot. De functie “is known to cause compatibility problems with certain network access points”, en Fedora “is not prepared to handle an influx of DNSSEC-related bug reports”, dus hij gaat uit.9

Mag ik vragen waarom het antwoord op een functie die stukgaat op slechte netwerken is om hem voor iedereen uit te zetten, in plaats van allow-downgrade te leveren zoals upstream doet en hem zichzelf te laten uitzetten waar dat moet? Niet wie het besloot. Wat er in het proces daartoe leidde. Want allow-downgrade bestaat juist voor het captive-portalgeval, het is om precies die reden de standaard van upstream, en in plaats daarvan no leveren betekent dat een machine op een volstrekt goed netwerk ook geen validatie krijgt.

Om eerlijk tegenover ze te zijn: allow-downgrade heeft zijn eigen probleem, en dat is een echt probleem. De modus merkt een resolver op die geen DNSSEC kan en stopt stilletjes met valideren. Een aanvaller die jouw DNS-antwoorden kan vormgeven kan die detectie met opzet laten afgaan, en de documentatie zegt het onomwonden: het “makes DNSSEC validation vulnerable to ‘downgrade’ attacks”.2 Een beveiligingsfunctie die elke aanvaller kan uitzetten doet minder dan ze lijkt te doen.

Dus geen van beide standaarden is goed. no geeft je niets. allow-downgrade geeft je iets wat een aanvaller je kan afnemen, en yes geeft je het echte werk plus alles uit de volgende sectie.

Hoeveel van het web is eigenlijk ondertekend

Voordat je veel moeite in validatie steekt is het het waard te weten welk deel van je lookups het überhaupt kan beschermen. Dus vroeg ik resolved om het oordeel over de apex van tweeëndertig domeinen die dit onderwerp werkelijk aangaan: de organen die de standaard schreven, de clubs die de resolver leveren, en de infrastructuur die iedereen opzoekt of hij het nu wil of niet.

Zestien van de tweeëndertig. De helft.

OndertekendNiet ondertekend
Standaarden en registriesietf.org, iana.org, icann.org, rfc-editor.org, ripe.net, isc.org, nlnetlabs.nl, nic.cz, afnic.fr, verisign.comgeen
Distributies en leveranciersdebian.org, fedoraproject.org, opensuse.org, almalinux.orgredhat.com, ubuntu.com, canonical.com, suse.com, rockylinux.org, archlinux.org
systemd zelfgeensystemd.io, freedesktop.org
Infrastructuurcloudflare.com, gov.ukgithub.com, kernel.org, google.com, wikipedia.org, mozilla.org, apache.org, gnu.org, quad9.net

Lees de eerste kolom van boven naar beneden. Elk standaardenorgaan en elke registry heeft ondertekend. Tien van de tien, geen uitzonderingen: de mensen die DNSSEC schreven, en de mensen die de registries draaien die de DS-records voor alle anderen publiceren. Ze hebben het werk op hun eigen zones gedaan.

Lees nu de derde rij van links naar rechts. systemd.io heeft geen DS. Het project dat de validerende resolver schreef waar dit hele bericht over gaat, heeft zijn eigen domein niet ondertekend, en freedesktop.org, waar zijn documentatie woont, ook niet. Daaronder is quad9.net ook niet ondertekend, en daar mag je even bij stilstaan: een publieke resolver wiens hele verkoopverhaal is dat hij DNSSEC voor je valideert, op een zone die niemand kan valideren.

En de leveranciers splitsen netjes langs één lijn. De communitydistributies hebben ondertekend. debian.org, fedoraproject.org, opensuse.org, almalinux.org. De bedrijven niet. redhat.com, ubuntu.com, canonical.com, suse.com. Dat zijn de vier organisaties die deze resolver verpakken en leveren aan het grootste deel van het Linux-landschap.

Daar zit geen gat in de technologie. Het protocol is al ruim vijftien jaar uit te rollen, het gereedschap is gratis, en de registries nemen het DS-record aan zonder ervoor te rekenen. Ik zeg het je voor niks: de mensen die de standaard schreven hebben ondertekend, en de meeste mensen die de software leveren niet.

Er is een scherpere versie van hetzelfde punt in gov.uk, dat wel ondertekend is, en dan dit doet:

$ dig +short @127.0.0.53 www.gov.uk CNAME
www-cdn.production.govuk.service.gov.uk.

$ dig +short @127.0.0.53 service.gov.uk DS
                                          (nothing)

uk heeft een DS. gov.uk heeft een DS. service.gov.uk heeft er geen, dus de keten houdt daar dood op, en www.gov.uk resolvet insecure ook al is de apex erboven netjes ondertekend. Iemand heeft het werk op gov.uk gedaan en toen de echte website op een niet-ondertekende delegatie gericht. Half werk.

Dus als je een zone ondertekent, controleer de namen die mensen werkelijk typen. Een ondertekende apex die met een CNAME een niet-ondertekende CDN-zone in wijst levert je niks op.

DNS over TLS werkt, onder voorwaarden

DNS over TLS is geïmplementeerd, het is in elke gangbare build meegecompileerd, en het werkt. Het staat overal standaard uit, ook upstream, waar DNSOverTLS= “Defaults to no”.6

De configuratie is twee regels, en de tweede is degene die mensen missen:

[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
DNSOverTLS=yes

Die #dns.quad9.net is geen versiering. Hij bepaalt de naam die voor SNI en voor het valideren van het certificaat wordt gebruikt. Laat hem weg en het certificaat wordt in plaats daarvan “checked against the server’s IP”.2 Dat werkt bij de grote aanbieders omdat die IP-adressen in de SAN’s van hun certificaat zetten, maar het is de zwakkere controle, het gaat stuk zodra een aanbieder daarmee ophoudt, en het biedt je geen bescherming tegen omgeleid worden naar een ander adres dat toevallig een geldig certificaat voor zichzelf heeft. Fedora’s eigen magazine-artikel hierover laat de hostnaam weg,10 wat jammer is, want de syntaxis staat gewoon in de commentaren van het meegeleverde configuratiebestand.

Strikt en opportunistisch zijn heel verschillende instellingen

ModusOp een server die DoT ondersteuntOp een server die dat niet doetAuthenticeert de server
yesVersleuteldAlle lookups falenJa
opportunisticVersleuteldStilletjes platte tekstNee
noPlatte tekstPlatte tekstn.v.t.

opportunistic leest als het verstandige midden en is dat meestal niet. De documentatie zegt het ronduit: in die modus is “the resolver is not capable of authenticating the server, so it is vulnerable to ‘man-in-the-middle’ attacks”,2 en iedereen die jouw verkeer op poort 853 kan laten vallen kan de downgrade afdwingen. Het beschermt je tegen passief meekijken op een netwerk waar niemand het probeert. Tegen iemand die het wel probeert doet het niks.

yes is de eerlijke instelling. Het betekent ook dat je, als hij geen verbinding kan maken, helemaal geen DNS krijgt, wat het waard is te weten voordat je het op een machine zet waar je niet naartoe kunt lopen.

Ik probeerde strikte DoT naar Quad9 vanaf deze bak en de query bleef hangen. Geen foutmelding, geen timeout, niets in de journal tussen het legen en mijn terugdraaien twee minuten later:

Sep 27 17:12:11 systemd-resolved[40105]: wlp4s0: Bus client set DNS server list to: 9.9.9.9#dns.quad9.net, ...
Sep 27 17:12:14 systemd-resolved[40105]: wlp4s0: Bus client set DNSOverTLS setting: yes
Sep 27 17:12:15 systemd-resolved[40105]: Flushed all caches.
Sep 27 17:14:39 systemd-resolved[40105]: wlp4s0: Bus client set DNS server list to: 192.0.2.53, ...

Ik kan je van hieruit niet vertellen of poort 853 op die verbinding bereikbaar is, want de shell waarvandaan ik de test draaide kon poort 443 ook niet bereiken en werd duidelijk zelf gefilterd. Wat het log wel laat zien is de faalmodus: strikte DoT die geen verbinding kan maken meldt niets nuttigs. Hij wacht. Zet je dit aan en wordt je DNS stil, kijk dan naar ss -tn dport = :853 voordat je naar resolved gaat zoeken.

Het fatsoenlijk testen

# does the transport actually come up
resolvectl flush-caches
resolvectl query ietf.org        # expect: acquired via ... encrypted transport: yes

# is anything still going out in the clear
sudo tcpdump -ni any 'port 53 and not host 127.0.0.53'

Het tweede commando is degene die de waarheid vertelt. Met werkende DoT hoort er niets meer op poort 53 de machine te verlaten, en alles wat dat wel doet is een programma dat om de stub heen is gekomen.

DNS over HTTPS bestaat hier niet

Rechttoe rechtaan antwoord op de vraag: systemd-resolved ondersteunt geen DNS over HTTPS. Niet gedeeltelijk, niet achter een vlag, niet met een buildoptie die niemand aanzet. Er zit helemaal geen DoH in.

En dat is niet van de documentatie afgelezen, die simpelweg verouderd zou kunnen zijn. Het is wat de binary bevat:

$ strings /usr/lib/systemd/systemd-resolved | grep -icE 'application/dns-message|dns-query|:443'
0

$ grep -oE 'name="DNSOver[A-Za-z]*"' /usr/share/dbus-1/interfaces/org.freedesktop.resolve1.Manager.xml
name="DNSOverTLS"

Geen DoH-wireformat, geen HTTP/2, één versleutelingseigenschap op de D-Bus-interface en die is TLS. De volledige lijst met resolved.conf-directieven upstream komt op zeventien instellingen en geen daarvan noemt HTTPS.6

Erom is gevraagd. Issue #8639, “Add support for DNS-over-HTTPS to systemd-resolved”, werd geopend op 2 april 2018 en staat nog steeds open, met twee pull requests eraan en niets gemerged.11 Acht jaar en het loopt door.

Zelfde lading, zelfde versleuteling, andere poortBEIDE DRAGEN DEZELFDE DNS-QUERY, BINNEN DEZELFDE TLSDNS over TLSDNS over HTTPStcp/853tcp/443In systemd-resolvedja, sinds v239nee, helemaal nietEen waarnemer ziet de namenneeneeEen netwerk kan het blokkerenja, eigen poortniet zonder pijnJe kunt je eigen DNS auditenjaneeGevraagd in systemd-issue 8639, april 2018. Nog steeds open.
Zelfde lading, zelfde versleuteling. Het verschil is op welke poort het zit, en wie het kan zien

Is dat de verkeerde keuze? Niet vanzelfsprekend, en het argument voor DoT is behoorlijk. Beide dragen DNS binnen TLS en beide houden dezelfde passieve waarnemer tegen. Het voordeel van DoH is dat het zich op poort 443 verstopt tussen al het andere, dus een netwerk dat versleutelde DNS wil blokkeren moet veel harder werken. Dat is werkelijk nuttig als de netwerkbeheerder de tegenstander is.

Het snijdt ook de andere kant op. Waar jij de beheerder bent, betekent die ononderscheidbaarheid dat je je eigen DNS ook niet kunt auditen, en elke applicatie die haar eigen DoH-client meelevert stopt met de systeemresolver te gebruiken, en zo krijg je een browser die je split DNS, je cache en je interne zones negeert. Op je eigen spul is een resolver die zichtbaar is op een bekende poort een voordeel.

Dus: bereikt DoT je resolver, gebruik het dan en je verliest niets. Is poort 853 geblokkeerd, dan heeft resolved geen antwoord en wil je een DoH-proxy ervoor, dnscrypt-proxy of cloudflared op de loopback met resolved daarop gericht. Caching en validatie blijven het werk van resolved. Alleen het transport verhuist.

Wat je distributie werkelijk levert

Dezelfde daemon gedraagt zich heel verschillend afhankelijk van wie hem verpakte. De dienst aanzetten is de makkelijke helft. De helft die wordt overgeslagen is hem inhaken in de netwerkgereedschappen die de distributie werkelijk gebruikt, en op een van deze vier is hij werkelijk niet ingehaakt tot je het zelf doet.

GeïnstalleerdAangezetnss-resolve ingehaaktConfiguratie komt viaOndersteund
Fedora (33+)jajaja, in systemd-libsNetworkManagerja
Ubuntujajajanetplan, dan NM of networkdja
Debian (12+)apart pakketneenee, apart pakketjij kiest en haakt het inja
RHEL / Rocky / Alma (9, 10)janeejaNetworkManager, als je het zegtTechnology Preview

Validatie en een volle cache: de instellingen zelf

Dit zijn overal dezelfde vier regels, want resolved.conf is resolved.conf op elke distributie. Wat verschilt is alleen hoe de rest van de configuratie de daemon bereikt, en daar gaan de volgende vier secties over.

# /etc/systemd/resolved.conf.d/60-local.conf
[Resolve]
DNSSEC=allow-downgrade
Cache=yes
CacheFromLocalhost=no
StaleRetentionSec=1d

Gebruik een drop-in in plaats van /etc/systemd/resolved.conf te bewerken, want het hoofdbestand heeft een lagere voorrang dan elke drop-in en een pakketupdate kan er ruzie met je over maken. De nummering is een gedocumenteerde conventie: leveranciers nemen 10 tot 40 onder /usr/, jij neemt 60 tot 90 onder /etc/, dus die van jou wint.6

Op systemd 261 en later is er een vijfde regel die het waard is toe te voegen, want tot dan was de cachegrootte helemaal niet instelbaar:

DNSCacheSize=16384    # systemd 261+; default 4096, max 2^24

Pas het toe en bevestig dat de daemon het met het bestand eens is, in plaats van aan te nemen dat hij het gelezen heeft:

systemd-analyze cat-config systemd/resolved.conf   # every fragment, in precedence order
systemctl restart systemd-resolved
resolvectl status | grep -E 'DNSSEC|Protocols'
resolvectl statistics                              # verdicts should start moving

DNSSEC=allow-downgrade is de instelling die ik zou zetten op een machine die ik niet ga babysitten, om de redenen in de sectie hierboven. DNSSEC=yes is de eerlijke en die faalt dicht. Kies met opzet.

Hem inhaken in de netwerkstack

De dienst aanzetten is één commando. De eigen netwerkgereedschappen van je distributie zover krijgen dat ze hun DNS-configuratie aan hem afgeven is het deel dat verschilt, en het is waar “aangezet” en “werkt goed” uit elkaar lopen.

Waar de DNS-configuratie vandaan komtDNSSEC aanzettenDe cache dimensionerenExtra bedrading nodig
FedoraNetworkManager, automatischdrop-indrop-ingeen
Ubuntunetplan, dan NM of networkddrop-in, of per link in .networkdrop-ingeen
Debianwelke stack je ook koosdrop-in, of per link in .networkdrop-inlibnss-resolve, plus de stack hieronder
RHEL-familieNetworkManager, als je het zegtdrop-indrop-indns=systemd-resolved

Fedora

De volledigste integratie van de vier, en de enige waar alles al is ingehaakt.

NetworkManager bezit het netwerk en geeft DNS aan resolved zonder dat het gezegd hoeft te worden. Op deze bak staat nergens in /etc/NetworkManager/ een dns=-regel en het werkt toch, want NetworkManager merkt de draaiende daemon op en gebruikt hem. /etc/resolv.conf is de stub-symlink, en glibc is ingehaakt omdat libnss_resolve.so.2 in systemd-libs zit, wat niet optioneel is:

$ rpm -qf /usr/lib64/libnss_resolve.so.2
systemd-libs-259.9-1.fc44.x86_64

$ grep ^hosts: /etc/nsswitch.conf
hosts: files myhostname mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns

Op Fedora is de drop-in hierboven dus het hele werk. Verder niets aan te sluiten.

Ubuntu

Standaard aangezet, en nss-resolve is op dezelfde manier ingehaakt. Het verschil is dat de configuratie meestal via netplan binnenkomt:

network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: true
      nameservers:
        addresses: [9.9.9.9, 149.112.112.112]
        search: [example.com]

Netplan geeft dat aan zijn renderer, NetworkManager op de desktop of systemd-networkd op de server, en de renderer geeft het aan resolved. Let op wat netplan niet kan uitdrukken. Er is geen netplan-sleutel voor DNSSEC=, en geen voor DNSOverTLS=. Die gaan in de drop-in hierboven, waar ze toch al thuishoren.

Zit je op systemd-networkd, dan zijn de instellingen per link ook native beschikbaar in het .network-bestand, en een instelling per link wint van de globale:

# /etc/systemd/network/10-lan.network
[Network]
DNS=9.9.9.9#dns.quad9.net
DNSSEC=yes
DNSOverTLS=yes
Domains=~.

Debian moet met de hand worden ingehaakt

Dit is degene die werkelijk niet is ingehaakt, en de reden dat de sectie hierboven bestaat.

Sinds Debian 12 is systemd-resolved een apart pakket, en de release notes zijn expliciet over de upgrade: “The new systemd-resolved package will not be installed automatically on upgrades”, en “until it has been installed, DNS resolution might no longer work since the service will not be present on the system.”12 Dezelfde notities beslechten de bredere vraag: “systemd-resolved was not, and still is not, the default DNS resolver in Debian.”

Het installeren kost drie pakketten, niet één, en het tweede is degene die iedereen mist:

apt install systemd-resolved libnss-resolve
systemctl enable --now systemd-resolved

libnss-resolve staat alleen onder Suggests:, het is geen afhankelijkheid,13 en Suggests is de ene relatie waar apt niets mee doet. Dus niemand installeert het.

De reden dat niemand het merkt is interessanter dan de reden dat niemand het installeert. Laat het weg en glibc bereikt resolved nog steeds, want /etc/resolv.conf wijst naar de stub en gewone nss-dns praat ermee. Het grootste deel van de daemon blijft werken:

Zonder libnss-resolveMet
CacheJa, via de stubJa
DNSSEC-validatieJa, het gebeurt in de daemonJa
DNS over TLSJaJa
AD-bit bereikt glibcJa, resolv.conf draagt trust-adJa
Secure tegenover insecure tegenover bogusNee, maar één bitJa
Adressen met een linkscopeNeeJa
/etc/hosts gelezen door de daemonNeeJa

Niets in de linkerkolom ziet er kapot uit, dus er wordt niets gerepareerd. Wat je kwijt bent is wat het DNS-wireformat niet kan dragen, en het duidelijkste geval is een adres met een scope eraan:

$ getent hosts _gateway            # via nss-resolve
fe80::5054:ff:fe12:3456 _gateway

$ dig +short @127.0.0.53 _gateway  # via the stub, as nss-dns would
192.0.2.1

Dezelfde naam, dezelfde daemon, twee verschillende antwoorden. Een link-lokaal IPv6-adres is betekenisloos zonder de interface waaraan het gescoopt is, en er is nergens in een DNS-antwoord plek om die te zetten, dus de stub kan alleen het IPv4 teruggeven. De native API heeft er wel plek voor en geeft je het adres dat je werkelijk wilde.

De oordeelrij is hetzelfde probleem. AD is één bit, ondertekend of niet. De native API scheidt secure van insecure van bogus, wat het verschil is tussen “niemand heeft dit ondertekend” en “iemand heeft dit ondertekend en iemand anders heeft eraan gezeten”. Een applicatie die erom geeft kan die twee over de draad niet uit elkaar houden.

Dat is het gat tussen de dienst die draait en de dienst die is ingehaakt, en het blijft onzichtbaar tot je ernaar gaat zoeken.

Kijk of het geland is:

grep ^hosts: /etc/nsswitch.conf     # wants 'resolve [!UNAVAIL=return] dns'
Vier manieren naar binnen op Debian, en degene die niet voor je geïnstalleerd isWAAR DE CONFIG VANDAAN KOMTHOE HET BINNENKOMTDE DAEMONifupdown, statischifupdown, DHCPsystemd-networkdNetworkManagerresolvconf-shim= resolvectlresolved127.0.0.53EN HET STUKJE DAT NIEMAND INSTALLEERTglibc getaddrinfo()libnss-resolveAlleen Suggests:. Zonder dat werkt glibc nog steeds, en het oordeel bereikt het nooit.
Vier manieren naar binnen op Debian, en degene die niet voor je geïnstalleerd is

Hoe de rest van je netwerk hem bereikt hangt af van op welke van Debians stacks je zit.

Het pakket verklaart Provides: resolvconf en Conflicts: resolvconf, openresolv,13 dus het neemt de resolvconf-interface volledig over. /usr/sbin/resolvconf wordt een symlink naar resolvectl, wat een multi-call binary is: onder die naam aangeroepen spreekt hij het resolvconf(8)-protocol en duwt alles wat hij krijgt rechtstreeks resolved in.14 Alles wat al resolvconf -a aanroept blijft dus ongewijzigd werken, met systemd-resolved als enige ondersteunde backend.

Niet de hele interface overleeft, en de gaten falen luid in plaats van stil:

resolvconf-optieOnder systemd-resolved
-a <iface>Registreert DNS per link, gelezen van stdin. Degene die ertoe doet
-d <iface>Meldt af, hetzelfde als resolvectl revert
-xGemapt op een routeringsdomein ~.
-pMarkeert de link als geen standaardroute (systemd 257+)
-fLaat -a en -d zwijgen over een interface die er niet is
-mGeaccepteerd en stilletjes genegeerd
-u, -i, -I, -l, -r, -R, -v, -VNiet ondersteund. Het commando faalt

Eén valkuil die het waard is te kennen voordat je hem gaat debuggen: de shim schrijft /etc/resolv.conf alleen als dat bestand een symlink naar /run/systemd/resolve/resolv.conf is, en niet als het een statisch bestand is.14

  • ifupdown, de standaard op een Debian-serverinstallatie. De stanza dns-nameservers in /etc/network/interfaces werd altijd geïmplementeerd door de hooks van het oude pakket resolvconf, en systemd-resolved conflicteert met dat pakket en levert geen eigen hooks in /etc/network/if-up.d/. Vertrouw voor een statische interface niet op de stanza. Zet de servers expliciet op de link en laat resolved ze bezitten:

    # /etc/network/interfaces
    auto enp1s0
    iface enp1s0 inet static
        address 192.0.2.10/24
        gateway 192.0.2.1
        up   /usr/sbin/resolvconf -a $IFACE <<< 'nameserver 192.0.2.53'
        down /usr/sbin/resolvconf -d $IFACE
    
  • DHCP op ifupdown is al geregeld. isc-dhcp-client levert hooks die naar de daemon zijn vernoemd, /etc/dhcp/dhclient-enter-hooks.d/resolved-enter en /etc/dhcp/dhclient-exit-hooks.d/resolved,15 dus de DNS-servers van een lease landen op de juiste link zonder dat er iets te configureren valt.

  • systemd-networkd is de schoonste optie en heeft helemaal geen shim nodig, want de twee helften zijn hetzelfde project. DNS, DNSSEC en DoT per link zijn native sleutels in het .network-bestand, precies als in het Ubuntu-voorbeeld hierboven.

  • NetworkManager, op een Debian-desktop, moet het één keer verteld worden:

    # /etc/NetworkManager/conf.d/10-resolved.conf
    [main]
    dns=systemd-resolved
    

Kies er een en weet welke je koos. De faalmodus hier is niet een daemon die weigert te starten, het is twee stacks die allebei geloven dat ze /etc/resolv.conf bezitten, wat leest als haperende DNS en een middag kost. resolvectl status zegt op welke link de servers geland zijn. Is het antwoord geen enkele, dan voedt niets de daemon, en dat is de bug.

RHEL, Rocky en Alma

En hier is degene die het waard is twee keer te lezen. Het pakket is er, nss-resolve is beschikbaar, NetworkManager kan het aansturen, en de documentatie van Red Hat zegt dit:

systemd-resolved is an unsupported Technology Preview.16

Die formulering staat sinds 9.0 in de release notes van RHEL 9 en staat er bij 9.8 nog steeds. Technology Preview betekent geen productie-SLA, en Red Hat raadt het uitdrukkelijk niet aan voor productiegebruik.

En hij staat ook niet aan. NetworkManager bezit resolv.conf op deze systemen, dus hem aanzetten is een NetworkManager-instelling en niet alleen de unit aanzetten:

# /etc/NetworkManager/conf.d/10-resolved.conf
[main]
dns=systemd-resolved
systemctl enable --now systemd-resolved
systemctl reload NetworkManager
resolvectl status          # confirm NM actually handed the servers over

Daarna geldt de drop-in hierboven ongewijzigd, en validatie en de cache gedragen zich precies zoals op Fedora.

Dus op RHEL heb je een beslissing die op de andere niet bestaat. Wil je een validerende, cachende, split-DNS-vaardige lokale resolver op een ondersteunde RHEL-bak? Dan is het ondersteunde antwoord niet deze. Het is unbound, of dnsmasq via NetworkManagers eigen dns=dnsmasq, waar Red Hat allebei werkelijk achter gaat staan.

Het label is geen oordeel over de code. Het is een oordeel over waar Red Hat een SLA achter zet, en een cachende validerende resolver is iets waar klanten tickets over openen. Prima. Maar koop je RHEL voor de ondersteuning, dan is naamresolutie op een niet-ondersteund onderdeel draaien een beslissing om met opzet te nemen en op te schrijven, niet een om in te rollen omdat het nu eenmaal in de repo zat.

Er is niets werkelijk verminkt, en zo bewijs je dat

Het uitgangspunt is het waard getest te worden, want “mijn distro heeft het uitgezet, ik zal wel moeten herbouwen” is de reflex en hier is die verkeerd.

systemd heeft twee volstrekt verschillende soorten buildoptie, en er wordt over gepraat alsof ze er één waren:17

BuildoptieSoortUpstreamFedoraDebian en UbuntuTijdens draaien te wijzigen
resolvemogelijkheidaangebouwdtruenee, en ze bouwen het allebei
nss-resolvemogelijkheidaangezetgebouwd, zit in systemd-libsenabled, komt als libnss-resolvenee, en ze bouwen het allebei
dns-over-tlsmogelijkheidautoauto, komt uit op OpenSSLopensslnee, en ze compileren het allebei mee
opensslmogelijkheidaangezetenabledenablednee, en ze zetten het allebei aan
default-dnssecalleen standaardallow-downgradenonoja, in een drop-in
default-dns-over-tlsalleen standaardnonoupstream-standaardja, in een drop-in
default-mdnsalleen standaardyesnonoja, in een drop-in
default-llmnralleen standaardyesresolvenoja, in een drop-in

Lees de laatste kolom. Elke instelling waarover dit bericht heeft geklaagd zit in de onderste helft, en elk ervan is een standaardwaarde, geen mogelijkheid. De code is op allebei meegecompileerd. Niemand heeft iets weggehaald.

Het bewijs staat eerder in dit bericht en had geen compiler nodig. Op het standaard Fedora-pakket, met DNSSEC=no ingebakken, zette één commando validatie aan en werkte het volledig: een ondertekende zone geauthenticeerd, een niet-ondertekende eerlijk gemeld, een kapotte geweigerd met een precieze diagnose. Was het eruit gecompileerd, dan had resolvectl status nooit yes/supported gezegd.

Controleer dus je eigen build voordat je naar een toolchain grijpt:

# is the crypto there at all
systemctl --version | tr ' ' '\n' | grep -E '^[+-](OPENSSL|GNUTLS|GCRYPT)$'

# ask for the feature and read back what the daemon says it can do
resolvectl dnssec <link> yes
resolvectl status <link> | grep DNSSEC          # yes/supported means the code is there

Op deze Fedora-bak is dat -GCRYPT +GNUTLS +OPENSSL, wat ruim voldoende is: DNSSEC heeft er een van nodig en DoT is tegen OpenSSL gebouwd.

Wat de distributies werkelijk wél weghalen

Er is één ding dat ze er allebei uit halen, en het staat niet in het lijstje waar iedereen over klaagt.

Upstream compileert een fallbacklijst met DNS-servers in de binary: Cloudflare, Google en Quad9.17 Zowel Fedora als Debian bouwt met -Ddns-servers= op niets gezet, en je kunt het op de geleverde binary bevestigen in plaats van me op mijn woord te geloven:

$ strings /usr/lib/systemd/systemd-resolved | grep -ciE 'quad9|one\.one\.one|dns\.google'
0

Niets. Geen hyperscaler in het uitvoerbare bestand ingebakken.

Dat is de ene plek waar beide distributies het beter hebben gedaan dan upstream, en dat verdient het onomwonden gezegd te worden, gezien hoeveel van dit bericht over standaardwaarden ging die ik verkeerd vind. Een machine die zijn DNS-configuratie kwijtraakt hoort luid te falen en op je te wachten. Hij hoort niet stilletjes elke naam die je opzoekt naar een resolver in een ander rechtsgebied te sturen die jij nooit hebt gekozen. Wil je een fallback? Zet FallbackDNS= en kies wie het is.

Als je werkelijk een herbouw nodig hebt

Er bestaat één echt geval: een minimale of embedded build waar iemand -Ddns-over-tls=false heeft meegegeven en het transport werkelijk ontbreekt. Controleer eerst met de commando’s hierboven, want het is zeldzaam en het ziet er identiek uit aan de instelling die gewoon uitstaat.

Heb je het wel nodig, herbouw dan het pakket, nooit make install:

# Fedora
dnf download --source systemd
rpmbuild --rebuild --define '_with_upstream 1' systemd-*.src.rpm

# Debian and Ubuntu
apt source systemd
cd systemd-*/ && editor debian/rules       # change the -Ddefault-* flags
dpkg-buildpackage -us -uc -b

Ik heb geen van beide op deze machine gedraaid, dus behandel ze als de vorm van het werk, niet als een getest recept. Het principe is wat telt. systemd is PID 1, en een met de hand gebouwde make install over de kopie van je distributie heen haalt het uit de pakketbeheerder: geen beveiligingsupdates meer, en de volgende upgrade vecht met je om de bestanden. Het pakket bouwen houdt allebei. Voor bijna iedereen is het eerlijke antwoord dat een drop-in van vier regels hetzelfde werk in twintig seconden doet, en daarom staat deze sectie er vooral om je het uit het hoofd te praten.

Drie configuraties die het waard zijn

Geen menukaart met elke optie. Drie posities, die ik elk werkelijk zou verdedigen.

Laptop, vijandige netwerkenServer, je eigen netwerkStrikt
Waartegen je je verdedigtHet café en het hotelportaalNiets lokaals; de upstream valideert alEen resolverpad dat je niet vertrouwt
DNSSEC=allow-downgradenoyes
DNSOverTLS=opportunisticnoyes
StaleRetentionSec=1d1dniet gezet
Overleeft een captive portalJan.v.t.Nee
Overleeft een gefilterde .arpaJaJaNee
Overleeft een geblokkeerde poort 853Jan.v.t.Nee
Een aanvaller kan het downgradenJa, beide instellingenn.v.t.Nee

Een laptop op netwerken die je niet beheert. Versleutel het transport, en neem het downgraderisico in ruil voor een ding dat achter een captive portal blijft werken.

# /etc/systemd/resolved.conf.d/60-local.conf
[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
DNSOverTLS=opportunistic
DNSSEC=allow-downgrade
Cache=yes
StaleRetentionSec=1d

Een server op een netwerk dat je wel draait, met een validerende upstream. Het is al een hop verderop gebeurd. Doe het niet twee keer, en neem geen afhankelijkheid van een publieke resolver.

[Resolve]
DNSSEC=no
DNSOverTLS=no
Cache=yes
CacheFromLocalhost=no
StaleRetentionSec=1d

Een machine waar je het echte werk wilt. Strikt op beide punten, niets voor een aanvaller om te downgraden, en je hebt gecontroleerd dat de upstream het kan dragen.

[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
DNSOverTLS=yes
DNSSEC=yes
Cache=yes

Die derde breekt reverse DNS als iets in je pad .arpa filtert, hij breekt volledig als poort 853 geblokkeerd is, en hij faalt dicht in plaats van stilletjes. Dat zijn de voorwaarden. Ken ze voordat je hem uitrolt, niet erna, want een bak die niks kan resolven is een lange rit als hij niet in de kamer hiernaast staat.

Wat je ook kiest, pas het toe en kijk dan wat de daemon werkelijk besloten heeft in plaats van wat jij schreef:

systemd-analyze cat-config systemd/resolved.conf   # every file, in order
systemctl restart systemd-resolved
resolvectl status                                  # the state it is really in

resolvectl status is hier het orakel, op dezelfde manier waarop de build het orakel is voor een Hugo-site. Een instelling in een bestand is een intentie. Wat status afdrukt is wat er gebeurt.

De rest van de werkwoorden is het waard te kennen, want samen beantwoorden ze bijna elke vraag die je over deze daemon zult hebben zonder dat je een log hoeft te lezen:

CommandoWat het je vertelt
resolvectl statusServers, domeinen en protocollen per link, en de live DNSSEC- en DoT-staat
resolvectl query NAAMHet antwoord, het gebruikte protocol, het DNSSEC-oordeel, en of het uit de cache kwam
resolvectl statisticsCachehits tegen missers, en de lopende telling secure/insecure/bogus
resolvectl show-cacheAlles wat nu in de cache zit, per scope
resolvectl flush-cachesDe cache legen zonder de daemon te herstarten
resolvectl dns LINK ...Servers op één link zetten tijdens het draaien, zonder configuratiebestand
resolvectl dnssec LINK yesValidatie aanzetten voor één link, om te testen voor je het vastlegt
resolvectl domain LINK ~xRouterings- en zoekdomeinen op één link zetten
resolvectl revert LINKElke wijziging tijdens het draaien op die link weggooien
resolvectl show-server-stateFeature-aftasting per server: wat elke upstream bleek te ondersteunen
resolvectl monitorQueries en antwoorden live meekijken, wat beter is dan gokken

Alles in dat middenblok is alleen tijdens het draaien en overleeft niet dat een link opnieuw opkomt, wat het de juiste manier maakt om een instelling te proberen voordat je hem in een drop-in schrijft. Het betekent ook dat nmcli device reapply stilletjes je testwerk ongedaan maakt, dus controleer status erna, niet ervoor.

De standaardwaarden zijn een standpunt, geen ongeluk

Drie functies in de doos. Eén aangezet.

Verleidelijk om dat als luiheid te lezen. Dat is het niet. Over elk van die standaardwaarden is geruzied door mensen die de bugwachtrij aan de andere kant van de beslissing zagen, en Fedora schreef tenminste eerlijk op dat het de ondersteuningslast niet aankon. Dat is een echte beperking en ik zal niet doen alsof dat niet zo is.

Maar een standaardwaarde is een standpunt, en dit standpunt zegt dat een lookup die niemand kan verifiëren een aanvaardbaar ding is om op te bouwen. We hebben de standaard sinds 2005. De registries publiceren de records gratis, de resolver op je machine implementeert het hele ding, en de reden dat hij stilstaat is dat te veel van het internet nooit iets heeft ondertekend, dus hem aanzetten maakt jouw machine degene die er kapot uitziet. Dat is de vorm van elke standaard die niemand afdwingt. Het netjes doen is een kostenpost die je in je eentje draagt, en het voordeel komt pas opdagen als genoeg anderen hem ook hebben gedragen.

En daarom is de telling het deel hiervan waar ik werkelijk iets mee zou doen. Niet de instellingen. De instellingen zijn twintig minuten. De telling zegt dat de organisaties die de richtlijnen publiceren, de resolver leveren en de broncode van de wereld hosten hun eigen zones grotendeels niet hebben ondertekend, en dat gov.uk de apex ondertekende en toen de ene website waar niemand omheen kan op een niet-ondertekende delegatie richtte. Iemand deed het moeilijke deel en keek toen nooit de naam na die mensen typen.

Je kunt alleen je eigen boeltje opknappen. Draai je een zone, onderteken hem, dien de DS in, en ga dan de www-naam opzoeken zoals een bezoeker dat zou doen en bevestig dat de keten tot het eind overleeft. Het is een middag. Doe dat en het argument voor validatie houdt op theoretisch te zijn voor iedereen die jouw naam opzoekt, en dat is het enige deel hiervan dat wij werkelijk kunnen repareren.


  1. systemd-resolved.service(8) — “implements a caching and validating DNS/DNSSEC stub resolver”; de vier clientinterfaces, de twee stub-listeners, synthetische records, de routeringsregels en de vier modi van /etc/resolv.conf. ↩︎ ↩︎ ↩︎ ↩︎

  2. resolved.conf(5), zoals geleverd in systemd 259.9 op Fedora 44 — DNSSEC=, DNSOverTLS=, Cache=, CacheFromLocalhost=, StaleRetentionSec= en de beschrijving van de proxystub op 127.0.0.54. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. resolved.conf(5) — DNSCacheSize=, MulticastDNSCacheSize= en LLMNRCacheSize=, “Each defaults to 4096”, toegevoegd in systemd 261. ↩︎

  4. RFC 5737 — “IPv4 Address Blocks Reserved for Documentation”: 192.0.2.0/24, 198.51.100.0/24 en 203.0.113.0/24. ↩︎

  5. RFC 3849 — 2001:DB8::/32 gereserveerd voor documentatie, “to reduce the likelihood of conflict and confusion when relating documented examples to deployed systems”. ↩︎

  6. resolved.conf(5), upstream latest — DNSSEC= “Defaults to allow-downgrade”, DNSOverTLS= “Defaults to no”, de nummeringsconventie voor drop-ins, en de volledige lijst met directieven zonder optie voor DNS over HTTPS. ↩︎ ↩︎ ↩︎ ↩︎

  7. Fedora systemd.spec, rawhide — de meson-buildvlaggen -Ddefault-dnssec=no, -Ddefault-dns-over-tls=no, -Ddefault-mdns=no, -Ddefault-llmnr=resolve. ↩︎

  8. Debian systemd packaging, debian/rules — -Ddefault-dnssec=no, -Ddefault-llmnr=no, -Ddefault-mdns=no, -Ddns-over-tls=openssl. De systemd van Ubuntu is van deze packaging afgeleid. ↩︎

  9. Fedora Change: systemd-resolved — Fedora 33 maakte het de standaardresolver; verandert de standaard “from the upstream default DNSSEC=allow-downgrade to DNSSEC=no” omdat de functie “is known to cause compatibility problems with certain network access points” en Fedora “is not prepared to handle an influx of DNSSEC-related bug reports”. ↩︎

  10. Fedora Magazine — Use DNS over TLS — de aanbevolen configuratie DNSOverTLS=yes, gegeven met kale IP-adressen en zonder de SNI-vorm #hostname. ↩︎

  11. systemd issue #8639 — “Add support for DNS-over-HTTPS to systemd-resolved”, geopend op 2 april 2018, nog steeds open. ↩︎

  12. Debian 12 release notes, 5.2.3 — “The new systemd-resolved package will not be installed automatically on upgrades”; “until it has been installed, DNS resolution might no longer work”; “systemd-resolved was not, and still is not, the default DNS resolver in Debian.” ↩︎

  13. Debian systemd packaging, debian/control — de stanza systemd-resolved: Provides: resolvconf, Conflicts: resolvconf, openresolv, Replaces: resolvconf, en libnss-resolve alleen vermeld onder Suggests:. ↩︎ ↩︎

  14. resolvconf(1), de resolvectl-compatibiliteitsmodus — resolvectl “is a multi-call binary. When invoked as ‘resolvconf’ … it is run in a limited resolvconf(8) compatibility mode”; systemd-resolved “is the only supported backend”; welke opties worden ondersteund, genegeerd of geweigerd; en de regel dat /etc/resolv.conf alleen wordt geschreven als het een symlink naar /run/systemd/resolve/resolv.conf is. ↩︎ ↩︎

  15. Debian isc-dhcp-client file list — levert /etc/dhcp/dhclient-enter-hooks.d/resolved-enter en /etc/dhcp/dhclient-exit-hooks.d/resolved. ↩︎

  16. RHEL 9.4 release notes, Technology Previews — “Note that systemd-resolved is an unsupported Technology Preview.” Ongewijzigd meegedragen van RHEL 9.0 tot en met 9.8. ↩︎

  17. systemd meson_options.txt — de scheiding tussen opties voor mogelijkheden (resolve, nss-resolve, dns-over-tls, openssl) en opties voor standaardwaarden (default-dnssec, default-dns-over-tls, default-mdns, default-llmnr), en de meegecompileerde fallbacklijst dns-servers. ↩︎ ↩︎