Auf deiner Maschine läuft gerade jetzt ein DNS-Resolver. Du hast ihn nicht installiert, du hast ihn wahrscheinlich nie konfiguriert, und er beantwortet jede Namensabfrage, die die Kiste macht. Auf Fedora, Ubuntu und den meisten Desktop-Linuxen ist das systemd-resolved, und es sitzt dort still seit dem Tag, an dem das System installiert wurde.
Es kann drei Dinge, die es wert sind. Es cacht, sodass dieselbe Abfrage nicht zweimal über das Netz geht. Es validiert DNSSEC, sodass eine gefälschte Antwort verworfen statt geglaubt wird. Und es spricht DNS over TLS, sodass das lokale Netz nicht jeden Namen mitlesen kann, nach dem du fragst.
Standardmäßig tut es genau eines davon. Die anderen beiden sind abgeschaltet, und auf manchen Distributionen sind sie zur Kompilierzeit abgeschaltet, was heißt, dass die Einstellung, die du ändern würdest, gar nicht die Einstellung ist, die es entschieden hat. Ein Validator, der nie validiert, ist weder Nutzen noch Zierde.
Das hier ist, was das Ding tatsächlich tut, gemessen auf einer laufenden Fedora-44-Kiste mit systemd 259, und was sich ändert, wenn du die anderen beiden anschaltest. Einschließlich dessen, was deine Distribution zur Kompilierzeit für dich entschieden hat, und wie viel davon du schlicht überstimmen kannst.
Was deine Abfragen tatsächlich beantwortet
Fang mit dem ehrlichen Bild an, denn „es benutzt /etc/resolv.conf“ ist auf diesen Systemen seit Jahren nicht mehr wahr.
systemd-resolved stellt sich auf vier verschiedene Arten bereit, und welche ein Programm benutzt, entscheidet, was es zurückbekommt.1 Der glibc-Weg ist nss-resolve, eingehängt über /etc/nsswitch.conf. Die nativen Wege sind D-Bus und Varlink, die das DNSSEC-Urteil und den Interface-Scope tragen, die getaddrinfo nicht ausdrücken kann. Dann der Stub-Listener, ein echter DNS-Server auf dem Loopback für alles, was rohes DNS spricht und von all dem oben nichts weiß.
Vier Türen, ein Daemon.
Auf dieser Maschine sieht diese Verdrahtung so aus:
$ 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 dieser Zeile ist nss-resolve. dns dahinter ist das traditionelle nss-dns, das dort als Fallback sitzt und nur greift, wenn resolved überhaupt nicht läuft.
Es gibt zwei Stub-Listener, nicht einen
Jeder kennt 127.0.0.53. Weniger Leute wissen, dass es einen zweiten gibt.
$ 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:*
Das sind nicht zwei Adressen für dieselbe Sache. Die zweite tut bewusst weniger:2
127.0.0.53 | 127.0.0.54 | |
|---|---|---|
| Rolle | Der volle lokale Resolver | Nur Proxy-Modus |
| Cache | Ja | Nein |
| DNSSEC-Validierung | Ja, wenn aktiviert | Nie |
| LLMNR und Multicast DNS | Ja | Nein |
Synthetische Namen (localhost, _gateway) | Ja | Nein |
| Upgrade auf DNS over TLS | Ja | Ja |
| Synthetischer Name dafür | _localdnsstub | _localdnsproxy |
| Du bekommst zurück | Was resolved entschieden hat | Was der Vorgelagerte gesagt hat |
Die Dokumentation ist über die zweite Spalte unverblümt. Der Proxy-Stub werde „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
Das macht 127.0.0.54 zum richtigen Ziel für ein Programm, das seine eigene Validierung machen will, oder für eines, das die rohe Antwort des Vorgelagerten braucht statt resolveds Deutung davon.
Beide Adressen haben synthetische Namen, _localdnsstub und _localdnsproxy, die ganz ohne Konfiguration auflösen.
Vier Modi für /etc/resolv.conf, nicht drei
Der Modus wird automatisch daraus erkannt, was die Datei ist, und es gibt vier davon.1
/etc/resolv.conf ist | Was es heißt | Clients, die NSS umgehen |
|---|---|---|
Symlink auf run/systemd/resolve/stub-resolv.conf | Der empfohlene Modus. Listet 127.0.0.53 und die aktuellen Suchdomänen | Gehen durch resolved |
Symlink auf /usr/lib/systemd/resolv.conf | Statisch, listet 127.0.0.53, trägt keine Suchdomänen | Gehen durch resolved |
Symlink auf run/systemd/resolve/resolv.conf | Listet die echten vorgelagerten Server, aktuell gehalten | Umgehen resolved vollständig |
| eine echte Datei, die etwas anderes verwaltet | resolved liest sie als Konsument, nicht als Lieferant | Umgehen resolved vollständig |
Die dritte Zeile erwischt die Leute. Sie sieht nach der ordentlichen Option aus, sie wird aktuell gehalten, und sie heißt stillschweigend, dass jedes Programm, das resolv.conf direkt liest, mit dem Resolver deines Providers spricht, ohne Cache, ohne Validierung und ohne Verschlüsselung, egal was du in resolved.conf konfiguriert hast.
Die trust-ad-Option in der Datei oben zählt auch. Ohne sie streift glibc das AD-Bit von der Antwort, bevor dein Programm sie sieht, aus dem vernünftigen Grund, dass die Behauptung eines beliebigen Resolvers, etwas validiert zu haben, nichts wert ist. Mit 127.0.0.53 als Nameserver kommt die Behauptung von deiner eigenen Maschine, also ist trust-ad hier richtig, und resolved schreibt es für dich hinein.
Zu den systemd-Verschwörungstheorien
Vor allem Detail, denn das ist das Thema, bei dem es immer auftaucht.
Wenn dein Einwand gegen das Folgende eine Theorie über Lennart Poettering persönlich ist, oder über Red Hats Beweggründe, oder darüber, dass systemd ein Komplott sei, dir etwas wegzunehmen, dann zieh Leine, denn ich habe kein Interesse daran, deine Erfindungen zu hören.
Alles in diesem Beitrag stammt von einer laufenden Maschine: der Quelltext, die Build-Flags, die ausgelieferte Binary, die Manpages und das gemessene Verhalten. Zu jeder Behauptung steht ein Befehl daneben, den du selbst ausführen und prüfen kannst. Die Build-Flags sind öffentlich. Der Quelltext ist öffentlich. Die eine Voreinstellung, die ich für wirklich falsch halte, wurde von Leuten gewählt, die ihre Begründung dort aufgeschrieben haben, wo jeder sie lesen und mit ihnen streiten kann, was genau das ist, was ich weiter unten tue.
Das ist erheblich mehr Transparenz, als du von den meisten Programmen bekommst, die du ohne ein Wort der Klage betreibst.
Bring Belege oder lass es.
Der Cache ist der Teil, der einfach funktioniert
Das ist die eine Funktion, die überall standardmäßig an ist, und die einzige, die sich ganz ohne Konfiguration bezahlt macht.
Die Messung, auf dieser Kiste, über 32 Domänen. Cache leeren, alle abfragen, noch einmal abfragen, und die Query-Zeit lesen, die dig meldet, statt den Prozess zu stoppen:
$ resolvectl flush-caches
$ for n in $NAMES; do dig +tries=1 @127.0.0.53 "$n" A | grep 'Query time'; done
| Median | Mittel | p90 | max | gesamt für 32 Namen | |
|---|---|---|---|---|---|
| kalter Cache | 103,5 ms | 106,7 ms | 184 ms | 238 ms | 3.414 ms |
| warmer Cache | 0,0 ms | 0,5 ms | 1 ms | 3 ms | 16 ms |
3.414 Millisekunden gegen 16. Das ist das ganze Argument für einen lokalen Cache, und deshalb ist er die Voreinstellung.
Es lohnt sich aber, ehrlich zu sein, was diese Zahl ist. Sie ist die Ersparnis über einen Durchlauf von zweiunddreißig Namen, die nie nachgeschlagen worden waren, gegen denselben Durchlauf wiederholt, und keine echte Last sieht wie eines von beiden aus. Die nützliche Zahl ist der Ausläufer statt des Medians: die schlechteste Abfrage in diesem Satz kostete 238 ms kalt und 3 ms warm. Eine Seite, die acht Hostnamen hereinzieht, schert sich nicht um deinen Median, sie wartet auf deinen langsamsten.
Die Regler
[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
| Einstellung | Voreinstellung | Was sie tut |
|---|---|---|
Cache=yes | an | Positive und negative Antworten cachen |
Cache=no-negative | Nur positive Antworten, für wenn du es leid bist, eine negative TTL abzuwarten | |
CacheFromLocalhost=no | an | Überhaupt nicht cachen, wenn der Vorgelagerte auf 127.0.0.1 liegt |
StaleRetentionSec=0 | aus | Abgelaufene Einträge ausliefern, wenn der Vorgelagerte nicht mehr antwortet |
DNSCacheSize=4096 | 4096 | Einträge pro Scope. Erst ab systemd 261 |
Die dritte Zeile ist die, die beißt. Wenn dein Vorgelagerter ein dnsmasq oder ein unbound auf dem Loopback ist, cacht resolved dessen Antworten überhaupt nicht, mit der Begründung, dass das Ding, mit dem es spricht, bereits ein Cache ist. Zeig resolved auf einen filternden Resolver auf einem anderen Host, und du bekommst zwei Cache-Ebenen. Zeig es auf einen auf dem Loopback, und du bekommst eine.
StaleRetentionSec= ist die interessante Einstellung, und sie ist standardmäßig aus. Setz sie, und wenn der Vorgelagerte nicht mehr antwortet, liefert resolved Einträge über ihre TTL hinaus weiter aus, statt zu scheitern.2 Es versucht immer zuerst den Vorgelagerten. Es gilt nicht für NXDOMAIN, denn dass ein Name nicht existiert, ist eine völlig gültige Antwort, und daran ist nichts veraltet. Für einen Laptop, der in die Funkabdeckung hinein und wieder heraus fährt, oder eine Maschine, die durch einen DNS-Ausfall hindurch weiterarbeiten muss, ist das etwas wert:
[Resolve]
StaleRetentionSec=1d
systemd 261 hat darauf aufbauend eine Cache-Dimensionierung pro Protokoll ergänzt, mit DNSCacheSize=, MulticastDNSCacheSize= und LLMNRCacheSize=, jeweils mit 4096 Einträgen voreingestellt und bei 2^24 gedeckelt.3 Nicht auf dieser Kiste, die 259 fährt, und auf keinem aktuellen stabilen Distributionsrelease. Wert zu wissen, dass es kommt, denn bis jetzt war die Cache-Größe überhaupt nicht einstellbar.
Der Daemon leert außerdem alles bei Speicherdruck, was sinnvoll ist und gelegentlich überrascht, wenn du herauszufinden versuchst, warum eine Cache-Trefferquote mau aussieht.
Split DNS ist der Grund, es zu behalten
Wenn du eine Sache hieraus mitnimmst, nimm diesen Abschnitt, denn das ist die Funktion, die wirklich etwas tut, das der alte Stub-Resolver nicht konnte.
Die traditionelle resolv.conf hat eine Liste von Nameservern für die ganze Maschine. Eine Liste. Fahr ein VPN hoch, und irgendetwas muss sie überschreiben, was heißt: entweder funktionieren deine internen Namen und der Rest des DNS geht über den Firmen-Resolver, oder umgekehrt. Eine dritte Möglichkeit gibt es nicht. Die Datei kann sie nicht ausdrücken.
resolved routet pro Abfrage, pro Interface.1 Jeder Link hat seine eigenen Server und seine eigenen Domänen, und eine Abfrage geht an den Link, dessen Domäne am besten zum Namen passt, nach Anzahl der Labels.
| Konfiguration | Geschrieben als | Wirkung |
|---|---|---|
| Suchdomäne | damiendye.uk | Suffix für Namen mit einem Label, und routet passende Abfragen auf diesen Link |
| Nur-Routing-Domäne | ~internal.example | Routet passende Abfragen auf diesen Link, nie als Suffix benutzt |
| Auffangroute | ~. | Schickt alles, was nirgendwo sonst passt, auf diesen Link |
| Standardroute | DNSDefaultRoute=yes | Nimmt Abfragen ohne Treffer, ohne ~. zu beanspruchen |
Ein Laptop mit hochgefahrenem Arbeits-VPN bekommt also das hier:
resolvectl domain tun0 '~corp.example' '~10.in-addr.arpa'
resolvectl dns tun0 10.0.0.53
Namen unter corp.example und Reverse-Lookups für 10.0.0.0/8 gehen durch den Tunnel. Alles andere geht weiter über den lokalen Link hinaus wie vorher. Niemandes resolv.conf wurde umgeschrieben, und wenn der Tunnel abfällt, gehen die Routen mit ihm.
Eine Regel ist es wert, klar gesagt zu werden, denn sie ist die, nach der Leute greifen und die sie falsch verstehen. ~. auf einem Link heißt bevorzuge diesen Link für alles. Es hindert außerdem implizit jeden anderen Link daran, Standardroute zu sein. Du willst, dass ein Link die Reste nimmt, ohne den ganzen Namensraum zu beanspruchen? Setz DNSDefaultRoute=yes und lass ~. in Ruhe.
Prüf, was es entschieden hat, statt es anzunehmen:
$ 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
Jede Adresse in diesem Beitrag stammt aus den Dokumentationsbereichen, 192.0.2.0/24 und 2001:db8::/32, also kopier sie nicht in eine Konfiguration und erwarte eine Antwort.4 5 Die Zahlen und das Verhalten sind echt, von einer laufenden Maschine. Die Adressen sind Platzhalter, denn eine globale IPv6-Adresse, die auf die übliche Art gebildet wird, trägt die MAC des Interfaces in ihrer unteren Hälfte, und eine zu veröffentlichen gibt ein Stück Hardware-Inventar mit heraus.
Dieses -DNSOverTLS und dieses DNSSEC=no sind die nächsten beiden Abschnitte.
Es kann DNSSEC validieren
Der Daemon wird von Upstream als „a caching and validating DNS/DNSSEC stub resolver“ beschrieben.1 Die validierende Hälfte ist echt, sie ist kein Wrapper um etwas anderes, und sie funktioniert. Sie ist nur nicht angeschaltet.
Sie pro Link anzuschalten braucht kein sudo, denn resolvectl geht über polkit, und eine aktive lokale Sitzung darf das:
$ resolvectl dnssec wlp4s0 yes
$ resolvectl status wlp4s0 | grep DNSSEC
DNSSEC=yes/supported
Dieses Suffix /supported ist resolved, das meldet, was es beim Abtasten des Vorgelagerten gefunden hat, getrennt davon, was du verlangt hast. yes/supported heißt, du hast Validierung verlangt und der Server kann sie tragen. no/unsupported auf einer Standardinstallation heißt, niemand hat gefragt, also hat niemand abgetastet.
Sobald sie an ist, kommt jede Abfrage mit einem Urteil zurück, und davon gibt es drei.
Hier sind alle drei auf dieser Maschine, gegen 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 ist signiert, also validiert die Kette von der Root aus und die Daten sind beglaubigt. systemd.io ist nicht signiert, also gibt es nichts zu prüfen, und resolved sagt das ehrlich, statt so zu tun. Das ist die eigene Domäne des systemd-Projekts, ein Punkt, auf den ich zurückkomme. dnssec-failed.org wird mit absichtlich kaputten Signaturen veröffentlicht. Die scheitert hart, mit einer Diagnose, die das genaue Problem benennt.
Hier ist die Unterscheidung, die verloren geht. Insecure ist kein Fehlschlag. Eine unsignierte Zone liefert Daten zurück und sagt dir, dass sie nicht verifiziert werden konnten. Nur eine Zone, die behauptet, signiert zu sein, und es dann nicht beweisen kann, wird verworfen.
Die Kette selbst ist sichtbar, wenn du sie sehen willst:
$ 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...
Die Root bürgt für uk, uk bürgt für damiendye.uk, und der 257-Schlüssel signiert den 256-Schlüssel, der die Einträge signiert. Algorithmus 13 dort ist ECDSA P-256, was du auf einer neuen Zone haben willst statt des RSA, das das uk-DS oben noch benutzt.
Über den einfachen Stub bekommt ein Programm, das nie von resolved gehört hat, dasselbe Urteil als AD-Flag:
$ 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
Und der Daemon führt eine laufende Strichliste, was der schnellste Weg ist, zu sehen, ob die Validierung überhaupt etwas tut:
$ resolvectl statistics
DNSSEC Verdicts
Secure: 134
Insecure: 62
Bogus: 0
Indeterminate: 2
Was Validierung kostet
Hier werde ich dich enttäuschen, denn ich habe versucht, das ordentlich zu messen, und konnte es nicht.
Warm ist es sauber und es ist nichts. Dieselben 32 Namen gegen einen vollen Cache kosteten 16 ms ohne Validierung und 23 ms mit ihr. Nenn es einen Rundungsfehler.
Kalt ist da, wo die Kosten sich zeigen sollten, und ein einzelner Client kann sie nicht isolieren. Ich habe den Durchlauf in beide Richtungen gefahren und widersprüchliche Antworten bekommen: bei der einen Reihenfolge sah Validierung 74 % langsamer aus, bei der anderen sah sie schneller aus, was unmöglich ist und dir sagt, was hier wirklich gemessen wird. Welcher Durchlauf auch immer als zweiter fährt, profitiert davon, dass der vorgelagerte Resolver bereits alles geholt hat, wonach der erste gefragt hat. Die dominierende Variable ist nicht die Validierung, sondern wessen Cache warm war.
Ich gebe dir also keine Zahl, hinter der ich nicht stehen kann. Wahr ist der Mechanismus, und die Dokumentation sagt seine Form klar: Validierung „requires retrieval of additional DNS data, and thus results in a small DNS lookup time penalty“.2 Eine kalte validierte Abfrage läuft die Delegierungskette ab und holt auf jeder Ebene DS und DNSKEY, bevor sie antworten kann, also kostet sie zusätzliche Roundtrips auf Namen, nach denen noch niemand gefragt hat, und gar nichts auf Namen, nach denen irgendwer gefragt hat.
Was auch der Grund ist, warum dieselbe Seite warnt, dass den Cache abzuschalten „comes at a performance penalty, which is particularly high when DNSSEC is used“.2 Die zwei Funktionen sind nicht unabhängig. Validierung ist genau deshalb erschwinglich, weil der Cache heißt, dass du einmal dafür zahlst.
Wenn du die echte Zahl für dein eigenes Netz willst, miss sie dort. Ein Client auf einer Leitung kann sie dir nicht sagen.
Warum ist Validierung also abgeschaltet
Weil deine Distribution sie beim Bauen des Pakets abgeschaltet hat, und das aus einem Grund tat, den sie aufgeschrieben hat.
Upstream-systemd liefert Validierung an aus. Die Build-Option sagt es:
option('default-dnssec', type : 'combo',
choices : ['yes', 'allow-downgrade', 'no'],
value : 'allow-downgrade')
und das Upstream-Handbuch stimmt zu: DNSSEC= „Defaults to allow-downgrade“.6
Jetzt schau, was Fedora diesem Build übergibt:7
-Ddefault-dnssec=no
-Ddefault-dns-over-tls=no
-Ddefault-mdns=no
-Ddefault-llmnr=resolve
Und was Debian und Ubuntu übergeben:8
-Ddefault-dnssec=no
-Ddefault-llmnr=no
-Ddefault-mdns=no
-Ddns-over-tls=openssl
Die Datei unter /usr/lib/systemd/resolved.conf auf einer Fedora-Kiste, die mit „Entries in this file show the compile time defaults“ überschrieben ist, liest sich also #DNSSEC=no. Lies das noch einmal, denn es tut etwas Verschlagenes: die Zeile sieht aus, als sagte die Software dir ihre eigene Voreinstellung, und was sie dir tatsächlich zurückspiegelt, ist Fedoras Build-Flag, während Upstreams echte Voreinstellung nirgends auf der Seite steht.
| Einstellung | Upstream-Voreinstellung | Fedora-Build | Debian/Ubuntu-Build | Ergebnis auf dieser Kiste |
|---|---|---|---|---|
DNSSEC= | allow-downgrade | no | no | DNSSEC=no |
DNSOverTLS= | no | no | no (mit OpenSSL kompiliert) | -DNSOverTLS |
MulticastDNS= | yes | no | no | -mDNS |
LLMNR= | yes | resolve | no | LLMNR=resolve |
Jede einzelne davon passt zu dem, was resolvectl status auf dieser Maschine ausgibt. Quelltext, Build-Flag, laufendes System, alle drei stimmen überein.
Fedoras Begründung steht im Change Proposal und ist erfrischend unverblümt. Die Funktion „is known to cause compatibility problems with certain network access points“, und Fedora „is not prepared to handle an influx of DNSSEC-related bug reports“, also geht sie aus.9
Darf ich fragen, warum die Antwort auf eine Funktion, die in schlechten Netzen bricht, ist, sie für alle abzuschalten, statt allow-downgrade auszuliefern, wie Upstream es tut, und sie sich dort selbst abschalten zu lassen, wo sie muss? Nicht wer entschieden hat. Was im Prozess dorthin geführt hat. Denn allow-downgrade existiert genau für den Fall des Captive Portals, es ist aus genau diesem Grund Upstreams Voreinstellung, und stattdessen no auszuliefern heißt, dass eine Maschine in einem völlig guten Netz ebenfalls keine Validierung bekommt.
Fairerweise: allow-downgrade hat sein eigenes Problem, und es ist ein echtes. Der Modus erkennt einen Resolver, der kein DNSSEC kann, und hört leise auf zu validieren. Ein Angreifer, der deine DNS-Antworten formen kann, kann diese Erkennung absichtlich auslösen, und die Dokumentation sagt es klar: er „makes DNSSEC validation vulnerable to ‘downgrade’ attacks“.2 Eine Sicherheitsfunktion, die jeder Angreifer abschalten kann, tut weniger, als sie aussieht.
Also ist keine der beiden Voreinstellungen gut. no gibt dir nichts. allow-downgrade gibt dir etwas, das ein Angreifer dir wegnehmen kann, und yes gibt dir das echte Ding plus alles im nächsten Abschnitt.
Wie viel des Webs ist überhaupt signiert
Bevor man viel Mühe in Validierung steckt, lohnt es sich zu wissen, welchen Anteil deiner Abfragen sie überhaupt schützen kann. Also habe ich resolved nach dem Urteil über den Apex von zweiunddreißig Domänen gefragt, die dieses Thema tatsächlich betreffen: die Gremien, die den Standard geschrieben haben, die Läden, die den Resolver ausliefern, und die Infrastruktur, die jeder auflöst, ob er will oder nicht.
Sechzehn von zweiunddreißig. Die Hälfte.
| Signiert | Unsigniert | |
|---|---|---|
| Standards und Registries | ietf.org, iana.org, icann.org, rfc-editor.org, ripe.net, isc.org, nlnetlabs.nl, nic.cz, afnic.fr, verisign.com | keine |
| Distributionen und Hersteller | debian.org, fedoraproject.org, opensuse.org, almalinux.org | redhat.com, ubuntu.com, canonical.com, suse.com, rockylinux.org, archlinux.org |
| systemd selbst | keine | systemd.io, freedesktop.org |
| Infrastruktur | cloudflare.com, gov.uk | github.com, kernel.org, google.com, wikipedia.org, mozilla.org, apache.org, gnu.org, quad9.net |
Lies die erste Spalte hinunter. Jedes einzelne Standardisierungsgremium und jede Registry hat signiert. Zehn von zehn, ohne Ausnahme: die Leute, die DNSSEC geschrieben haben, und die Leute, die die Registries betreiben, die die DS-Einträge für alle anderen veröffentlichen. Sie haben die Arbeit an ihren eigenen Zonen gemacht.
Jetzt lies die dritte Zeile quer. systemd.io hat kein DS. Das Projekt, das den validierenden Resolver geschrieben hat, um den es in diesem ganzen Beitrag geht, hat seine eigene Domäne nicht signiert, und freedesktop.org, wo seine Dokumentation liegt, auch nicht. Darunter ist quad9.net ebenfalls unsigniert, was einen Moment verdient: ein öffentlicher Resolver, dessen ganzes Verkaufsargument ist, dass er DNSSEC für dich validiert, auf einer Zone, die niemand validieren kann.
Und die Hersteller teilen sich sauber entlang einer Linie. Die Community-Distributionen haben signiert. debian.org, fedoraproject.org, opensuse.org, almalinux.org. Die Firmen nicht. redhat.com, ubuntu.com, canonical.com, suse.com. Das sind die vier Organisationen, die diesen Resolver für den größten Teil des Linux-Bestands paketieren und ausliefern.
An der Technik liegt es nicht. Das Protokoll ist seit über fünfzehn Jahren einsetzbar, die Werkzeuge sind kostenlos, und die Registries nehmen den DS-Eintrag entgegen, ohne dafür zu kassieren. Ich sag dir das umsonst: die Leute, die den Standard geschrieben haben, haben signiert, und die meisten Leute, die die Software ausliefern, nicht.
Es gibt eine schärfere Fassung desselben Punktes in gov.uk, das signiert ist und dann das hier tut:
$ 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 hat ein DS. gov.uk hat ein DS. service.gov.uk hat keines, also endet die Kette dort abrupt, und www.gov.uk löst insecure auf, obwohl der Apex darüber ordentlich signiert ist. Jemand hat die Arbeit an gov.uk gemacht und dann die eigentliche Website auf eine unsignierte Delegierung gezeigt. Halbe Arbeit.
Wenn du also eine Zone signierst, prüf die Namen, die Leute tatsächlich tippen. Ein signierter Apex, der per CNAME in eine unsignierte CDN-Zone zeigt, bringt dir nichts.
DNS over TLS funktioniert, mit Bedingungen
DNS over TLS ist implementiert, es ist in jeden verbreiteten Build einkompiliert, und es funktioniert. Es ist überall standardmäßig aus, auch bei Upstream, wo DNSOverTLS= „Defaults to no“.6
Die Konfiguration sind zwei Zeilen, und die zweite ist die, die Leute übersehen:
[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
DNSOverTLS=yes
Dieses #dns.quad9.net ist keine Verzierung. Es setzt den Namen, der für SNI und für die Prüfung des Zertifikats benutzt wird. Lass ihn weg, und das Zertifikat wird stattdessen „checked against the server’s IP“.2 Das funktioniert bei den großen Anbietern, weil die IP-Adressen in die SANs ihrer Zertifikate schreiben, aber es ist die schwächere Prüfung, sie bricht in dem Moment, in dem ein Anbieter damit aufhört, und sie schützt dich nicht davor, auf eine andere Adresse umgeleitet zu werden, die zufällig ein gültiges Zertifikat für sich selbst hält. Fedoras eigener Magazin-Artikel dazu lässt den Hostnamen weg,10 was schade ist, denn die Syntax steht direkt in den Kommentaren der ausgelieferten Konfigurationsdatei.
Strikt und opportunistisch sind sehr verschiedene Einstellungen
| Modus | Auf einem Server, der DoT kann | Auf einem Server, der es nicht kann | Authentifiziert den Server |
|---|---|---|---|
yes | Verschlüsselt | Alle Abfragen scheitern | Ja |
opportunistic | Verschlüsselt | Stillschweigend im Klartext | Nein |
no | Klartext | Klartext | entfällt |
opportunistic liest sich wie der vernünftige Mittelweg und ist es meistens nicht. Die Dokumentation sagt es geradeheraus: in diesem Modus „the resolver is not capable of authenticating the server, so it is vulnerable to ‘man-in-the-middle’ attacks“,2 und jeder, der deinen Verkehr auf Port 853 verwerfen kann, kann das Downgrade erzwingen. Es schützt dich vor passiver Beobachtung in einem Netz, in dem niemand es versucht. Gegen jemanden, der es versucht, tut es nichts.
yes ist die ehrliche Einstellung. Sie heißt auch, dass du, wenn es keine Verbindung bekommt, gar kein DNS bekommst, was es wert ist zu wissen, bevor du sie auf eine Maschine setzt, zu der du nicht hinlaufen kannst.
Ich habe striktes DoT zu Quad9 von dieser Kiste aus versucht, und die Abfrage hing. Kein Fehler, kein Timeout, nichts im Journal zwischen dem Leeren und meinem Zurücknehmen zwei Minuten später:
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, ...
Ich kann dir von hier aus nicht sagen, ob Port 853 auf dieser Leitung erreichbar ist, denn die Shell, aus der ich den Test gefahren habe, erreichte auch Port 443 nicht und war offensichtlich selbst gefiltert. Was das Log zeigt, ist der Fehlermodus: striktes DoT, das keine Verbindung bekommt, meldet nichts Nützliches. Es wartet. Wenn du das anschaltest und dein DNS verstummt, prüf ss -tn dport = :853, bevor du bei resolved zu suchen anfängst.
Es ordentlich 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'
Der zweite Befehl ist der, der die Wahrheit sagt. Mit funktionierendem DoT sollte nichts die Maschine auf Port 53 verlassen, und alles, was es doch tut, ist ein Programm, das einen Weg um den Stub herum gefunden hat.
DNS over HTTPS existiert hier nicht
Klare Antwort auf die Frage: systemd-resolved unterstützt kein DNS over HTTPS. Nicht teilweise, nicht hinter einem Flag, nicht mit einer Build-Option, die niemand anschaltet. Es ist überhaupt kein DoH darin.
Und das ist nicht aus der Dokumentation abgelesen, die schlicht veraltet sein könnte. Es ist, was die Binary enthält:
$ 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"
Kein DoH-Wire-Format, kein HTTP/2, eine Verschlüsselungs-Eigenschaft auf dem D-Bus-Interface, und die ist TLS. Die vollständige Liste der resolved.conf-Direktiven bei Upstream umfasst siebzehn Einstellungen, und keine davon erwähnt HTTPS.6
Es ist gefordert worden. Issue #8639, „Add support for DNS-over-HTTPS to systemd-resolved“, wurde am 2. April 2018 eröffnet und ist immer noch offen, mit zwei angehängten Pull Requests und nichts gemergt.11 Acht Jahre und es läuft weiter.
Ist das die falsche Entscheidung? Nicht offensichtlich, und das Argument für DoT ist ordentlich. Beide tragen DNS innerhalb von TLS, und beide stoppen denselben passiven Beobachter. DoHs Vorteil ist, dass es sich mit allem anderen auf Port 443 versteckt, sodass ein Netz, das verschlüsseltes DNS blockieren will, sehr viel härter arbeiten muss. Das ist wirklich nützlich, wenn der Netzbetreiber der Gegner ist.
Es schneidet auch in die andere Richtung. Wo der Betreiber du bist, heißt diese Ununterscheidbarkeit, dass du auch dein eigenes DNS nicht prüfen kannst, und jede Anwendung, die ihren eigenen DoH-Client mitbringt, hört auf, den Systemresolver zu benutzen, was der Weg ist, auf dem du einen Browser bekommst, der dein Split DNS, deinen Cache und deine internen Zonen ignoriert. Auf deiner eigenen Ausrüstung ist ein Resolver, der auf einem bekannten Port sichtbar ist, eine Funktion.
Also: wenn DoT deinen Resolver erreicht, benutz es und du verlierst nichts. Wenn Port 853 blockiert ist, hat resolved keine Antwort, und du willst einen DoH-Proxy davor, dnscrypt-proxy oder cloudflared auf dem Loopback mit resolved darauf gezeigt. Caching und Validierung bleiben resolveds Aufgabe. Nur der Transport zieht um.
Was deine Distribution tatsächlich ausliefert
Derselbe Daemon verhält sich sehr unterschiedlich, je nachdem, wer ihn paketiert hat. Den Dienst zu aktivieren ist die leichte Hälfte. Die Hälfte, die übersprungen wird, ist, ihn in die Netzwerkwerkzeuge einzustöpseln, die die Distribution tatsächlich benutzt, und auf einer dieser vier ist er wirklich nicht eingestöpselt, bis du es selbst tust.
| Installiert | Aktiviert | nss-resolve eingehängt | Konfiguration kommt über | Unterstützt | |
|---|---|---|---|---|---|
| Fedora (33+) | ja | ja | ja, in systemd-libs | NetworkManager | ja |
| Ubuntu | ja | ja | ja | netplan, dann NM oder networkd | ja |
| Debian (12+) | eigenes Paket | nein | nein, eigenes Paket | du wählst und hängst es ein | ja |
| RHEL / Rocky / Alma (9, 10) | ja | nein | ja | NetworkManager, einmal gesagt | Technology Preview |
Validierung und ein voller Cache: die Einstellungen selbst
Das sind überall dieselben vier Zeilen, denn resolved.conf ist auf jeder Distribution resolved.conf. Was sich unterscheidet, ist nur, wie der Rest der Konfiguration den Daemon erreicht, und das sind die nächsten vier Abschnitte.
# /etc/systemd/resolved.conf.d/60-local.conf
[Resolve]
DNSSEC=allow-downgrade
Cache=yes
CacheFromLocalhost=no
StaleRetentionSec=1d
Benutz ein Drop-in, statt /etc/systemd/resolved.conf zu editieren, denn die Hauptdatei hat niedrigere Priorität als jedes Drop-in, und ein Paketupdate kann sich mit dir darüber streiten. Die Nummerierung ist eine dokumentierte Konvention: Hersteller nehmen 10 bis 40 unter /usr/, du nimmst 60 bis 90 unter /etc/, damit deines gewinnt.6
Auf systemd 261 und neuer gibt es eine fünfte Zeile, die es wert ist, ergänzt zu werden, denn bis dahin war die Cache-Größe überhaupt nicht einstellbar:
DNSCacheSize=16384 # systemd 261+; default 4096, max 2^24
Wende es an und bestätige, dass der Daemon mit der Datei übereinstimmt, statt anzunehmen, dass er sie gelesen hat:
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 ist die Einstellung, die ich auf eine Maschine setzen würde, die ich nicht bewachen werde, aus den Gründen im Abschnitt oben. DNSSEC=yes ist die ehrliche, und sie scheitert geschlossen. Wähl bewusst.
Es in den Netzwerkstapel einstöpseln
Den Dienst anzuschalten ist ein Befehl. Die eigenen Netzwerkwerkzeuge deiner Distribution dazu zu bringen, ihm ihre DNS-Konfiguration zu übergeben, ist der Teil, der variiert, und es ist der Punkt, an dem „aktiviert“ und „funktioniert richtig“ auseinandergehen.
| Woher die DNS-Konfiguration kommt | DNSSEC anschalten | Cache dimensionieren | Zusätzliche Verdrahtung nötig | |
|---|---|---|---|---|
| Fedora | NetworkManager, automatisch | Drop-in | Drop-in | keine |
| Ubuntu | netplan, dann NM oder networkd | Drop-in, oder pro Link in .network | Drop-in | keine |
| Debian | der Stapel, den du gewählt hast | Drop-in, oder pro Link in .network | Drop-in | libnss-resolve, plus der Stapel unten |
| RHEL-Familie | NetworkManager, einmal gesagt | Drop-in | Drop-in | dns=systemd-resolved |
Fedora
Die vollständigste Integration der vier, und die einzige, bei der schon alles eingestöpselt ist.
NetworkManager besitzt das Netz und übergibt DNS an resolved, ohne dass man es ihm sagen muss. Auf dieser Kiste gibt es nirgends in /etc/NetworkManager/ eine dns=-Zeile, und es funktioniert trotzdem, weil NetworkManager den laufenden Daemon erkennt und ihn benutzt. /etc/resolv.conf ist der Stub-Symlink, und glibc ist eingehängt, weil libnss_resolve.so.2 in systemd-libs mitkommt, was nicht optional ist:
$ 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
Auf Fedora ist das Drop-in oben also die ganze Arbeit. Sonst nichts zu verbinden.
Ubuntu
Standardmäßig aktiviert, und nss-resolve ist auf dieselbe Art eingehängt. Der Unterschied ist, dass die Konfiguration meist über netplan ankommt:
network:
version: 2
ethernets:
enp1s0:
dhcp4: true
nameservers:
addresses: [9.9.9.9, 149.112.112.112]
search: [example.com]
Netplan übergibt das an seinen Renderer, NetworkManager auf dem Desktop oder systemd-networkd auf dem Server, und der Renderer übergibt es an resolved. Beachte, was netplan nicht ausdrücken kann. Es gibt keinen netplan-Schlüssel für DNSSEC= und keinen für DNSOverTLS=. Die gehören in das Drop-in oben, wo sie ohnehin hingehören.
Wenn du auf systemd-networkd bist, sind die Einstellungen pro Link auch nativ in der .network-Datei verfügbar, und eine Einstellung pro Link schlägt die globale:
# /etc/systemd/network/10-lan.network
[Network]
DNS=9.9.9.9#dns.quad9.net
DNSSEC=yes
DNSOverTLS=yes
Domains=~.
Debian muss von Hand eingehängt werden
Das ist die eine, die wirklich nicht eingestöpselt ist, und der Grund, warum der Abschnitt oben existiert.
Seit Debian 12 ist systemd-resolved ein eigenes Paket, und die Release Notes sind über das Upgrade ausdrücklich: „The new systemd-resolved package will not be installed automatically on upgrades“ und „until it has been installed, DNS resolution might no longer work since the service will not be present on the system.“12 Dieselben Notes klären die größere Frage: „systemd-resolved was not, and still is not, the default DNS resolver in Debian.“
Es zu installieren sind drei Pakete, nicht eines, und das zweite ist das, das jeder übersieht:
apt install systemd-resolved libnss-resolve
systemctl enable --now systemd-resolved
libnss-resolve steht nur unter Suggests:, ist keine Abhängigkeit,13 und Suggests ist die eine Beziehung, auf die apt nicht reagiert. Also installiert es niemand.
Der Grund, warum es niemandem auffällt, ist interessanter als der Grund, warum es niemand installiert. Lass es weg, und glibc erreicht resolved trotzdem, weil /etc/resolv.conf auf den Stub zeigt und das einfache nss-dns mit ihm spricht. Der größte Teil des Daemons funktioniert weiter:
Ohne libnss-resolve | Mit ihm | |
|---|---|---|
| Cache | Ja, über den Stub | Ja |
| DNSSEC-Validierung | Ja, sie passiert im Daemon | Ja |
| DNS over TLS | Ja | Ja |
AD-Bit erreicht glibc | Ja, resolv.conf trägt trust-ad | Ja |
| Secure gegen insecure gegen bogus | Nein, nur ein Bit | Ja |
| Link-bezogene Adressen | Nein | Ja |
/etc/hosts vom Daemon gelesen | Nein | Ja |
Nichts in der linken Spalte sieht kaputt aus, also wird nichts repariert. Was du verlierst, ist das, was das DNS-Wire-Format nicht tragen kann, und der klarste Fall ist eine Adresse mit einem Scope daran:
$ 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
Derselbe Name, derselbe Daemon, zwei verschiedene Antworten. Eine link-lokale IPv6-Adresse ist ohne das Interface, auf das sie bezogen ist, bedeutungslos, und in einer DNS-Antwort gibt es nirgends einen Platz dafür, also kann der Stub nur die IPv4 zurückgeben. Die native API hat einen Platz dafür und gibt dir die Adresse, die du tatsächlich wolltest.
Die Urteilszeile ist dasselbe Problem. AD ist ein Bit, signiert oder nicht. Die native API trennt secure von insecure von bogus, was der Unterschied ist zwischen „das hat niemand signiert“ und „das hat jemand signiert, und jemand anderes war dran“. Eine Anwendung, die das interessiert, kann die beiden über die Leitung nicht auseinanderhalten.
Das ist die Lücke zwischen dem Dienst, der läuft, und dem Dienst, der eingestöpselt ist, und sie bleibt unsichtbar, bis du danach suchst.
Prüf, dass es angekommen ist:
grep ^hosts: /etc/nsswitch.conf # wants 'resolve [!UNAVAIL=return] dns'
Wie der Rest deines Netzwerks ihn erreicht, hängt davon ab, auf welchem von Debians Stapeln du bist.
Das Paket deklariert Provides: resolvconf und Conflicts: resolvconf, openresolv,13 übernimmt also die resolvconf-Schnittstelle vollständig. /usr/sbin/resolvconf wird ein Symlink auf resolvectl, das eine Multi-Call-Binary ist: unter diesem Namen aufgerufen spricht es das resolvconf(8)-Protokoll und schiebt alles, was ihm übergeben wird, direkt in resolved.14 Alles, was bereits resolvconf -a aufruft, funktioniert daher unverändert weiter, mit systemd-resolved als einzigem unterstütztem Backend.
Nicht die ganze Schnittstelle überlebt, und die Lücken scheitern laut statt leise:
resolvconf-Option | Unter systemd-resolved |
|---|---|
-a <iface> | Registriert DNS pro Link, von stdin gelesen. Die, auf die es ankommt |
-d <iface> | Meldet ab, dasselbe wie resolvectl revert |
-x | Auf eine ~.-Routing-Domäne abgebildet |
-p | Markiert den Link als keine Standardroute (systemd 257+) |
-f | Sorgt dafür, dass -a und -d über ein nicht vorhandenes Interface schweigen |
-m | Angenommen und stillschweigend ignoriert |
-u, -i, -I, -l, -r, -R, -v, -V | Nicht unterstützt. Der Befehl scheitert |
Eine Falle ist es wert, bekannt zu sein, bevor du sie debuggst: der Shim schreibt /etc/resolv.conf nur dann, wenn diese Datei ein Symlink auf /run/systemd/resolve/resolv.conf ist, und nicht, wenn sie eine statische Datei ist.14
ifupdown, die Voreinstellung bei einer Debian-Serverinstallation. Diedns-nameservers-Anweisung in/etc/network/interfaceswurde immer von den Hooks des altenresolvconf-Pakets umgesetzt, undsystemd-resolvedsteht mit diesem Paket im Konflikt und liefert keine eigenen/etc/network/if-up.d/-Hooks. Verlass dich bei einem statischen Interface nicht auf die Anweisung. Setz die Server ausdrücklich auf dem Link und lass resolved sie besitzen:# /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 $IFACEDHCP auf ifupdown ist bereits erledigt.
isc-dhcp-clientliefert Hooks, die nach dem Daemon benannt sind,/etc/dhcp/dhclient-enter-hooks.d/resolved-enterund/etc/dhcp/dhclient-exit-hooks.d/resolved,15 sodass die DNS-Server eines Lease ohne jede Konfiguration auf dem richtigen Link landen.systemd-networkdist die sauberste Option und braucht gar keinen Shim, denn die beiden Hälften sind dasselbe Projekt. DNS, DNSSEC und DoT pro Link sind native Schlüssel in der.network-Datei, genau wie im Ubuntu-Beispiel oben.NetworkManager, auf einem Debian-Desktop, muss es einmal gesagt bekommen:
# /etc/NetworkManager/conf.d/10-resolved.conf [main] dns=systemd-resolved
Wähl einen und wisse, welchen du gewählt hast. Der Fehlermodus hier ist kein Daemon, der den Start verweigert, es sind zwei Stapel, die beide glauben, /etc/resolv.conf zu besitzen, was sich als sporadisches DNS liest und einen Nachmittag kostet. resolvectl status sagt, auf welchem Link die Server gelandet sind. Wenn die Antwort keiner davon ist, füttert nichts den Daemon, und das ist der Fehler.
RHEL, Rocky und Alma
Und hier ist die, die es wert ist, zweimal gelesen zu werden. Das Paket ist da, nss-resolve ist verfügbar, NetworkManager kann es ansteuern, und Red Hats Dokumentation sagt das hier:
systemd-resolved is an unsupported Technology Preview.16
Diese Formulierung steht seit 9.0 in den RHEL-9-Release-Notes und steht bei 9.8 immer noch da. Technology Preview heißt kein Produktions-SLA, und Red Hat empfiehlt es ausdrücklich nicht für den Produktionseinsatz.
Aktiviert ist es auch nicht. NetworkManager besitzt auf diesen Systemen resolv.conf, es anzuschalten ist also eine NetworkManager-Einstellung statt nur die Unit zu aktivieren:
# /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
Danach gilt das Drop-in oben unverändert, und Validierung und Cache verhalten sich genau wie auf Fedora.
Auf RHEL hast du also eine Entscheidung, die es auf den anderen nicht gibt. Du willst einen validierenden, cachenden, Split-DNS-fähigen lokalen Resolver auf einer unterstützten RHEL-Kiste? Dann ist die unterstützte Antwort nicht diese. Sie heißt unbound, oder dnsmasq über NetworkManagers eigenes dns=dnsmasq, und hinter beide stellt Red Hat sich tatsächlich.
Das Etikett ist keine Aussage über den Code. Es ist eine Aussage darüber, wofür Red Hat ein SLA geben will, und ein cachender validierender Resolver ist ein Ding, zu dem Kunden Tickets aufmachen. Fair genug. Aber wenn du RHEL wegen des Supports kaufst, ist die Namensauflösung auf einer nicht unterstützten Komponente zu betreiben eine Entscheidung, die man bewusst trifft und aufschreibt, und nicht eine, in die man hineinrutscht, weil es im Repo lag.
Nichts ist tatsächlich verstümmelt, und so beweist du es
Die Prämisse ist es wert, geprüft zu werden, denn „meine Distro hat es abgeschaltet, ich werde neu bauen müssen“ ist der Reflex, und hier ist er falsch.
systemd hat zwei völlig verschiedene Arten von Build-Option, und über sie wird geredet, als wären sie eine:17
| Build-Option | Art | Upstream | Fedora | Debian und Ubuntu | Zur Laufzeit änderbar |
|---|---|---|---|---|---|
resolve | Fähigkeit | an | gebaut | true | nein, und beide bauen es |
nss-resolve | Fähigkeit | aktiviert | gebaut, liegt in systemd-libs | enabled, liegt als libnss-resolve bei | nein, und beide bauen es |
dns-over-tls | Fähigkeit | auto | auto, löst auf OpenSSL auf | openssl | nein, und beide kompilieren es mit ein |
openssl | Fähigkeit | aktiviert | enabled | enabled | nein, und beide aktivieren es |
default-dnssec | nur Voreinstellung | allow-downgrade | no | no | ja, in einem Drop-in |
default-dns-over-tls | nur Voreinstellung | no | no | Upstream-Voreinstellung | ja, in einem Drop-in |
default-mdns | nur Voreinstellung | yes | no | no | ja, in einem Drop-in |
default-llmnr | nur Voreinstellung | yes | resolve | no | ja, in einem Drop-in |
Lies die letzte Spalte. Jede Einstellung, über die sich dieser Beitrag beschwert hat, sitzt in der unteren Hälfte, und jede einzelne ist eine Voreinstellung, keine Fähigkeit. Der Code ist auf beiden einkompiliert. Niemand hat etwas entfernt.
Der Beweis steht weiter oben in diesem Beitrag und brauchte keinen Compiler. Auf dem Standard-Fedora-Paket, mit eingebackenem DNSSEC=no, hat ein Befehl die Validierung angeschaltet, und sie funktionierte vollständig: eine signierte Zone beglaubigt, eine unsignierte ehrlich gemeldet, eine kaputte mit einer präzisen Diagnose verworfen. Wäre sie herauskompiliert worden, hätte resolvectl status nie yes/supported gesagt.
Prüf also deinen eigenen Build, bevor du nach einer Toolchain greifst:
# 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
Auf dieser Fedora-Kiste ist das -GCRYPT +GNUTLS +OPENSSL, was reichlich ist: DNSSEC braucht eines davon, und DoT ist gegen OpenSSL gebaut.
Was die Distributionen wirklich herausnehmen
Es gibt eine Sache, die beide herausnehmen, und sie steht nicht auf der Liste, über die sich irgendwer beschwert.
Upstream kompiliert eine Fallback-Liste von DNS-Servern in die Binary: Cloudflare, Google und Quad9.17 Sowohl Fedora als auch Debian bauen mit leerem -Ddns-servers=, und du kannst es an der ausgelieferten Binary bestätigen, statt mir zu glauben:
$ strings /usr/lib/systemd/systemd-resolved | grep -ciE 'quad9|one\.one\.one|dns\.google'
0
Nichts. Kein Hyperscaler in die ausführbare Datei eingebacken.
Das ist die eine Stelle, an der beide Distributionen Upstream verbessert haben, und das verdient es, klar gesagt zu werden, angesichts dessen, wie viel dieses Beitrags über Voreinstellungen ging, die ich für falsch halte. Eine Maschine, die ihre DNS-Konfiguration verliert, sollte laut scheitern und auf dich warten. Sie sollte nicht still jeden Namen, den du nachschlägst, an einen Resolver in einer anderen Rechtsordnung schicken, den du nie gewählt hast. Du willst einen Fallback? Setz FallbackDNS= und wähl, wer es ist.
Wenn du wirklich einen Neubau brauchst
Ein echter Fall existiert: ein minimaler oder eingebetteter Build, bei dem jemand -Ddns-over-tls=false übergeben hat und der Transport tatsächlich fehlt. Prüf zuerst mit den Befehlen oben, denn es ist selten und sieht genauso aus, als wäre die Einstellung schlicht aus.
Wenn du es doch brauchst, bau das Paket neu, nie 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
Ich habe keines von beiden auf dieser Maschine gefahren, also nimm sie als die Form der Arbeit, nicht als geprüftes Rezept. Das Prinzip ist, worauf es ankommt. systemd ist PID 1, und ein handgebautes make install über die Kopie deiner Distribution nimmt es aus der Paketverwaltung heraus: keine Sicherheitsupdates mehr, und das nächste Upgrade kämpft mit dir um die Dateien. Das Paket zu bauen behält beides. Für fast jeden ist die ehrliche Antwort, dass ein vierzeiliges Drop-in dieselbe Arbeit in zwanzig Sekunden erledigt, weshalb dieser Abschnitt vor allem existiert, um dich davon abzubringen.
Drei Konfigurationen, die es wert sind
Keine Speisekarte aller Optionen. Drei Positionen, von denen ich jede tatsächlich verteidigen würde.
| Laptop, feindliche Netze | Server, dein eigenes Netz | Strikt | |
|---|---|---|---|
| Wogegen du dich verteidigst | Das Café und das Hotelportal | Nichts Lokales; der Vorgelagerte validiert bereits | Ein Resolver-Pfad, dem du nicht traust |
DNSSEC= | allow-downgrade | no | yes |
DNSOverTLS= | opportunistic | no | yes |
StaleRetentionSec= | 1d | 1d | nicht gesetzt |
| Überlebt ein Captive Portal | Ja | entfällt | Nein |
Überlebt ein gefiltertes .arpa | Ja | Ja | Nein |
| Überlebt blockierten Port 853 | Ja | entfällt | Nein |
| Ein Angreifer kann es downgraden | Ja, beide Einstellungen | entfällt | Nein |
Ein Laptop in Netzen, die du nicht kontrollierst. Verschlüssele den Transport, und nimm das Downgrade-Risiko dafür in Kauf, dass die Sache hinter einem Captive Portal noch funktioniert.
# /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
Ein Server in einem Netz, das du betreibst, mit validierendem Vorgelagerten. Es ist einen Hop entfernt bereits passiert. Tu es nicht zweimal, und nimm keine Abhängigkeit von einem öffentlichen Resolver auf.
[Resolve]
DNSSEC=no
DNSOverTLS=no
Cache=yes
CacheFromLocalhost=no
StaleRetentionSec=1d
Eine Maschine, auf der du das echte Ding willst. Strikt in beiden Punkten, nichts, was ein Angreifer downgraden kann, und du hast geprüft, dass der Vorgelagerte es tragen kann.
[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
DNSOverTLS=yes
DNSSEC=yes
Cache=yes
Die dritte bricht Reverse DNS, wenn irgendetwas in deinem Pfad .arpa filtert, sie bricht vollständig, wenn Port 853 blockiert ist, und sie scheitert geschlossen statt still. Das sind die Bedingungen. Kenn sie, bevor du sie ausrollst, nicht danach, denn eine Kiste, die nichts auflösen kann, ist eine lange Fahrt, wenn sie nicht im Nebenraum steht.
Was du auch wählst, wende es an und prüf dann, was der Daemon tatsächlich entschieden hat, statt dessen, was du geschrieben hast:
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 ist hier das Orakel, so wie der Build das Orakel für eine Hugo-Site ist. Eine Einstellung in einer Datei ist eine Absicht. Was status ausgibt, ist, was passiert.
Die übrigen Verben sind es wert, gekannt zu werden, denn zusammen beantworten sie fast jede Frage, die du zu diesem Daemon haben wirst, ohne ein Log zu lesen:
| Befehl | Was er dir sagt |
|---|---|
resolvectl status | Server, Domänen und Protokolle pro Link, und der aktuelle DNSSEC- und DoT-Zustand |
resolvectl query NAME | Die Antwort, das benutzte Protokoll, das DNSSEC-Urteil, und ob sie aus dem Cache kam |
resolvectl statistics | Cache-Treffer gegen Fehlschläge, und die laufende Strichliste secure/insecure/bogus |
resolvectl show-cache | Alles, was gerade gecacht ist, pro Scope |
resolvectl flush-caches | Den Cache leeren, ohne den Daemon neu zu starten |
resolvectl dns LINK ... | Server auf einem Link zur Laufzeit setzen, ohne Konfigurationsdatei |
resolvectl dnssec LINK yes | Validierung für einen Link anschalten, um vor dem Festschreiben zu testen |
resolvectl domain LINK ~x | Routing- und Suchdomänen auf einem Link setzen |
resolvectl revert LINK | Jede Laufzeitänderung auf diesem Link wegwerfen |
resolvectl show-server-state | Feature-Abtastung pro Server: was bei jedem Vorgelagerten gefunden wurde |
resolvectl monitor | Abfragen und Antworten live mitschauen, was besser ist als raten |
Alles in diesem mittleren Block gilt nur zur Laufzeit und überlebt nicht, dass ein Link wieder hochkommt, was es zur richtigen Art macht, eine Einstellung auszuprobieren, bevor du sie in ein Drop-in schreibst. Es heißt auch, dass nmcli device reapply dein Testen stillschweigend rückgängig macht, also prüf status danach, nicht davor.
Die Voreinstellungen sind eine Position, kein Versehen
Drei Funktionen in der Box. Eine angeschaltet.
Verlockend, das als Faulheit zu lesen. Ist es nicht. Über jede dieser Voreinstellungen wurde von Leuten gestritten, die die Bug-Queue auf der anderen Seite der Entscheidung sehen konnten, und Fedora hat wenigstens ehrlich aufgeschrieben, dass es die Supportlast nicht tragen konnte. Das ist eine echte Einschränkung, und ich werde nicht so tun, als wäre es anders.
Aber eine Voreinstellung ist eine Position, und diese sagt, dass eine Abfrage, die niemand prüfen kann, etwas ist, worauf zu bauen in Ordnung geht. Wir haben den Standard seit 2005. Die Registries veröffentlichen die Einträge kostenlos, der Resolver auf deiner Maschine implementiert das Ganze, und der Grund, warum er brachliegt, ist, dass zu viel des Internets nie irgendetwas signiert hat, sodass ihn anzuschalten deine Maschine zu der macht, die kaputt aussieht. Das ist die Form jedes Standards, den niemand durchsetzt. Ihn ordentlich zu machen ist ein Aufwand, den du allein trägst, und der Nutzen taucht erst auf, wenn genug andere ihn ebenfalls getragen haben.
Weshalb die Bestandsaufnahme der Teil davon ist, bei dem ich tatsächlich handeln würde. Nicht die Einstellungen. Die Einstellungen sind zwanzig Minuten. Die Bestandsaufnahme sagt, dass die Organisationen, die die Leitlinien veröffentlichen, den Resolver ausliefern und den Quelltext der Welt hosten, ihre eigenen Zonen größtenteils nicht signiert haben, und dass gov.uk den Apex signiert und dann die eine Website, an der niemand vorbeikommt, auf eine unsignierte Delegierung gezeigt hat. Jemand hat den schweren Teil gemacht und dann nie den Namen geprüft, den Leute tippen.
Du kannst nur deins in Ordnung bringen. Wenn du eine Zone betreibst, signier sie, hinterleg das DS, und schlag dann den www-Namen so nach, wie ein Besucher es täte, und bestätige, dass die Kette bis zum Ende überlebt. Es ist ein Nachmittag. Tu das, und das Argument für Validierung hört auf, für jeden, der deinen Namen auflöst, theoretisch zu sein, was der einzige Teil davon ist, den irgendwer von uns tatsächlich reparieren kann.
systemd-resolved.service(8)— „implements a caching and validating DNS/DNSSEC stub resolver“; die vier Client-Schnittstellen, die zwei Stub-Listener, synthetische Einträge, die Routing-Regeln und die vier/etc/resolv.conf-Modi. ↩︎ ↩︎ ↩︎ ↩︎resolved.conf(5), wie in systemd 259.9 auf Fedora 44 ausgeliefert —DNSSEC=,DNSOverTLS=,Cache=,CacheFromLocalhost=,StaleRetentionSec=und die Beschreibung des127.0.0.54-Proxy-Stubs. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎resolved.conf(5)—DNSCacheSize=,MulticastDNSCacheSize=undLLMNRCacheSize=, „Each defaults to 4096“, ergänzt in systemd 261. ↩︎RFC 5737 — „IPv4 Address Blocks Reserved for Documentation“:
192.0.2.0/24,198.51.100.0/24und203.0.113.0/24. ↩︎RFC 3849 —
2001:DB8::/32für Dokumentation reserviert, „to reduce the likelihood of conflict and confusion when relating documented examples to deployed systems“. ↩︎resolved.conf(5), upstream latest —DNSSEC=„Defaults toallow-downgrade“,DNSOverTLS=„Defaults tono“, die Nummerierungskonvention für Drop-ins, und die vollständige Direktivenliste ohne eine Option für DNS over HTTPS. ↩︎ ↩︎ ↩︎ ↩︎Fedora
systemd.spec, rawhide — die meson-Build-Flags-Ddefault-dnssec=no,-Ddefault-dns-over-tls=no,-Ddefault-mdns=no,-Ddefault-llmnr=resolve. ↩︎Debian
systemdpackaging,debian/rules—-Ddefault-dnssec=no,-Ddefault-llmnr=no,-Ddefault-mdns=no,-Ddns-over-tls=openssl. Ubuntus systemd leitet sich von dieser Paketierung ab. ↩︎Fedora Change: systemd-resolved — Fedora 33 machte es zum Standard-Resolver; ändert die Voreinstellung „from the upstream default
DNSSEC=allow-downgradetoDNSSEC=no“, weil die Funktion „is known to cause compatibility problems with certain network access points“ und Fedora „is not prepared to handle an influx of DNSSEC-related bug reports“. ↩︎Fedora Magazine — Use DNS over TLS — die empfohlene
DNSOverTLS=yes-Konfiguration, angegeben mit nackten IP-Adressen und ohne die#hostname-SNI-Form. ↩︎systemd issue #8639 — „Add support for DNS-over-HTTPS to systemd-resolved“, eröffnet am 2. April 2018, immer noch offen. ↩︎
Debian 12 release notes, 5.2.3 — „The new
systemd-resolvedpackage 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.“ ↩︎Debian
systemdpackaging,debian/control— diesystemd-resolved-Stanza:Provides: resolvconf,Conflicts: resolvconf, openresolv,Replaces: resolvconf, undlibnss-resolvenur unterSuggests:gelistet. ↩︎ ↩︎resolvconf(1), the resolvectl compatibility mode — 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“; welche Optionen unterstützt, ignoriert oder abgelehnt werden; und die Regel, dass /etc/resolv.conf nur geschrieben wird, wenn es ein Symlink auf /run/systemd/resolve/resolv.conf ist. ↩︎ ↩︎Debian
isc-dhcp-clientfile list — liefert/etc/dhcp/dhclient-enter-hooks.d/resolved-enterund/etc/dhcp/dhclient-exit-hooks.d/resolved. ↩︎RHEL 9.4 release notes, Technology Previews — „Note that
systemd-resolvedis an unsupported Technology Preview.“ Unverändert von RHEL 9.0 bis 9.8 übernommen. ↩︎systemd
meson_options.txt— die Trennung zwischen Fähigkeits-Optionen (resolve,nss-resolve,dns-over-tls,openssl) und Voreinstellungs-Optionen (default-dnssec,default-dns-over-tls,default-mdns,default-llmnr), und die einkompiliertedns-servers-Fallback-Liste. ↩︎ ↩︎