Jede Maschine in einer Active-Directory-Domäne findet ihren Domänencontroller, indem sie DNS fragt. Nicht aus einer konfigurierten Liste. Sie fragt nach einem SRV-Eintrag, und sie geht dorthin, wohin die Antwort sie schickt. Das macht eine Handvoll Einträge unter _msdcs zum sicherheitskritischsten Ding in der Zone und wirft zwei Fragen auf, die es wert sind, ordentlich beantwortet zu werden: wie signierst du sie, und wer darf sie schreiben. Keine wird oft gestellt, und beide werden standardmäßig schlecht beantwortet.
Dieser Beitrag beantwortet beide für einen Samba4-Domänencontroller. BIND mit dlz_bind9, das das Verzeichnis bereitstellt, Inline-Signing, ein Hidden Primary, den nie ein Client erreicht, eine Delegierung innerhalb einer öffentlichen Zone, sodass die Vertrauenskette bis zur Root läuft, und dynamische Client-Updates weit weg von den Locators.
Aber Signieren ist nur etwas wert, wenn du weißt, was es schützt, und das heißt, eine Ebene tiefer anzufangen — bei der Frage, wie ein Name überhaupt aufgelöst wird. Das erste Drittel hier ist also der Gang von der Root hinunter. Wenn du das schon im Schlaf beherrschst, spring zu Sambas zwei DNS-Backends.
Ein Resolver kommt fast ohne Wissen zur Welt
Es lohnt sich, klar zu sein, mit wie wenig ein Resolver geboren wird, denn alles andere in diesem Beitrag folgt daraus.
Ein frisch installierter rekursiver Resolver kennt zwei Dinge. Er kennt die Adressen der Root-Server — eine Hints-Datei, dreizehn Namen von a.root-servers.net bis m.root-servers.net, per Anycast von weit mehr Instanzen als dreizehn bereitgestellt. Und, wenn er validiert, kennt er einen öffentlichen Schlüssel: den der Root.
Das ist die gesamte eingebaute Konfiguration. Jede andere Tatsache, die er je bereitstellen wird — welche Server autoritativ für uk sind, wo deine Domäne lebt, welche Adresse dein Mailserver hat — wird zur Laufzeit durch Fragen gelernt und dann gecacht, bis ihre TTL abläuft.
Das ist ein gutes Design. Niemand muss eine Liste der Nameserver des Internets ausliefern, und keine zentrale Stelle muss eine Änderung an deiner Zone genehmigen. Aber es hat eine Folge, um die es im Rest dieses Beitrags geht: ein Resolver glaubt, was ihm gesagt wird, von genau dem Server, auf den die vorherige Antwort ihn verwiesen hat. Nimm die Signaturen weg, und die ganze Struktur ist eine Kette von Behauptungen, jede nur dadurch beglaubigt, dass sie von der Adresse ankam, die die letzte Antwort genannt hat. Das ist viel Gewicht, das an einer Absenderadresse hängt.
Der Gang den Baum hinunter
Eine Abfrage ist nicht eine Frage. Sie ist eine Reihe von Verweisen den Namensbaum hinunter, und jeder Schritt ist eine eigene Anfrage an einen anderen Server.
Sagen wir, ein Client will dc01.ad.example.co.uk. Ein Resolver mit kaltem Cache tut das:
- Frag einen Root-Server. Die Root kennt die Antwort nicht und tut auch nicht so. Sie gibt einen Verweis zurück: einen leeren Antwortabschnitt, und im Authority-Abschnitt die
NS-Einträge füruk, mit den Adressen dieser Nameserver als Glue im Additional-Abschnitt. - Frag einen
uk-Server. Noch ein Verweis, diesmal zu den Nameservern fürexample.co.uk. - Frag einen
example.co.uk-Server. Wennad.example.co.ukdelegiert ist — und im Design später in diesem Beitrag ist es das — noch ein Verweis. - Frag einen
ad.example.co.uk-Server. Dieser ist autoritativ für den Namen, also antwortet er mit demA-Eintrag und setzt das AA-Bit (Authoritative Answer).
Drei Dinge an diesem Gang zählen später.
Der Verweis ist die Delegierung. Der Elternteil einer Zone hält ihren Inhalt nicht. Er hält NS-Einträge, die sagen „frag da drüben“, und — sobald das Signieren ins Spiel kommt — einen DS-Eintrag, der sagt „und hier ist der Fingerabdruck des Schlüssels, den du dann erwarten solltest“. Delegierung ist der einzige strukturelle Mechanismus, den DNS hat, und sie ist das, womit du eine interne Zone intern hältst.
Fast alles davon wird gecacht. Die Root- und TLD-Verweise haben lange TTLs, also springt ein warmer Resolver direkt zu Schritt drei oder vier. Deshalb lohnt es sich, den Cache eines Resolvers anzugreifen: vergifte einen Eintrag, und du hast alles darunter umgeleitet, so lange wie die TTL, die du gewählt hast.
Der volle Name wird nicht an jeden Server geschickt. Unter QNAME-Minimierung fragt ein Resolver die Root nur nach uk, nicht nach dc01.ad.example.co.uk. Gut zu wissen, falls du je nach deinen internen Namen in einem Query-Log weiter oben suchst. Sie sollten nicht dort sein.
Stub, rekursiv, autoritativ
Drei Wörter, die austauschbar benutzt werden und recht verschiedene Aufgaben meinen. Die Unterscheidung ist für die Active-Directory-Hälfte dieses Beitrags tragend, also lohnt es sich, sie festzunageln.
| Was er tut | Läuft den Baum? | Hält Zonendaten? | |
|---|---|---|---|
| Stub-Resolver | Die Bibliothek oder der lokale Dienst, den deine Anwendungen aufrufen. Fragt einen konfigurierten Server und nimmt die Antwort. | Nein | Nein |
| Forwarder | Reicht Anfragen an einen anderen Resolver weiter, cacht die Antworten. | Nein | Nein |
| Rekursiver Resolver | Macht den Gang oben, cacht jeden Schritt, validiert optional Signaturen. | Ja | Nein |
| Autoritativer Server | Antwortet für Zonen, die ihm gegeben wurden, und nur für die. Sagt „weiß ich nicht“ zu allem anderen. | Nein | Ja |
Die wichtige Zeile ist die letzte. Ein autoritativer Server hat bei Rekursion nichts verloren, und ein rekursiver Resolver hat nichts damit zu tun, autoritativ zu sein. Beide zu kombinieren ist, wie du eine Maschine bekommst, die sowohl beliebige Fragen von Clients annimmt als auch Daten hält, von denen diese Clients abhängen — was genau die Maschine ist, die dein Domänencontroller nicht sein soll.
Auf einem modernen Linux-Desktop gibt es eine weitere Ebene, die man kennen sollte. systemd-resolved betreibt einen Stub-Listener auf der Loopback-Adresse und schreibt eine resolv.conf, die auf sich selbst zeigt, mit options edns0 trust-ad. Dieses trust-ad ist das, was man beachten muss: es sagt dem Stub, dem AD-Bit (Authenticated Data) in Antworten von diesem Server zu glauben. Das AD-Bit ist für sich genommen kein Beweis für irgendetwas — es ist die Behauptung des vorgelagerten Resolvers, dass er validiert hat. Ihm zu vertrauen ist vernünftig, wenn der Vorgelagerte deiner ist und der Hop zu ihm vertrauenswürdig, und ansonsten bedeutungslos.
Wie Dienste tatsächlich gefunden werden
Ein A-Eintrag beantwortet eine Frage: welche Adresse hat dieser Name. Er sagt nichts darüber, auf welchem Port der Dienst liegt, welchen von mehreren Servern man bevorzugen soll oder was zu tun ist, wenn der erste ausfällt.
SRV-Einträge beantworten alle drei. Aus RFC 2782 ist die Form:
_service._proto.name. TTL IN SRV priority weight port target
Jedes Feld verdient seinen Platz:
- Die Unterstrich-Präfixe halten die Dienst-Labels in einem eigenen Namensraum.
_tcpkann niemals mit einem Host kollidieren, der tatsächlichtcpheißt, denn ein Hostname darf nicht mit einem Unterstrich beginnen. - Priorität ist die Failover-Reihenfolge — niedriger wird zuerst versucht. Server mit einer höheren Prioritätsnummer werden nur benutzt, wenn alles darunter unerreichbar ist.
- Gewicht teilt Last innerhalb einer Priorität. Ein Paar mit Gewicht 100 und Gewicht 300 bekommt grob ein Viertel und drei Viertel der Clients. Es ist eine proportionale Ziehung, kein Round-Robin.
- Port befreit den Dienst von einer wohlbekannten Nummer, was ist, wie ein Client einen LDAP-Server auf 3268 findet, ohne dass jemand es fest verdrahtet.
- Ziel muss ein Name mit
A- oderAAAA-Einträgen sein. RFC 2782 ist ausdrücklich, dass es kein CNAME sein darf und dass ein einzelnes Ziel von.heißt „dieser Dienst wird hier bewusst nicht angeboten“ — etwas Nützliches, um es absichtlich zu veröffentlichen.
Das ist, was eine Domäne auffindbar statt konfiguriert macht. Eine domänenverbundene Maschine hat keine Liste deiner Domänencontroller. Sie hat einen Domänennamen, und sie fragt.
Die Namen, die Active Directory veröffentlicht
Eine AD-Domäne ist aus Sicht eines Clients größtenteils ein Satz von SRV-Einträgen. Der Baum unter _msdcs ist der interessante Teil, denn er ist, wie Clients „einen LDAP-Server“ von „einem Domänencontroller für diese Domäne“ von „einem Global Catalogue für diesen Forest“ unterscheiden:
| Name | Was danach fragt |
|---|---|
_ldap._tcp.<domain> | Alles, was LDAP in der Domäne will |
_ldap._tcp.dc._msdcs.<domain> | Domänencontroller-Lokalisierung — die große |
_ldap._tcp.pdc._msdcs.<domain> | Speziell der PDC-Emulator |
_ldap._tcp.gc._msdcs.<forest> | Global Catalogue |
_kerberos._tcp.<domain>, _kerberos._udp.<domain> | KDC-Lokalisierung, bevor irgendein Ticket existiert |
_kpasswd._tcp.<domain>, _kpasswd._udp.<domain> | Passwortänderungen |
_ldap._tcp.<site>._sites.dc._msdcs.<domain> | Site-bewusste Lokalisierung — finde einen DC, der nah ist |
Sieh dir an, wofür diese Liste da ist. Kerberos-Authentifizierung kann nicht beginnen, bevor der Client einen KDC gefunden hat, und er findet den KDC per DNS. Site-bewusste Lokalisierung heißt, die Antwort entscheidet auch, gegen welches Rechenzentrum ein Client sich authentifiziert.
Dieselbe Idee, modernisiert
SRV hat einen Nachfolger, den man kennen sollte. SVCB- und HTTPS-Einträge (RFC 9460) verallgemeinern das Muster: Dienstparameter in DNS, einschließlich ALPN, Port und Adress-Hinweisen, in einem Eintrag. So lernt ein Browser, direkt zu HTTP/3 zu gehen, ohne einen Redirect. Der HTTPS-Eintrag ist bereits weit verbreitet. Der Mechanismus ist im Geiste derselbe wie SRV: dem Client wird von DNS gesagt, wie er den Dienst erreicht. Was heißt, dass das Argument im nächsten Abschnitt genauso auf ihn zutrifft.
Alles nach der Abfrage vertraut der Abfrage
Hier ist der Angelpunkt, und er ist der Grund, warum ein DNS-Beitrag einen Sicherheitsabschnitt hat statt umgekehrt.
Eine domänenverbundene Maschine bootet und fragt, wo ein Domänencontroller ist. Sie bekommt einen Namen und einen Port. Sie verbindet dorthin, und dann fängt sie an, all die Dinge zu tun, die wir normalerweise für die Sicherheitsschicht halten: Kerberos, LDAP-Signing, Channel Binding, Zertifikatsvalidierung.
Was passiert also, wenn die Antwort eine Lüge war?
Fair zu sein zählt hier, denn die Antwort ist nicht „sofortige Katastrophe“. Kerberos wurde mit gegenseitiger Authentifizierung entworfen, genau damit ein Client nicht seiner Namensabfrage ausgeliefert ist: ein Host, der kein Service-Ticket für den Namen vorweisen kann, nach dem der Client gefragt hat, kann den Austausch nicht abschließen. Bekomm eine SRV-Antwort, die auf eine Maschine ohne Schlüssel in der Domäne zeigt, und für einen streng konfigurierten Client scheitert es.
Der realistische Schaden ist subtiler, und er liegt ganz in den Lücken drumherum:
- Downgrade. Ein Client, der auf NTLM zurückfällt, wenn Kerberos nicht funktioniert, ist gerade an den ausgeliefert worden, der geantwortet hat.
- Relay und Coercion. Der Angreifer muss nicht der DC sein. Der Name zu sein, zu dem ein Client verbindet, reicht, um damit anzufangen, diese Authentifizierung irgendwohin zu relayen, wo sie nützlich ist.
- Denial of Service, der wie ein Fehler aussieht. Zeige
_ldap._tcp.dc._msdcsauf etwas, das nicht antwortet, und die Domäne ist zeitweise kaputt, auf eine Weise, die eine gute Weile niemand als DNS diagnostiziert. - Alles ohne jede gegenseitige Authentifizierung. Zeitsynchronisation, Syslog, Monitoring, Backup-Agenten, dieses interne HTTP-Ding mit
verify=False. Reichlich Umgebungen haben mehr davon, als sie zugeben würden.
Das allgemeine Prinzip ist das, was man mitnehmen sollte: DNS ist ein Entdeckungsmechanismus, kein Autorisierungsmechanismus. Es ist völlig vernünftig, einen Dienst per Namen zu finden. Es ist nicht vernünftig, aufgrund eines Namens irgendetwas zu gewähren, und die Zahl der Systeme, die es leise tun — PTR-basierte Allowlists, Hostnamen-ACLs, „es ist im internen Netz, also muss es unseres sein“ — ist die eigentliche Angriffsfläche.
Was DNSSEC behebt und was nicht
DNSSEC existiert, um eine Frage zu beantworten: kam diese Antwort wirklich unverändert von der Zone, der der Name gehört?
Es funktioniert (RFC 4033 und die zwei darauf folgenden) durch Signieren und durch das Verketten der Signaturen mit etwas, dem du bereits vertraust:
- Jedes RRset in einer signierten Zone hat ein
RRSIG— eine Signatur über diesen Satz. - Die öffentlichen Schlüssel der Zone werden als
DNSKEYveröffentlicht. - Die Elternzone veröffentlicht einen
DS-Eintrag: einen Hash des Kindschlüssels. - Dieses
DSist selbst vom Elternteil signiert, dessen Schlüssel imDSseines Elternteils gehasht ist, den ganzen Weg hinauf zur Root — und der Schlüssel der Root ist das eine Ding, das dein Resolver von Geburt an kennt.
Verweigerung wird auch signiert, was leicht zu übersehen ist und mehr zählt, als es klingt. Ohne sie ist „kein solcher Name“ eine unbeglaubigte Antwort, die ein Angreifer fälschen kann, um etwas verschwinden zu lassen. NSEC und NSEC3 geben ein beglaubigtes „zwischen diesen zwei Namen existiert nichts“.
Das ist eine echte Lösung für ein echtes Problem. Cache-Poisoning ist nicht theoretisch: der Kaminsky-Angriff machte Off-Path-Spoofing billig genug, um über das gesamte Internet hinweg Notfall-Patches zu erzwingen, und die Gegenmaßnahmen, die folgten — Quellport-Randomisierung, 0x20-Groß-Klein-Mischung — sind allesamt Versuche, das Raten schwerer zu machen, nicht Antworten überprüfbar. Seitenkanal-Arbeit hat seither wiederholt an ihnen genagt. Signaturen sind das Einzige in dieser Liste, das das Spiel ändert, statt den Preis zu erhöhen.
Jetzt die ehrlichen Grenzen, denn DNSSEC wird in beide Richtungen überverkauft.
Es ist keine Vertraulichkeit. Signieren ist Public-Key-Authentifizierung; jede Anfrage und Antwort ist auf dem Draht immer noch im Klartext. Privatsphäre auf dem Client-Hop ist DoT, DoH oder DoQ, und die sind ein anderer Mechanismus, der ein anderes Problem löst. Den Hop zu einem Resolver zu verschlüsseln, der nicht validiert, kauft dir ein privates Gespräch mit etwas, das immer noch angelogen werden kann.
Es validiert nicht Bedeutung, nur Herkunft. DNSSEC beweist, dass der Besitzer der Zone dies veröffentlicht hat. Wenn der Eintrag falsch ist, oder bösartig, oder von jemandem geschrieben, dem die Zone unklugerweise das Schreiben erlaubte, wird die Signatur genauso treu darauf angewandt. Eine Zone zu signieren, die nicht vertrauenswürdige Maschinen schreiben können, macht den Inhalt nicht vertrauenswürdig — es beglaubigt die Lüge. Halt diesen Satz fest; das letzte Drittel dieses Beitrags ist im Wesentlichen seine Folge.
Es hilft nur, wenn jemand validiert. Wenn dein Resolver nicht validiert, sind Signaturen auf den Zonen, die du abfragst, Dekoration. Und wenn Validierung an einem Resolver quer durchs Netz vom Client geschieht, vertraut der Client dem AD-Bit und dem Pfad — siehe trust-ad oben.
Und es muss betrieben werden. Eine abgelaufene Signatur ist keine verschlechterte Antwort, sie ist SERVFAIL — der Name geht dunkel. Ein DS im Elternteil, das nicht mehr zum Schlüssel des Kindes passt, tut dasselbe. Das ist die ehrliche Kosten, und deshalb zählt die Automatisierung in den nächsten Abschnitten mehr als das anfängliche Signieren.
Warum eine erfundene interne TLD nicht signiert werden kann
Hier kommen Namensentscheidungen, die vor Jahren getroffen wurden, mit einer Rechnung zurück, und es lohnt sich, es zu buchstabieren, weil es der Grund ist, warum das Design dieses Beitrags eine echte, besessene Domäne für interne Namen benutzt.
Sieh noch einmal, wie die Kette gebaut wird: der Schlüssel einer Zone wird durch einen DS-Eintrag in ihrem Elternteil verbürgt. Ein validierender Resolver kann eine interne Zone also nur authentifizieren, wenn diese Zone einen Elternteil hat, der willens und in der Lage ist, ein DS für sie zu veröffentlichen.
ad.corp.local hat keinen solchen Elternteil. Genauso wenig .lan, .home oder irgendetwas anderes, das an dem Tag erfunden wurde, an dem die Domäne bereitgestellt wurde:
.localist für mDNS reserviert. Es für Unicast-DNS zu benutzen ist nicht bloß unsigniert, es ist eine dokumentierte Kollision damit, wie jedes Betriebssystem im Gebäude sich verhalten darf. Es bekommt unten einen eigenen Abschnitt, weil es in einer anderen Liga spielt als die anderen drei..lan,.corp,.homesind nicht registrierte Zeichenketten. Es gibt keinen Elternteil, der einDShält, und die beantragten Versionen wurden wegen des Namenskollisions-Chaos in privaten Umgebungen von der Delegierung zurückgehalten.home.arpaist ordentlich für Heimnetze reserviert, was ideal klingt — aber seine Delegierung ist bewusst unsicher. Es gibt keinen signierten Pfad hinunter zu ihm, per Design..internalwurde von ICANN genau für diesen Zweck beiseitegelegt, und es löst das Kollisionsproblem. Dieses löst es nicht: keine Delegierung heißt keinDS, also keine Kette.
Deine Optionen mit einer nicht verketteten internen Zone sind, sie unsigniert zu lassen, oder einen lokalen Trust Anchor auf jedem validierenden Resolver in der Umgebung zu konfigurieren und zur Root deiner eigenen privaten Insel zu werden — ein Schlüssel, den du jetzt von Hand verteilen, überwachen und rollen musst, auf jedem Resolver, für immer, mit SERVFAIL über die gesamte Domäne als Fehlermodus.
Es gibt eine viel einfachere Antwort, und sie ist gratis: mach die interne Zone zu einer Delegierung innerhalb einer öffentlichen Zone, die du bereits besitzt und bereits signierst. ad.example.com, delegiert von example.com. Das DS geht in den öffentlichen Elternteil. Jeder validierende Resolver in der Umgebung authentifiziert interne Namen dann mit dem Trust Anchor, den er schon hat, und du verteilst nichts.
Der Inhalt bleibt drinnen. Nur das Vertrauen kommt von außen. Diese Unterscheidung ist das Design im Rest dieses Beitrags.
.local ist keine Stilfrage
Eine dieser vier verdient einen eigenen Abschnitt, weil sie die ist, die Leute verteidigen, und weil die Verteidigung immer dieselbe ist: es funktioniert, wir benutzen es seit Jahren, wo ist das Problem.
Das Problem ist, dass .local keine nicht registrierte Zeichenkette ist, die jemand eines Tages verkaufen könnte. Es ist ein Namensraum, der bereits einen Besitzer und ein definiertes Verhalten hat, und das Verhalten ist nicht „frag den DNS-Server“. RFC 6762 §3 ist darüber nicht zimperlich:
Any DNS query for a name ending with “.local.” MUST be sent to the mDNS IPv4 link-local multicast address 224.0.0.251 (or its IPv6 equivalent FF02::FB).
Lies, was dieses MUST tatsächlich regelt. Es ist keine Aussage darüber, wem die Zeichenkette gehört. Es ist eine Anweisung darüber, wohin die Anfrage geht — und die Antwort ist eine Multicast-Gruppe auf dem lokalen Link, nicht dein Domänencontroller. Derselbe Abschnitt sagt, Namen unter .local seien „meaningful only on the link where they originate“, das DNS-Äquivalent einer 169.254-Adresse.
Ein Client, der die Spezifikation korrekt befolgt, wird deinen DNS-Server also nie nach einem .local-Namen fragen. Er ruft auf dem Draht, und nimmt, was auch immer antwortet.
Das erzeugt eine Reihe von Fehlern mit einem sehr bestimmten Geschmack:
- Auflösung hängt vom Client ab, nicht von deinem DNS. macOS löst
.localüber Bonjour auf und tat es immer;systemd-resolvedroutet dielocal-Domäne standardmäßig zu mDNS; Windows macht mDNS seit Windows 10. Drei Stacks, drei Regelsätze, und deine Zonendatei wird von keinem von ihnen konsultiert. - Es endet am ersten Router. mDNS ist per Design link-local. Ein Name, der an einem Schreibtisch im selben VLAN auflöst, löst nicht von einer anderen Etage, einem anderen Standort oder dem VPN auf — was das „funktioniert im Büro, kaputt zu Hause“-Ticket ist, das viermal als „Netzwerkproblem“ geschlossen wird, bevor jemand die Spezifikation liest.
- Das Verhalten ändert sich unter dir. Ob eine gegebene Maschine zuerst Multicast, zuerst Unicast oder beides parallel versucht, hängt vom Resolver-Stack und seiner Version ab. Umgebungen, die „seit Jahren einwandfrei“ auf
.localliefen, sind meist Umgebungen, in denen ein Distributions-Update die Reihenfolge noch nicht geändert hat. - Der Beweis fehlt dort, wo du ihn suchst. Das Query-Log des DC zeigt nichts, weil nichts ankam. Leute verbringen Tage am Server, und die Anfrage hat den Client nie verlassen.
- Und es kann nicht signiert werden, was der Punkt dieses Abschnitts ist. Kein Elternteil, kein
DS, keine Kette, kein Weg, eine zu veröffentlichen.
Jetzt leg das unter eine Active-Directory-Domäne. Der Realm wird vom Domänennamen abgeleitet. Die Service-Principals werden vom Realm abgeleitet. Die _msdcs-Locators — die Einträge, um die es in diesem ganzen Beitrag geht — sitzen unter einem Suffix, das ein konformer Client per Rufen ins lokale Segment auflösen muss. Du baust Kerberos auf einem Namensraum auf, den die Hälfte deiner Umgebung mit einem Protokoll auflöst, das zum Finden von Druckern entworfen wurde.
Und der Ausstieg ist teuer, was die ursprüngliche Entscheidung wert macht, unsentimental zu sein. Auf Samba gibt es keine In-Place-Umbenennung. Der dokumentierte Weg ist samba-tool domain backup rename gefolgt von einem Restore: du nimmst eine umbenannte Kopie der Datenbank, säst daraus einen neuen DC, und fügst jeden anderen DC von Grund auf neu hinzu, ohne dass eine Überlappung zwischen Alt-Namen- und Neu-Namen-DCs erlaubt ist. Es ist nicht Windows’ rendom, das die DCs wenigstens einen nach dem anderen durchgeht. Es ist ein Neuaufbau mit einem hübscheren Namen.
Also die ehrliche Zusammenfassung. .local für eine Unicast-Domäne ist keine Vorliebe, keine Konvention und kein harmloses Stück Altlast. Es ist ein dokumentierter Konflikt mit einem Protokoll, das auf jedem Betriebssystem im Gebäude aktiviert ausgeliefert wird, und die Rechnung kommt Jahre später als zeitweise, nicht reproduzierbare Auflösungsfehler an — und, wenn du die Zone endlich signieren willst, als ein Domänen-Neuaufbau.
Derselbe Fehler mit einem anderen Hut
Wenn wir schon dabei sind: Port 5353.
mDNS lauscht auf UDP 5353, und es ist kein Ersatzport, der zufällig frei ist. Einen normalen Unicast-DNS-Dienst darauf zu stellen, oder Resolver auf :5353 zu zeigen, weil 53 belegt war oder Root brauchte, richtet denselben Schaden aus der anderen Richtung an — jede mDNS-fähige Maschine in diesem Segment spricht jetzt mit deinem Namensdienst, und dein Namensdienst fängt jetzt Multicast-Dienstsuche ab, die er nie beantworten sollte. Der Name und der Port sind zwei Hälften eines Namensraums, und beide gehören bereits jemand anderem.
.local auf 53, oder Unicast-DNS auf 5353. Dasselbe Missverständnis, dieselbe Klasse zeitweiser Fehler, dieselben Wochen der Zeit von jemand anderem.
Und sei ehrlich darüber, was das aussagt
Microsoft empfahl .local in der Small-Business-Server-Ära, weshalb so viele Umgebungen es immer noch tragen, und hörte vor langer Zeit auf, es zu empfehlen. Eine .local-Domäne zu erben ist Pech. Die meisten, die das hier lesen und eine haben, haben sie nicht gewählt, und der Abschnitt oben ist ein Migrationsplan statt einer Anschuldigung.
Eine zu deployen ist eine völlig andere Sache, und ich werde deutlich dazu sein, denn das abzumildern hat niemandem geholfen.
Wer .local für Active Directory oder für Standard-Unicast-DNS deployt, oder einen DNS-Dienst auf Port 5353 stellt, ist kein IT-Profi. Er mag den Jobtitel tragen. Er macht den Job nicht. RFC 6762 ist seit 2013 veröffentlicht, in zwanzig Minuten lesbar, und der Satz, der die ganze Frage klärt, steht in Abschnitt 3. Den Namensdienst eines Unternehmens auf einem Namensraum zu bauen, den die Spezifikation für Link-Local-Multicast reserviert, ist keine vertretbare technische Entscheidung. Es ist jemand, der in dem einen Teil des Stacks rät, in dem Raten Fehler erzeugt, die niemand reproduzieren kann und alle dem Netzwerk anlasten.
Er sollte aus deiner IT-Abteilung entfernt werden. Nicht seitwärts versetzt, nicht mit DNS unter Aufsicht betraut. Aus der Funktion entfernt. Die Rolle existiert, um zu wissen, welches Paket wohin geht und auf wessen Autorität. Wer das Dokument nicht gelesen hat, das den Namensraum regelt, den er für jede Maschine im Gebäude gewählt hat, erfüllt diese Rolle nicht, und ihn darin zu halten heißt, dass die nächste Entscheidung dieser Größe genauso getroffen wird.
Und jedes System, das er angefasst hat, sollte auditiert werden. Das ist der Teil, den Leute überspringen, und der Teil, der am meisten zählt. Eine Entscheidung wie diese ist nie isoliert. Wer RFC 6762 nicht prüfte, bevor er die Domäne benannte, hat auch nichts anderes geprüft — also geh und sieh dir an, was er sonst gebaut hat. Erwarte zu finden:
- DNS-Server, die zur Welt offen sind, Forwarder, die jedem antworten, der fragt, und nirgends ACLs.
- Dynamisches Update weit offen gelassen, und Zonen-Container mit Berechtigungen, die niemand seit Erstellung der Domäne überprüft hat.
- Zertifikate und Kerberos, auf Namen gebaut, die nie konsistent auflösen würden, mit den Fehlern durch Hosts-Dateien überklebt.
- Hosts-Dateien. Überall. Ganze Umgebungen wurden durch sie zusammengehalten, genau weil
.localnie richtig funktionierte und jemand einen Workaround statt einer Ursache fand. - Firewall-Regeln und Dienstkonten, angelegt, um die Symptome loszuwerden, immer noch vorhanden, immer noch mehr gewährend, als sich jetzt jemand erinnert.
Das ist keine Rachsucht. Es ist, wofür ein Kompetenzsignal da ist. Wenn du eine Entscheidung findest, die getroffen wurde, ohne die Spezifikation zu lesen, ist die richtige Reaktion, anzunehmen, dass der Rest genauso getroffen wurde, und nachzusehen — denn dieselbe Person konfigurierte deine Authentifizierung, deine Zertifikate und deine Zugriffskontrolle, und du hast jetzt direkte Beweise dafür, wie sie an ein Problem herangeht, das sie nicht vollständig versteht.
Dieses Chaos zu erben kostet dich eine Migration. Die Person zu beschäftigen, die es immer noch anrichtet, kostet dich erheblich mehr.
Sambas zwei DNS-Backends
Ein Samba-AD-Domänencontroller speichert seine DNS-Daten im Verzeichnis selbst, und es gibt zwei Wege, sie bereitzustellen.
SAMBA_INTERNAL ist Sambas eigener DNS-Server, in den AD-DC eingebaut. Er behandelt die AD-Zonen und Kerberos-authentifizierte dynamische Updates und reicht alles andere an einen Forwarder. Samba beschreibt ihn als „the basic feature required in an AD“ unterstützend und empfiehlt ihn „for simple DNS setups“, was fair ist und wörtlich zu nehmen. Er bekommt unten einen eigenen Abschnitt, denn was er nicht tut, ist länger und interessanter als was er tut.
BIND9_DLZ betreibt BIND als DNS-Server, mit Sambas dlz_bind9-Modul hineingeladen. DLZ — Dynamically Loadable Zones — ist eine BIND-Schnittstelle, um eine Zone mit etwas anderem als einer Zonendatei zu hinterlegen. Das Modul beantwortet BINDs Anfragen direkt aus sam.ldb, also gibt es keine Kopie, keinen Export-Schritt und keine Synchronisation, die schiefgehen kann: BIND liest das Verzeichnis, während es bereitstellt.
Der interne DNS-Server macht keine Rekursion — er kann es nicht
Fang mit dem Ding an, das fast immer falsch beschrieben wird, auch von Leuten, die es betreiben.
„Der DC ist unser DNS-Server, er macht Rekursion für die Clients“ ist nicht, was passiert, denn der interne DNS-Server kann überhaupt keine Rekursion machen. Sambas eigene Feature-Liste sagt es klar. Das interne DNS unterstützt nicht:
- als Caching-Resolver zu agieren
- rekursive Anfragen (aber es kann an einen anderen rekursiven DNS-Nameserver weiterleiten)
- Shared-Key-Transaction-Signature (TSIG)
- Stub-Zonen
- Zonentransfers
- Round-Robin-Lastverteilung zwischen DCs
wobei Scavenging und Conditional Forwarders ebenfalls als nicht implementiert gelistet sind.
Lies die ersten zwei zusammen, denn das ist die ganze Geschichte. Er kann nicht auflösen, und er kann nicht cachen. Was dns forwarder dir tatsächlich kauft, ist ein Relay: ein Client fragt den DC nach windowsupdate.com, der DC fragt einen echten Resolver, die Antwort kommt durch den DC zurück, und dann vergisst der DC sie vollständig. Der nächste Client stellt dieselbe Frage und das Ganze passiert wieder.
Sieh zurück auf die Tabelle weiter oben in diesem Beitrag und beachte, was das ist: ein Forwarder ohne den Cache des Forwarders. Er hat die Kosten der Rolle — einen zusätzlichen Hop, eine Abhängigkeit, ein Ding, das ausfallen kann — und keinen ihrer Vorteile.
Ein DC mit SAMBA_INTERNAL, der die Umgebung bereitstellt, ist also kein DNS-Server in dem Sinn, den Leute meinen. Er ist ein ungecachter Proxy für einen echten Resolver, und du hast ihn auf die Maschine gestellt, die dein Verzeichnis hält.
Warum das ein Risiko ist und nicht nur eine Ineffizienz
Die Ineffizienz ist leicht zu sehen: jede externe Abfrage im Gebäude wird zu einem Roundtrip durch den AD-DC, dauerhaft, ohne Cache, der die Kante nimmt. In einer ruhigen Domäne bemerkt es niemand. Genau deshalb überlebt es.
Das Sicherheitsargument ist das, das es wert ist, gemacht zu werden, und es hat drei Teile.
Der Listener ist im falschen Prozess. Das interne DNS ist eine Service-Task des AD-DC selbst, laufend mit den Privilegien des Verzeichnisses — nicht ein separater Daemon unter eigenem Konto, wie named es ist. Das Ding, das unbeglaubigtes UDP von allem parst, was Port 53 erreichen kann, läuft also innerhalb des Prozesses, der LDAP und Kerberos bereitstellt und sam.ldb besitzt. BIND hat dreißig Jahre feindlicher Aufmerksamkeit, einen eigenen Benutzer und die Angewohnheit, in einem Jail betrieben zu werden, genau weil ein DNS-Listener eine raue Gegend ist. Sambas interner Server ist ein Komfort-Feature, das zufällig in den Kronjuwelen wohnt.
Clients bereitzustellen heißt, für Clients erreichbar zu sein. Um der DNS-Server der Umgebung zu sein, muss er Anfragen von jeder Workstation annehmen, jedem Drucker, jedem Laptop eines Auftragnehmers auf dem Gast-VLAN, das jemand versehentlich gebrückt hat. Das ist eine große, dauerhaft offene, unbeglaubigte Angriffsfläche auf dem wertvollsten Host, den du hast, und du betreibst sie, um die Kosten eines Resolvers zu sparen, den ein Raspberry Pi hosten könnte.
Und ein zur Welt offener Forwarder ist die Waffe von jemand anderem. Ein DC, der für alles weiterleitet, was fragt, ist ein offener Forwarder. Sobald er außerhalb deines Netzes erreichbar ist, wird er zum Reflexions- und Amplifikations-Teilnehmer, was heißt, dass der Verkehr und die Missbrauchsmeldungen beide bei deinem Domänencontroller ankommen. Es gibt kein Rate-Limiting, nach dem man greifen könnte, denn der interne Server hat keins.
Nichts davon braucht eine Samba-Schwachstelle, um eine schlechte Idee zu sein. Es ist eine schlechte Idee allein von der Form her: es stellt einen unbeglaubigten, versehentlich-internetzugewandten, parser-lastigen Dienst in denselben Prozess wie dein Verzeichnis, um eine Aufgabe zu erledigen, von der dokumentiert ist, dass er sie nicht ordentlich kann.
Er fällt früher um und reißt mehr mit sich
Es lohnt sich, klar zu sein, was dieses Argument nicht ist. Es ist keine Behauptung, dass Sambas DNS-Code mehr Bugs hat als BINDs. BIND hat eine lange CVE-Liste, meist weil es die am meisten untersuchte DNS-Implementierung ist, die existiert, und Advisories zu zählen wäre eine schlechte Art, zwischen ihnen zu wählen.
Der Vergleich, der zählt, ist strukturell, und er kommt auf drei Fragen mit drei unbequemen Antworten hinaus.
Wer kann ihm ein fehlerhaftes Paket schicken? Mit SAMBA_INTERNAL, das die Umgebung bereitstellt: jede Workstation, jedes Telefon im WLAN, alles, was zu Port 53 auf dieser Kiste routen kann. Mit dem Design in diesem Beitrag: der Signierer. Ein Host, ein TSIG-Schlüssel, allow-query auf ihn beschränkt. Das ist kein kleiner Gradunterschied. Es ist der Unterschied zwischen einem exponierten Dienst und einem effektiv unerreichbaren, und er überragt jeden Unterschied in Code-Qualität zwischen den zwei Implementierungen.
Was fällt um, wenn es umfällt? Das ist das, was die Schwere entscheidet. named ist ein separater Daemon unter eigenem Konto; wenn er stirbt, hört DNS auf und der Domänencontroller authentifiziert weiter. Sambas internes DNS ist eine Service-Task innerhalb des AD-DC, also passiert alles, was es verklemmt, erschöpft oder abstürzen lässt, innerhalb des Prozesses, der LDAP und Kerberos bereitstellt. Ein DNS-Problem wird zu einem Verzeichnis-Ausfall. Und systemctl restart named kostet eine Sekunde, während einen DC neu zu starten eine andere Art Morgen ist.
Was kannst du dagegen tun, während es passiert? BIND hat Response Rate Limiting, allow-query, allow-recursion, blackhole, Per-View-Policy und die Option, Clients gar nicht zu antworten. Der interne Server hat dns forwarder und eine Log-Datei. Wenn etwas anfängt, darauf einzuhämmern, gibt es keinen Regler zum Drehen.
Dann füge den fehlenden Cache hinzu. Jede Client-Abfrage ist ein frischer ausgehender Roundtrip, also kostet eine Abfrageflut den DC eine vorgelagerte Abfrage pro Paket statt einen Cache-Treffer — und sie kostet ihn im selben Prozess, der versucht, Kerberos-Tickets auszustellen. Du brauchst dafür keinen Exploit; du brauchst einen geschäftigen Morgen, eine sich schlecht benehmende Anwendung oder jemanden, der einen Scanner auf das falsche VLAN richtet. Anhaltend präsentiert es sich als langsame Authentifizierung, und niemand denkt daran, DNS anzusehen.
Also ja — selbst mit DLZ im Bild ist BIND der sicherere Ort dafür. Das DLZ-Modul gibt named tatsächlich Zugriff auf Sambas Daten, und das ist eine echte Überlegung, aus der dieser Beitrag bereits einen Punkt gemacht hat. Aber ein named-Absturz ist ein DNS-Ausfall statt eines Verzeichnis-Ausfalls, und in diesem Design nimmt dieses named gar keine Anfragen von der Umgebung an. Bugs sind eine Tatsache jeder Codebasis. Blast Radius und Erreichbarkeit sind Dinge, die du wählst.
Und er kann das Design in diesem Beitrag nicht bauen
Es gibt einen einfacheren, endgültigeren Grund, warum er hier nicht das Backend ist.
Keine Zonentransfers. Die Pipeline im nächsten Abschnitt — Hidden Primary, Signierer, eigenständige autoritative Server — beginnt mit einem AXFR aus dem DC. SAMBA_INTERNAL hat nichts zum Transferieren. Kein TSIG, außerdem, also fehlt sogar die Authentifizierung, die dieser Transfer bräuchte. Und kein Signieren, und keine Validierung von irgendetwas, das er weiterleitet.
Also die ehrliche Zusammenfassung von SAMBA_INTERNAL: es ist das Backend, das dich an einem Sonntagnachmittag im Labor eine AD-Domäne hochziehen lässt, ohne BIND zu konfigurieren, und darin ist es sehr gut. Samba sagt „simple DNS setups“ und meint es. Es ist kein Resolver, es wurde nie gebaut, um der DNS-Dienst für eine Umgebung zu sein, und in dem Moment, in dem du Signieren, Transfers, Views, ACLs, Caching oder Rate-Limiting willst, ist die Antwort nicht, es zu tunen. Es hat diese Regler nicht. Die Antwort ist BIND.
Wenn du es heute mit jedem Client auf den DC gezeigt betreibst, ist die Behebung nicht dringend, aber auch nicht optional: gib den Clients einen echten validierenden Resolver, und setze dns forwarder auf dem DC, damit er darauf zeigt, sodass der DC für seine eigenen Zonen antwortet und sonst nichts.
Und das ist der Teil, über den ich klar sein will, denn „benutz kein DLZ“ wird wiederholt, als wäre es eine Härtungsregel: DLZ ist nicht die Exposition. Es ist der Extraktionsmechanismus. Was zählt, ist nicht, welches Modul in BIND geladen ist — es ist, wer mit diesem BIND sprechen darf und was als Nächstes mit der Zone passiert. Ein DLZ-hinterlegtes BIND, das nur eine Transfer-Anfrage von deinem Signierer beantwortet, ist keine Angriffsfläche in irgendeinem interessanten Sinn. Ein SAMBA_INTERNAL-DC, der jede Namensabfrage von vierhundert Laptops abfängt, sehr wohl. Nur einer dieser zwei taucht auf Härtungs-Checklisten auf, und es ist nicht der, der zählt.
Zwei Dinge über DLZ sind wahr und wert, um sie herum zu planen statt sie zu fürchten:
- Das Modul ist versionsgekoppelt an BIND. Samba liefert eine separate
.sopro BIND-Version, undnamed.confnennt eine bestimmte. Ein BIND-Major-Upgrade heißt, das passende Modul muss vorhanden sein, sonst startetnamednicht. Es ist eine Paketierungs-Abhängigkeit, die man im Voraus testet, keine Sicherheitseigenschaft. namedbraucht Zugriff auf Sambas Daten, weshalb Samba ein eigenes Verzeichnis für die Teile hält, die BIND braucht, statt ihm das ganze Private-Verzeichnis zu gewähren. Die Gewährung soll schmal sein — es lohnt sich, zu prüfen, dass sie es auf deinen DCs noch ist, denn es ist die eine Stelle, an der DLZ tatsächlich erweitert, was eine Kompromittierung vonnamederreichen würde.
Der Grund, warum DLZ hier die richtige Wahl ist, ist, was es ermöglicht: BINDs DLZ-Schnittstelle unterstützt das Aufzählen einer ganzen Zone, was einen Zonentransfer aus einer DLZ-hinterlegten Zone überhaupt erst möglich macht. Dieser Transfer ist der erste Hop der Pipeline, und es ist BIND, das ihn macht, was heißt, dass der Rest der Pipeline gewöhnliche BIND-Konfiguration ist statt irgendetwas Exotisches.
Es gibt eine Einschränkung, die alles Nachgelagerte formt, und es lohnt sich, sie klar zu sagen, weil man leicht das Gegenteil annimmt. Eine DLZ-Zone kann nicht selbst signiert werden. ISC ist darüber im BIND ARM ausdrücklich: DLZ „is unable to handle DNSSEC-signed data due to its limited API“. Du kannst keine dnssec-policy an die dlz-Anweisung hängen und fertig sein.
Was du tun kannst — und was ISC für DLZ im selben Atemzug vorschlägt — ist, sie als Hidden Primary zu betreiben, wobei das Signieren von einer normalen BIND-Zone erledigt wird, die die Daten hereintransferiert. Das ist der nächste Abschnitt, und die Einschränkung ist der Grund, warum er die Form hat, die er hat.
Es ist wert, zu wissen, dass das eine Samba-Einschränkung ist statt ein Gesetz von Active Directory. Microsofts DNS-Server macht seit Windows Server 2012 Online-Signing dynamischer, AD-integrierter Zonen. Die Zone wird an Ort und Stelle signiert, die privaten Schlüssel replizieren über die AD-Replikation selbst zu den Key Masters, und dynamische Updates funktionieren weiter. Auf Windows ist „signiere die AD-Partitionen“ eine echte Option, und die Antwort auf den Churn-Einwand ist eingebaut. (Auf Server 2008 R2 war sie es nicht: du konntest eine AD-integrierte Zone signieren, aber keine, die dynamische Updates annimmt, und jede Änderung hieß Neu-Signieren von Hand — woher die Folklore stammt, dass AD-Zonen nicht signierbar seien.)
Samba hat kein Äquivalent. Kein Backend signiert: der interne Server hat gar kein DNSSEC, und DLZ kann keine signierten Daten tragen. Auf Samba ist die Transfer-und-Signier-Pipeline also nicht ein Design unter mehreren. Sie ist der Weg.
Die Veröffentlichungs-Pipeline
Jetzt ihre Form. Vier Rollen, und der DC ist ganz hinten, wo nichts ihn erreichen kann.
named auf dem DC oder ein separater Host ist. Die eigenständigen autoritativen Server sind das Einzige, was Clients je sehen, und sie sind Secondaries der signierten Zone.1. Der Domänencontroller — Hidden Primary. BIND mit dlz_bind9, autoritativ für die AD-Zonen aus dem Verzeichnis. Rekursion aus. Kein client-zugewandter Dienst. allow-transfer auf den Signierer allein beschränkt, mit TSIG. Aus Sicht des Netzes stellt der DC gar kein DNS bereit, und das Einzige, was ihn je abfragt, ist die nächste Kiste.
2. Die Signier-Instanz — eine normale BIND-Zone, die die Daten hereintransferiert. Hier leben inline-signing und die dnssec-policy, auf einer Zone desselben Namens, die die transferierte Kopie hält. BIND behält die unsignierte Kopie, die es empfangen hat, und die signierte Kopie, die es veröffentlicht, als getrennte Dinge und signiert neu, wenn neue Transfers eintreffen. Weil es ein Transfer statt einer geteilten Datei ist, kann diese Instanz auf dem DC selbst sitzen — ein zweites named auf seiner eigenen Adresse — oder auf einem separaten Host, und die Konfiguration ist so oder so nahezu identisch.
Der Handel ist der, wonach er aussieht: auf dem DC ist eine Maschine weniger zu betreiben, während ein separater Host private Schlüssel von der Kiste fernhält, die das Verzeichnis hält. Beides ist vertretbar, und die Wahl ändert sonst nichts an der Pipeline. Was zählt, ist, dass Signieren einmal geschieht, an einem definierten Punkt, unter einer Schlüsselrichtlinie — der Unterschied zwischen DNSSEC, das du betreibst, und DNSSEC, das um drei Uhr morgens abläuft.
3. Die autoritativen Server — das Einzige, was Clients sehen. Einfache Secondaries der signierten Zone. Sie halten keine Schlüssel, signieren nicht und haben keinen Weg zum Verzeichnis. Wird einer kompromittiert, hat der Angreifer eine Kopie einer Zone und keine Fähigkeit, einen neuen Eintrag zu fälschen, der validiert.
4. Die Resolver. Validierende rekursive Resolver für die Umgebung, die die internen Zonen bedingt an diese autoritativen Server weiterleiten und den öffentlichen Baum für alles andere laufen. Das ist, worauf die resolv.conf der Clients zeigt.
Zwei Eigenschaften fallen aus dieser Anordnung, die es wert sind, für sich genannt zu werden, weil sie der ganze Sinn sind:
- Die Maschine, die das Verzeichnis hält, ist für die Maschinen, die es benutzen, nicht erreichbar. Ein Domänencontroller ist der wertvollste Host in der Umgebung. Ihm einen client-zugewandten Netzdienst zu geben — einen, der unbeglaubigtes UDP von jeder Workstation beantwortet — ist ein schlechter Handel für einen Dienst, den andere Maschinen erledigen können.
- Signieren geschieht einmal, an einem definierten Punkt. Der übliche Einwand gegen das Signieren einer AD-Zone ist Churn — dass der Inhalt sich zu oft ändert, als dass Signaturen mithalten könnten. Dieser Einwand ist eigentlich ein Einwand gegen das Standard-Layout, nicht gegen das Signieren. Mit ausgelagerter Client-Registrierung, wie der nächste Abschnitt argumentiert, dass sie es sein muss, ändert sich die AD-Zone, wenn ein Domänencontroller herauf- oder heruntergestuft wird, und sonst ungefähr nie. Eine Zone, die nur DCs schreiben, ist eine stabile Zone, und eine stabile Zone ist ein unauffälliges Ding zum Signieren. Clients herauszuhalten ist nicht nur eine Sicherheitskontrolle; es ist, was die Zone ruhig genug macht, um sauber zu signieren.
Wie das Signieren tatsächlich verdrahtet ist
Die Form oben ist der wichtige Teil, aber „eine normale BIND-Zone, die die Daten hereintransferiert“ verdient es, gezeigt statt beschrieben zu werden, denn der erste Versuch daran scheitert meist am Versuch, das DLZ direkt zu signieren.
Auf dem DC bleibt die DLZ-Seite bewusst stumpf. Sie stellt das Verzeichnis bereit, reicht die Zone an genau einen Peer und tut sonst nichts:
key "transfer-to-signer" {
algorithm hmac-sha256;
secret "...";
};
options {
recursion no;
allow-query { key transfer-to-signer; localhost; };
allow-transfer { key transfer-to-signer; };
notify no;
};
dlz "AD DNS Zone" {
database "dlopen /usr/lib64/samba/bind9/dlz_bind9_18.so";
};
Beachte, dass die .so die BIND-Major-Version in ihrem Namen trägt. Das ist die Versionskopplung aus dem vorigen Abschnitt konkret gemacht — ein BIND-Upgrade braucht das passende Samba-Modul an Ort und Stelle, bevor named startet.
Auf der Signier-Seite ist die Zone eine gewöhnliche Secondary mit den angehängten Signier-Optionen. Das ist der Teil, der nicht auf dem DLZ leben kann:
dnssec-policy "ad-internal" {
keys {
ksk lifetime P365D algorithm ecdsa256;
zsk lifetime P90D algorithm ecdsa256;
};
};
zone "ad.example.com" {
type secondary;
primaries { 192.0.2.10 key transfer-to-signer; };
file "ad.example.com.axfr";
inline-signing yes;
dnssec-policy "ad-internal";
allow-transfer { key transfer-to-public; };
also-notify { 192.0.2.20; 192.0.2.21; };
};
Dass das auf einer Secondary funktioniert, ist das tragende Detail, und das ARM sagt es direkt:
If
yes, BIND 9 maintains a separate signed version of the zone. An unsigned zone is transferred in or loaded from disk and the signed version of the zone is served with, possibly, a different serial number.
BIND behält also zwei Kopien — die unsignierte, die es empfangen hat, in file geschrieben, und die signierte, die es bereitstellt, daneben mit einer .signed-Endung geschrieben. Ein Transfer trifft ein, die signierte Version wird neu erzeugt, und die nachgelagerten Server bekommen ein notify, denn an diesem Punkt ist es eine völlig gewöhnliche Zone. inline-signing yes ist tatsächlich die Voreinstellung, sobald eine dnssec-policy angehängt ist; es ist oben ausgeschrieben, weil eine Konfiguration, die sagt, was sie tut, die Zeile wert ist.
Schlüsselrollover kommt mit der Richtlinie statt mit einem Cron-Job. ZSK-Rollover brauchen gar keine Eingabe; KSK-Rollover brauchen, dass das neue DS zum Elternteil kommt, was die CDS/CDNSKEY-Automatisierung von später in diesem Beitrag ist. rndc dnssec -status ad.example.com sagt dir, wo jeder Schlüssel in seiner Lebensdauer ist.
Ob dieser Block auf dem DC oder auf einem eigenen Host läuft, ist eine Frage, auf welche Adresse primaries zeigt. Auf dem DC ist es eine zweite named-Instanz auf einer zweiten Adresse, die von der ersten über das Loopback oder ein Management-Interface transferiert. Das ist, was „BIND mit DLZ kann der Signierer sein“ in der Praxis heißt: dieselbe Software, dieselbe Kiste wenn du willst, aber das Signieren geschieht auf der transferierten Kopie statt auf der DLZ-Zone.
Der eine betriebliche Haken ist Notify — und er ist auf der DLZ-Seite. ISCs Handbuch ist darüber unverblümt: DLZ „has no built-in support for DNS notify“, also werden Secondary-Server nicht automatisch über Änderungen an den Zonen in der Datenbank informiert. Samba kann einen Eintrag im Verzeichnis ändern, und die DLZ-Instanz hat keine Ahnung, dass sie es jemandem sagen sollte.
Der erste Hop ist also ein Poll, kein Push. Der Signierer aktualisiert auf dem SOA-Timer der Zone, die er transferiert, was heißt:
- Die Propagierung von einer DC-Änderung zu einem signierten, veröffentlichten Eintrag ist durch dieses Refresh-Intervall begrenzt, nicht durch Sekunden. Stufe einen DC herauf, und seine neuen
_msdcs-Einträge erscheinen nachgelagert bis zu einem Refresh später. - Dieses Intervall ist der Regler, den man dreht, wenn die Verzögerung zählt. Es ist ein Handel gegen die Frage, wie oft du das DLZ abgefragt haben willst, was das andere Ding aufbringt, das ISC über DLZ sagt: es macht Echtzeit-Datenbank-Lookups ohne Caching und ist „not recommended for use on high-volume servers“.
- Beide davon sind Argumente für diese Topologie statt gegen sie. Der einzige Client, den die DLZ-Instanz je hat, ist der Signierer, der einmal pro Refresh fragt. Die tatsächliche Abfragelast der Umgebung landet auf den eigenständigen autoritativen Servern, die eine einfache signierte Zonendatei mit voller Geschwindigkeit bereitstellen.
Alles nachgelagert vom Signierer ist konventionell: die autoritativen Server sind Secondaries der signierten Zone, sie bekommen ein ordentliches notify, und sie berühren nie das Verzeichnis oder einen Schlüssel.
Clients dürfen die Zone mit den Locators nicht schreiben
Das ist die wichtige, und es ist eine Design-Regel statt einer Einstellung.
Active Directory registriert Einträge dynamisch. Eine Maschine tritt bei, und sie registriert sich selbst; ein DC startet, und er registriert die SRV-Einträge, die seine Dienste bewerben. „Sicheres“ dynamisches Update heißt, dass diese Updates beglaubigt sind — die Maschine beweist mit ihren eigenen Anmeldedaten, dass sie ist, wer sie zu sein behauptet, und es gibt eine Per-Eintrag-ACL, sodass eine Maschine im Allgemeinen nur einen Eintrag ändern kann, den sie erstellt hat.
Lies das sorgfältig, denn die Garantie ist enger, als es zuerst scheint. Sicheres dynamisches Update beglaubigt, wer schreibt. Es bewertet nicht, was der Eintrag bedeutet. Und der Satz der Konten, die schreiben dürfen, ist weit breiter, als Leute annehmen: in einer Standard-AD-integrierten Zone hält die Gruppe Authenticated Users Create All Child Objects auf dem Zonen-Container im Verzeichnis, weil ADIDNS jeden Eintrag als AD-Objekt unter CN=MicrosoftDNS,DC=DomainDnsZones speichert. Nicht nur Maschinenkonten — jedes beglaubigte Konto in der Domäne, einschließlich desjenigen, das dem gehört, der heute Morgen den Rechnungsanhang geöffnet hat.
Was zwei Fehlermodi in einer Zone erzeugt, die sowohl Client-Einträge als auch Dienst-Locators hält:
Namen, die noch nicht existieren, gehören niemandem. Per-Eintrag-ACLs schützen einen Eintrag, der bereits einen Besitzer hat. Ein Name, der nie registriert wurde, hat kein Objekt, auf dem eine ACL durchzusetzen ist, also bekommt ihn das erste Konto, das ihn erstellt. Das ist der Mechanismus hinter der ganzen Familie von ADIDNS-Angriffen, wobei der schärfste ein Wildcard ist: erstelle *, und jeder Name in der Zone, den niemand ausdrücklich beansprucht hat — Tippfehler, ausgemusterte Hosts, wpad — löst auf den Angreifer auf. Bestehende Einträge bleiben unberührt, was genau ist, warum es unbemerkt bleibt.
Der Blast Radius schließt die Locators ein. Die Einträge unter _msdcs sind, wie jeder Client in der Domäne einen Domänencontroller und einen KDC findet. Sie sind die sicherheitskritischsten Einträge, die du besitzt, und in einem Standard-Deployment sitzen sie in derselben Zone, in die vierhundert Laptops jedes Mal schreiben, wenn sie ein DHCP-Lease bekommen.
Also die Regel: die Zone, die die Locators hält, wird von Domänencontrollern geschrieben, und von nichts sonst. Dynamische Client-Registrierung geht woandershin.
Woandershin kann eins von zwei Dingen sein, und beide sind in Ordnung:
- Eine delegierte Sub-Zone, anderswo bereitgestellt. Die AD-Zone hält eine
NS-Delegierung für, sagen wir,dyn.ad.example.com, und die Einträge landen auf einem separaten Server, der die Updates annimmt. Sambas Partitionen nehmen gar keinen Client-Schreibzugriff an. - Eine separate Zone in Samba mit eigener Update-ACL. Immer noch im Verzeichnis, aber ihre eigene Zone, sodass ein Client-Schreibzugriff keinen Weg zu
_msdcsoder zu den eigenen Einträgen eines DC hat.
Das erste gibt die härtere Trennung; das zweite ist weniger zu betreiben. Was die Regel bricht, ist keins von beiden. Es ist die Voreinstellung, wo die zwei zusammenleben. Was ist, wie die meisten Domänen immer noch laufen, weil niemand es gewählt hat und niemand zurückgegangen ist, um nachzusehen.
Und es gibt eine zweite Dividende, die, die das zurück an die Pipeline bindet. Eine Zone, die nur Domänencontroller schreiben, ist eine Zone, die sich kaum je ändert: eine DC-Heraufstufung, eine DC-Herunterstufung, und ansonsten Stille. Aller Churn in einer Standard-AD-Zone ist Client-Registrierung. Nimm die heraus, und der Einwand gegen das Signieren der AD-Partitionen geht mit ihr — es gibt keinen Strom von Updates, den die Signaturen jagen müssten, also ist es zu signieren Routine statt ein Kampf. Die Schreibdisziplin und das Signieren sind dieselbe Entscheidung, zweimal gesehen.
Neben einem von beiden gibt es eine Berechtigung, die man sich ansehen sollte — und auf einem Microsoft-DC ist sie die direkte Behebung. Weil ADIDNS-Einträge Verzeichnisobjekte sind, kommt die Berechtigung aus einer AD-ACL statt aus irgendetwas im DNS-Protokoll: Authenticated Users, die Create All Child Objects auf dem Zonen-Container halten. Das zu straffen ist die sauberste Minderung für das Problem unbeanspruchter Namen und Wildcards, und in vielen Umgebungen kann die Berechtigung ganz entfernt werden, sobald du weißt, was sich tatsächlich selbst registrieren muss. Sambas AD-DNS speichert seine Einträge auf dieselbe Weise im Verzeichnis, also gilt dieselbe Frage — geh und sieh dir an, was dein Zonen-Container tatsächlich gewährt.
Aber Moment — sollten Clients 2026 überhaupt registrieren?
Alles oben nimmt an, dynamisches Client-Update sei etwas, das du brauchst und sicher zu machen versuchst. Bevor du das akzeptierst, lohnt es sich, die Frage zu stellen, die niemand stellt, denn die Antwort hat sich geändert, seit dieses Verhalten entworfen wurde.
Dynamische DNS-Registrierung wurde für einen Desktop gebaut. Eine Kiste unter einem Schreibtisch, ein Netzwerkkabel, eine Adresse, die sie jahrelang behielt. In dieser Welt war eine Maschine, die ihren eigenen Namen registrierte, ordentlich und im Grunde wahr.
Jetzt sieh, was ein Client 2026 ist. Er wacht im Heim-WLAN auf. Er kommt ins Büro und tritt dem Firmen-WLAN bei. Er geht in eine Dockingstation und greift zusätzlich eine kabelgebundene Adresse ab. Jemand startet das VPN, und ein Tunnel-Adapter erscheint mit einer dritten Adresse. Sie gehen in ein Café, tethern an ein Telefon, und das VPN kommt auf einer vierten zurück. Das ist eine Maschine, ein Name und ein halbes Dutzend Adressen an einem Arbeitstag — und standardmäßig wird er eine gute Zahl davon zu registrieren versuchen.
Die Zone füllt sich also mit Behauptungen, die einmal wahr waren.
- Windows registriert jeden Adapter, den es hat, es sei denn, jemand ist herumgegangen und hat Adressen dieser Verbindung in DNS registrieren pro Interface abgehakt. Ein gedockter Laptop im VPN ist eine Maschine mit drei aktiven Adaptern und einer Meinung über alle.
- Mehrere A-Einträge für einen Namen sind kein Fehlerzustand, es ist das normale Ergebnis. Eine Abfrage gibt sie alle zurück, Clients versuchen sie in beliebiger Reihenfolge, und Verbindungen zu diesem Namen scheitern im Verhältnis dazu, wie viele der Adressen tot sind. Das ist der Mechanismus hinter „Fernwartung kann die Maschine in der einen Minute sehen und in der nächsten nicht“.
- VPN-Adressen sind die schlimmsten davon, denn eine Tunneladresse ist eine Stunde lang gültig, und der Eintrag überlebt sie. Der Tunnel bricht ab, die Pool-Adresse geht an jemand anderen, und der Name zeigt jetzt auf einen Kollegen.
- Dockingstations trüben die Identität selbst. Sofern MAC-Address-Passthrough nicht konfiguriert ist, gehört das Lease der Dockingstation statt dem Laptop, also driften in einer Hot-Desk-Umgebung Namen, Leases und Maschinen täglich auseinander.
- Und Besitz macht es dauerhaft. Ein Eintrag kann nur von dem Konto aktualisiert werden, das ihn erstellt hat. Wenn ein Eintrag von DHCP unter einem Anmeldedatum erstellt wurde und die Maschine später versucht, ihn unter ihrem eigenen zu aktualisieren, scheitert das Update, leise, und die veraltete Adresse bleibt genau, wo sie ist.
Die Aufräum-Geschichte ist auch nicht die Rettung, nach der sie klingt. Samba hat Scavenging seit 4.9, aber es ist standardmäßig aus (dns zone scavenging = yes, mit samba-tool dns zoneoptions --aging=1), und Samba selbst sagt, es „should only be enabled on new zones or new installations“, weil ältere Versionen dynamische Einträge als statisch und statische als dynamisch markierten. Auf den Umgebungen, die am ehesten voller Müll sind — denen, die seit Jahren laufen — ist das Werkzeug zum Aufräumen genau das, das anzuschalten man dir abrät. Es hatte auch eine eigene CVE.
Also stell die Frage direkt: was verbraucht tatsächlich den A-Eintrag eines Laptops?
In den meisten Läden sehr wenig. Benutzer verbinden zu Servern; Server verbinden nicht zu Laptops. Die echten Verbraucher sind Fernwartungs-Tools, RDP zu einer benannten Workstation und Inventar oder Monitoring — und fast all dieses Tooling pflegt sein eigenes Inventar und arbeitet von einem eincheckenden Agenten, weil es sich für mobile Clients ohnehin nie auf DNS verlassen konnte.
Was nahelegt, die Voreinstellung umzukehren:
- Server und Infrastruktur bekommen Einträge aus dem Provisioning. Mit NetBox und Ansible schon im Bild wird der Eintrag von demselben Ding erstellt, das die Maschine erstellt hat, er ist per Konstruktion korrekt, und er wird entfernt, wenn die Maschine es wird.
- Der stabile kabelgebundene Bestand kann Einträge von DHCP nehmen, wenn etwas sie tatsächlich braucht, mit einem Anmeldedatum, das sie besitzt, sodass Updates nicht scheitern.
- Mobile Clients registrieren gar nichts. Sie sind Verbraucher von DNS, keine Veröffentlicher davon. Wenn etwas einen Laptop erreichen muss, braucht es einen Agenten, keinen A-Eintrag.
Du landest am selben Ort, an den das Sicherheitsargument dich gestellt hat, aus einer völlig anderen Richtung. Weniger Schreiber heißt eine kleinere ADIDNS-Fläche, eine Zone, die nicht voller abgelaufener Behauptungen ist, und — zurück zur Pipeline — eine Zone, ruhig genug, um sie ohne Nachdenken zu signieren.
Der Sicherheitsfall sagt, Clients dürfen die Zone mit den Locators nicht schreiben. Der betriebliche Fall fragt, warum sie überhaupt DNS schreiben. 2026, für eine Flotte, die ihre Adresse fünfmal am Tag ändert, ist „tun sie nicht“ eine völlig gute Antwort, und erheblich weniger Arbeit, als ihr Chaos sicher zu machen.
Getrennte Sichten, und woher das Vertrauen kommt
Das letzte Stück bindet die zwei Hälften des Beitrags zusammen.
Es gibt zwei Sichten auf den Namensraum. Eine öffentliche Zone, ins Internet veröffentlicht, die die Handvoll Namen hält, die die Welt braucht. Und eine interne Sicht — der aus AD abgeleitete Inhalt, jeder beigetretene Host, jeder Dienst-Locator, die Site-Topologie — die die Welt nichts angeht. Dieser Inhalt ist eine Karte der Umgebung, und er sollte von außen unerreichbar und nicht transferierbar sein.
Aber das Vertrauen für die interne Sicht kommt von der öffentlichen Seite, und das ist, was dieses Design besser macht als die übliche interne-DNS-Insel:
example.comist öffentlich und signiert, mit seinemDSim Elternteil und einer Kette zur Root.ad.example.comist davon delegiert. Der öffentliche Elternteil veröffentlicht die Delegierung und einDSfür den Schlüssel der internen Zone.- Die internen autoritativen Server stellen das signierte
ad.example.combereit. Die internen Resolver validieren es — Root →com→example.com→ad.example.com— mit nichts als dem Root Trust Anchor, den sie schon hatten.
Kein lokaler Trust Anchor. Keine Insel. Kein von Hand verteilter Schlüssel. Die Daten verlassen nie das Gebäude, und der Validierungspfad ist der gewöhnliche öffentliche. Wenn du einen Resolver hinzufügst, validiert er interne Namen korrekt, ganz ohne DNSSEC-Konfiguration.
Ehrlich über den Handel, denn es gibt einen. Eine Delegierung und ein DS in der öffentlichen Zone zu veröffentlichen heißt, dass die Existenz von ad.example.com und die Namen seiner Nameserver öffentlich sind. Der Inhalt ist es nicht, und ist es nie — aber du hast der Welt gesagt, dass die Zone existiert. Im Gegenzug validiert jeder Resolver, den du besitzt, interne Namen gegen die echte Root. Das ist ein guter Handel für die meisten Umgebungen, und er sollte ein bewusster sein statt eine Überraschung.
Zwei Dinge, die man daneben richtig machen muss:
- Halte die interne Sicht unaufzählbar und nicht transferierbar.
allow-transferauf den internen autoritativen Servern ist für den Signierer und deine eigenen Secondaries, nichts sonst. Und bedenke, dassNSEC-beglaubigte Verweigerung jeden, der die Zone abfragen kann, sie von Ende zu Ende durchlaufen lässt;NSEC3erhöht diese Kosten, aber die eigentliche Kontrolle ist, dass Außenstehende die Server gar nicht erreichen können. - Automatisiere das
DS. EinDSim Elternteil, das aufhört, zum Schlüssel des Kindes zu passen, nimmt die ganze interne Domäne zuSERVFAIL.CDS/CDNSKEYexistieren, damit das Kind eine Schlüsseländerung signalisieren kann und der Elternteil sie aufnehmen kann, ohne dass ein Mensch während eines Rollovers einen Eintrag editiert. Wenn die Elternzone bei einem Registrar oder Provider liegt, der es unterstützt, benutze es; wenn nicht, muss die Rollover-Prozedur vor dem ersten Roll aufgeschrieben werden, nicht währenddessen.
Windows und Linux dazu bringen, wirklich zu validieren
Alles bis hierher ging darum, eine Zone zu veröffentlichen, die verifiziert werden kann. Nichts davon tut irgendetwas, bis etwas auf der Client-Seite darauf besteht, sie zu verifizieren. Eine perfekt signierte Zone und ein Client, der nie eine Signatur prüft, erzeugen genau dieselbe Erfahrung wie eine unsignierte Zone, bis zu dem Tag, an dem sie es nicht tun.
Es gibt nur zwei Orte, an denen Validierung geschehen kann, und der Unterschied zwischen ihnen ist der Unterschied zwischen einer Sicherheitskontrolle und einem höflichen Vorschlag.
Am Resolver validieren und dem AD-Bit vertrauen. Der Client fragt einen Resolver, der Resolver macht die Kryptografie, und er meldet das Ergebnis, indem er ein Bit setzt — AD, Authenticated Data — in der Antwort. Der Client glaubt dem Bit. Das ist das Modell, das Windows benutzt, und es ist nur so stark wie der Pfad zwischen dem Client und dem Resolver, denn alles, was als der Resolver antworten kann, kann dieses Bit setzen.
Auf dem Client selbst validieren. Die Maschine betreibt ihren eigenen validierenden Resolver, sodass der „Pfad zum Resolver“ ein Loopback-Socket innerhalb der Maschine ist und nichts mehr zu spoofen bleibt. Das ist stärker, und auf Linux ist es völlig erreichbar.
Die Voreinstellung ist nichts
Bevor du irgendetwas konfigurierst, lohnt es sich, zu sehen, was eine gängige Linux-Workstation ab Werk tut. Das ist eine Fedora-44-Maschine, systemd 259, unangetastet:
$ resolvectl status | head -3
Global
Protocols: LLMNR=resolve -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
$ grep options /etc/resolv.conf
options edns0 trust-ad
$ dig +dnssec cloudflare.com A | grep flags
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
Lies die drei zusammen, denn sie erzählen eine kleine Geschichte.
Der Stub ist mit trust-ad konfiguriert — ihm wurde gesagt, dem AD-Bit zu glauben. systemd-resolved meldet DNSSEC=no/unsupported, validiert also selbst nichts. Und die Antwort für eine signierte Zone kommt mit den Flags qr rd ra und ohne ad zurück — nichts irgendwo in diesem Pfad behauptete, validiert zu haben.
Das ist keine Fehlkonfiguration. Das ist die Voreinstellung. Einem Client kann gesagt werden, einer Behauptung zu vertrauen, die nichts in der Kette aufstellt. Nichts prüft es. Wert, eine Minute damit zu sitzen, bevor du sonst irgendetwas konfigurierst.
Linux
Drei Optionen, in aufsteigender Reihenfolge, wie wenig du dem Netz vertrauen musst.
1. systemd-resolved, lokal validierend. Ein Drop-in statt die ausgelieferte Datei zu editieren:
# /etc/systemd/resolved.conf.d/dnssec.conf
[Resolve]
DNSSEC=yes
DNSOverTLS=opportunistic
Dann systemctl restart systemd-resolved und mit resolvectl status bestätigen, dass die Zeile jetzt DNSSEC=yes liest.
Die Einstellung, mit der man vorsichtig sein muss, ist die mittlere. DNSSEC=allow-downgrade sieht wie ein vernünftiger Kompromiss aus und ist keine Sicherheitskontrolle — resolved.conf(5) sagt es selbst:
Note that this mode makes DNSSEC validation vulnerable to “downgrade” attacks, where an attacker might be able to trigger a downgrade to non-DNSSEC mode by synthesizing a DNS response that suggests DNSSEC was not supported.
Ein Angreifer, der Antworten fälschen kann, ist genau der Angreifer, gegen den DNSSEC existiert, also kauft ein Modus, den sie durch das Fälschen einer Antwort abschalten können, dir nichts gegen sie. Es ist yes, oder es ist Dekoration.
2. Ein echter validierender Resolver auf dem Host. Der Validator von systemd-resolved ist bequem statt gründlich. Wo es zählt, betreibe Unbound oder BIND auf dem Loopback und zeige den Stub darauf:
# unbound: validate against the root anchor, refuse to be stripped
server:
module-config: "validator iterator"
auto-trust-anchor-file: "/var/lib/unbound/root.key"
harden-dnssec-stripped: yes
val-permissive-mode: no
# the internal zone is reached like any other name — no local anchor needed
forward-zone:
name: "ad.example.com."
forward-addr: 192.0.2.53
BINDs Äquivalent ist eine Zeile — dnssec-validation auto; — die seine eingebaute Kopie des Root-Anchors benutzt und den Rollover für dich verwaltet.
3. Umgebungsweit, an den Resolvern, die du schon betreibst. Das sind Stufe vier der Pipeline weiter oben in diesem Beitrag. Validierung geschieht dort, Clients vertrauen dem AD-Bit, und der Hop zwischen ihnen ist das Ding, das du schützen musst — mit DoT, oder mit einem Netz, über das du bereit bist, diese Annahme zu treffen.
Und beachte, was in keiner dieser Konfigurationen ist: ein Trust Anchor für die interne Zone. Weil ad.example.com eine Delegierung innerhalb einer öffentlich signierten Zone ist, validiert jede davon interne Namen über die gewöhnliche Kette von der Root. Das ist das Design aus dem vorigen Abschnitt, das sich bezahlt macht. Die Alternative ist, einen lokalen Anchor auf jeden Client und Resolver in der Umgebung zu schieben, und ihn bei jedem Rollover neu zu schieben.
Windows
Das Wichtige zuerst, denn es wird routinemäßig missverstanden: der Windows-DNS-Client validiert kein DNSSEC. Er macht keine Kryptografie, prüft keine Signatur und hält keinen Trust Anchor. Er ist ein Stub-Resolver, und das war er immer.
Was du tun kannst, ist ihn zwingen, Antworten abzulehnen, die nicht in seinem Namen vom Server validiert wurden. Das ist die Name Resolution Policy Table, und sie ist pro Namensraum statt global:
# Require validated answers for the internal zone
Add-DnsClientNrptRule -Namespace ".ad.example.com" `
-DnsSecEnable -DnsSecValidationRequired
# What is actually in force on this machine, including from Group Policy
Get-DnsClientNrptPolicy -Effective
Get-DnsClientNrptRule
Für die Umgebung lebt dasselbe in Group Policy unter Computer Configuration → Policies → Windows Settings → Name Resolution Policy: erstelle eine Regel für den Namensraum, hake die DNSSEC-Option an, und hake die Anforderung an, dass der Client prüft, dass die Daten vom DNS-Server validiert wurden.
Zwei Dinge folgen daraus, und beide zählen.
Etwas Vorgelagertes muss immer noch das Validieren tun. Die NRPT-Regel bringt den Client dazu, das AD-Bit zu fordern; sie erzeugt keins. Der Resolver, auf den diese Clients zeigen, muss ein validierender Resolver sein, oder jeder Name in diesem Namensraum scheitert.
Und deshalb hat die NRPT-Regel IPsec-Optionen daneben. Microsoft hat sie aus dem Grund dorthin gesetzt, der oben in diesem Abschnitt dargelegt wurde: ein Bit zu fordern, das jeder On-Path-Angreifer setzen kann, ist nicht viel von einer Forderung. Wenn du dich in einem nicht vertrauenswürdigen Netz auf das Resolver-validiert-Modell verlässt, muss der letzte Hop geschützt werden — IPsec zwischen Client und Resolver, oder DoT, wo der Resolver es unterstützt.
Es fällt geschlossen aus, also roll es in dieser Reihenfolge aus
Validierung zu erzwingen wandelt eine Klasse stiller Kompromittierung in eine Klasse lauter Ausfälle. Das ist der richtige Handel, und es ist trotzdem ein Ausfall: ein abgelaufenes RRSIG, ein DS im Elternteil, das nach einem Rollover nicht mehr passt, oder ein Resolver, der die Elternzone nicht erreichen kann, erzeugen alle SERVFAIL, und SERVFAIL für _ldap._tcp.dc._msdcs heißt, die Domäne ist unten statt verschlechtert.
Tu es also in dieser Reihenfolge:
- Schalte Validierung zuerst an den Resolvern ein, und lass die Clients in Ruhe. Achte für ein paar Wochen auf
SERVFAILin den Resolver-Logs — hier findest du die Zone, die seit einem Jahr leise kaputt ist. - Automatisiere das
DS, bevor du irgendetwas erzwingst, wie im vorigen Abschnitt. Die meisten selbstverschuldeten DNSSEC-Ausfälle sind ein Rollover, bei dem der Elternteil nie aktualisiert wurde. - Dann erzwinge die Clients, ein Namensraum nach dem anderen, beginnend mit deiner eigenen Workstation und einer Test-OU statt der ganzen Umgebung.
Der Fehler, gegen den du planst, ist ein Client, dem ein gefälschter Domänencontroller gereicht wird. Der Fehler, den du riskierst, ist ein Client, dem gar nichts gereicht wird. Der zweite ist behebbar und offensichtlich; der erste ist keins von beiden. Du hörst vom Ausfall innerhalb einer Minute. Vom anderen hättest du überhaupt nie gehört.
Den letzten Hop verschlüsseln: DoT und DoH auf den internen Resolvern
Der Validierungsabschnitt ließ eine Sache offen. Im Resolver-validiert-Modell — dem, das Windows dir gibt — vertraut der Client einem einzigen Bit, das der Resolver setzt, und dieses Bit ist nur den Pfad wert, über den es reiste. Etwas muss diesen Pfad schützen.
BIND unterstützt beide verschlüsselten Transporte nativ, das ist also ein Konfigurations-Job statt eines Beschaffungs-Jobs:
- DNS over TLS — ein
tls-Block, referenziert auslisten-on, üblicherweise auf Port 853. - DNS over HTTPS — derselbe
tls-Block plus einhttp-Block, auf 443. - Ausgehendes DoT, denn
forwardersnimmt einen TLS-Transport pro Adresse oder für die ganze Liste. - Zonentransfers über TLS, denn die
primaries-Anweisung einertype secondary-Zone nimmt auch einen — was direkt für die Pipeline weiter oben in diesem Beitrag nützlich ist.
Zuerst, sei klar, was das kauft, denn DoT und DNSSEC werden ständig vermischt, und sie sind keine Alternativen. DNSSEC beglaubigt die Daten, den ganzen Weg zurück zur Zone, die sie veröffentlicht hat. DoT schützt das Gespräch mit dem Resolver. Das eine überlebt einen feindlichen Resolver und ein feindliches Netz zwischen Resolvern; das andere hindert die Maschine in deinem WLAN daran, zu lesen und umzuschreiben, was dein Laptop fragte. Du willst beide, und keins ersetzt das andere. Den Hop zu einem Resolver zu verschlüsseln, der nicht validiert, ist ein privates Gespräch mit etwas, das immer noch angelogen werden kann.
Es bereitstellen
tls internal-resolver {
key-file "/etc/pki/dns/resolver.key";
cert-file "/etc/pki/dns/resolver.pem";
protocols { TLSv1.3; };
};
http internal-doh {
endpoints { "/dns-query"; };
};
options {
dnssec-validation auto;
listen-on port 53 { 192.0.2.53; };
listen-on port 853 tls internal-resolver { 192.0.2.53; };
listen-on port 443 tls internal-resolver
http internal-doh { 192.0.2.53; };
listen-on-v6 port 853 tls internal-resolver { 2001:db8::53; };
};
Das Zertifikat ist die eigentliche Arbeit, und es ist der Teil, der übersprungen wird. Ein Client, der verifiziert — was der ganze Sinn ist — braucht ein Zertifikat, das für den Namen gültig ist, mit dem er konfiguriert wurde, ausgestellt von etwas, dem er bereits vertraut. Das heißt deine interne CA und deine bestehende Zertifikatsautomatisierung, nicht das ephemeral-Schlüsselwort. ephemeral erzeugt ein Wegwerf-Self-Signed-Zertifikat; es ist da, damit du beweisen kannst, dass der Listener funktioniert, und es ist für jeden Client, der tatsächlich prüft, wertlos.
Darüber weiterleiten
Wenn diese Resolver irgendwohin weiterleiten, statt den Baum selbst zu laufen, kann der vorgelagerte Hop auch verschlüsselt werden — und hier ist eine Unterscheidung wert, sie richtig zu machen:
tls upstream {
ca-file "/etc/pki/tls/certs/ca-bundle.crt";
remote-hostname "dns.example.net";
};
options {
forwarders port 853 tls upstream { 192.0.2.1; };
};
Ohne remote-hostname bekommst du Verschlüsselung ohne Authentifizierung: der Verkehr ist für einen passiven Beobachter unlesbar, und ein aktiver Angreifer, der die Verbindung abfangen kann, präsentiert einfach sein eigenes Zertifikat. Mit remote-hostname und ca-file verifiziert BIND, mit wem es spricht. Das erste ist etwas wert. Nur das zweite ist es wert, eine Kontrolle genannt zu werden.
Die Client-Hälfte ist nicht symmetrisch
Hier wird eine gemischte Umgebung heikel, und es ist der Grund, beide Transporte zu konfigurieren statt einen zu wählen.
Linux macht DoT ordentlich. systemd-resolved nimmt DNSOverTLS=yes für den strikten Modus, und dem Server kann der Name gegeben werden, gegen den zu verifizieren ist:
[Resolve]
DNS=192.0.2.53#resolver.ad.example.com
DNSOverTLS=yes
DNSSEC=yes
Wie bei DNSSEC= ist die mittlere Einstellung die Falle: DNSOverTLS=opportunistic fällt auf Klartext zurück, wenn TLS nicht verfügbar ist, was ein Angreifer, der in die Verbindung eingreifen kann, arrangieren kann.
Windows macht DoH, und nicht DoT. Client-DoH-Unterstützung kam in Windows 11 und Server 2022, pro Server mit einem Template konfiguriert:
$doh = "https://resolver.ad.example.com/dns-query"
netsh dnsclient add encryption server=192.0.2.53 dohtemplate=$doh
DoT ist, zum Zeitpunkt des Schreibens, nur in Insider-Builds aufgetaucht. Auf freigegebenem Windows ist die verschlüsselte Option also DoH oder nichts, was genau ist, warum der NRPT-Abschnitt weiter oben stattdessen zu IPsec griff.
Daher beide aus derselben BIND-Instanz bereitstellen. DoT für die Linux-Flotte und alles andere, das es spricht, DoH für Windows, ein Resolver, ein Zertifikat.
Welches, wo
Für einen internen Resolver ist DoT der bessere Transport und DoH die Kompatibilitätsantwort.
DoT sitzt auf einem eigenen Port. Du kannst es sehen, erlauben, verweigern und auf alles alarmieren, das DNS macht und es nicht so macht. DoHs Vorteil — von gewöhnlichem Web-Verkehr auf 443 nicht zu unterscheiden — ist ein echter Nutzen in einem feindlichen Netz und ein Ärgernis in deinem eigenen, wo unterscheiden zu können, was DNS ist, ein Feature ist, für das du bezahlt hast. In der Umgebung, die du kontrollierst, bevorzuge den Transport, den du beobachten kannst, und betreibe DoH, weil Windows dir keine Wahl lässt, statt weil es besser ist.
Wenn du schon dabei bist: verschlüssele die Transfers
Die Pipeline weiter oben in diesem Beitrag bewegt die AD-Zone per AXFR, und der Getrennte-Sichten-Abschnitt machte den Punkt, dass ihr Inhalt eine Karte der Umgebung ist. TSIG beglaubigt diese Transfers; es verbirgt sie nicht. Da die primaries-Anweisung einer Secondary eine TLS-Konfiguration annimmt, kann der Transfer auch über TLS laufen — RFC 9103, wenn du den Standard willst:
zone "ad.example.com" {
type secondary;
primaries { 192.0.2.10 port 853 tls xfr-tls key transfer-to-signer; };
...
};
Vom Schlüssel beglaubigt, vom Transport verschlüsselt. Wenn irgendein Hop in dieser Pipeline eine Standortverbindung kreuzt, einen Hypervisor, den du teilst, oder irgendetwas, worauf du nicht gern einen Hub setzen würdest, ist es die zwanzig Minuten wert.
Was es nicht behebt
- Es ist keine Validierung. Oben behandelt, und wert, wiederholt zu werden, weil Hersteller „sicheres DNS“ verkaufen und Verschlüsselung allein meinen.
- Es verbirgt nichts vor dem Resolver. Der Resolver sieht jede Abfrage vollständig. Verschlüsselung schützt den Pfad, nicht die Privatsphäre der Abfrage vor dem Betreiber — was in Ordnung ist, wenn der Betreiber du bist.
- Es tut nichts für einen Client, der das Zertifikat nicht verifiziert, und opportunistische Modi sind von genau dem Angreifer downgradebar, um den du dir Sorgen machst.
- Und es ist kein Grund, einen Listener auf den Domänencontroller zu stellen. Verschlüsseltes DNS auf dem DC würde das falsche Problem wunderschön lösen. Der DC antwortet immer noch niemandem.
Wie du prüfst, was du hast
Befehle zum Laufen gegen deine eigene Umgebung. Die Ausgaben sind der interessante Teil, und ein paar davon sind beim ersten Mal unbequeme Lektüre.
# Walk the tree yourself, one delegation at a time
dig +trace dc01.ad.example.com
# Validate, and show the chain being built
delv +rtrace +vtrace ad.example.com SOA
# Is the resolver you are pointed at actually validating?
# A deliberately broken test name must come back SERVFAIL, not an address
dig @<resolver> dnssec-failed.org A
# What does the estate advertise as a domain controller?
dig SRV _ldap._tcp.dc._msdcs.<domain>
dig SRV _kerberos._udp.<domain>
# Is a DC answering for names it has no business answering?
# Ask it for something it is not authoritative for. "recursion requested
# but not available" is the answer you want. An actual address means it is
# serving the estate — recursing if it is BIND, relaying to the forwarder
# if it is SAMBA_INTERNAL. Either way it should not be doing that.
dig @<dc> www.example.org A
# Is the DC configured as the estate's DNS relay?
grep -E 'dns forwarder|server services' /etc/samba/smb.conf
# Will a DC hand its zone to anybody who asks?
dig @<dc> AXFR ad.example.com
# Which backend is this DC running, and does named have the module?
grep -r dlz /etc/named.conf /var/lib/samba/bind-dns/ 2>/dev/null
samba-tool dns query <dc> <domain> @ ALL
# Is the internal zone chained to the public parent?
dig DS ad.example.com @<public-authoritative-for-example.com>
# What does the local stub actually do with the AD bit, and is the
# hop to the resolver encrypted?
resolvectl status # DNSSEC= and DNSOverTLS= per link
grep options /etc/resolv.conf # trust-ad, trusting whom exactly?
# Did anything in the path claim to have validated? Look for "ad" in the flags
dig +dnssec ad.example.com SOA | grep flags
# Validate independently of whatever the local resolver believes
delv ad.example.com SOA # "fully validated" is the line you want
# Is the resolver actually listening for DoT, and does its certificate
# match the name clients are configured with?
kdig +tls @192.0.2.53 ad.example.com SOA
openssl s_client -connect 192.0.2.53:853 \
-servername resolver.ad.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -dates
Und auf einem Windows-Client, um zu sehen, ob er überhaupt irgendetwas fordert:
Get-DnsClientNrptPolicy -Effective # the rules actually in force
Resolve-DnsName ad.example.com -DnssecOk
Get-DnsClientDohServerAddress # is the hop to the resolver encrypted?
Die zwei, die am häufigsten eine Überraschung erzeugen, sind der Rekursions-Check und der AXFR-Versuch. Wenn ein DC einen davon für einen beliebigen Client beantwortet, ist die Pipeline in diesem Beitrag nicht vorhanden, was auch immer das Diagramm im Wiki sagt.
Der dritte ist resolvectl status auf einer Maschine, die niemand angefasst hat. DNSSEC=no/unsupported neben trust-ad in resolv.conf ist der Normalzustand eines Linux-Desktops, und es heißt, die oben beschriebene Signier-Arbeit wird derzeit von niemandem geprüft.
Die Kurzfassung
Ein Resolver kommt zur Welt und kennt die Root-Server und einen Schlüssel und lernt alles andere, indem ihm gesagt wird. Namen werden durch das Laufen von Delegierungen hinunter gefunden, und Dienste werden durch das Fragen nach einem SRV-Eintrag gefunden — also kam, wenn ein Client anfängt, Kerberos mit einem Domänencontroller zu machen, die Identität dieses Domänencontrollers aus einer DNS-Antwort. Entdeckung per DNS ist korrekt und in Ordnung. Autorisierung per DNS ist es nicht, und eine überraschende Menge Infrastruktur tut es trotzdem leise.
DNSSEC ist, was diese Antworten überprüfbar macht: Signaturen auf jedem Satz, ein DS in jedem Elternteil, eine Kette zu einem einzigen Trust Anchor an der Root, und beglaubigte Verweigerung, sodass ein Name nicht zum Verschwinden gebracht werden kann. Es kauft Herkunftsauthentifizierung und Integrität — nicht Privatsphäre, und nicht Korrektheit. Es signiert, was auch immer die Zone sagt, weshalb es eine Zone nicht retten kann, die nicht vertrauenswürdige Maschinen schreiben dürfen. Und es braucht einen Elternteil: eine erfundene interne TLD hat nirgends ein DS unterzubringen, also lassen .local, .lan, .internal und home.arpa dich alle entweder unsigniert oder eine private Insel von Hand verteilter Schlüssel betreibend.
Für eine Active-Directory-Domäne erzeugt das ein Design statt einer Liste von Einstellungen. Benutze eine Delegierung innerhalb einer öffentlichen Zone, die du besitzt, sodass das Vertrauen die gewöhnliche Kette von der Root herunterkommt, während die Daten nie herausgehen. Stelle die AD-Partitionen mit BIND und dlz_bind9 bereit — DLZ ist nicht das Risiko, es ist, wie du die Zone aus dem Verzeichnis bekommst — und lass den DC ein Hidden Primary sein, der hinaustransferiert und niemandem sonst antwortet. Eine DLZ-Zone kann nicht selbst signiert werden, also ist das Signieren eine normale Secondary-Zone, die die transferierte Kopie hält, mit inline-signing und einer dnssec-policy darauf: ein zweites named auf dem DC, wenn du weniger Maschinen willst, ein separater Host, wenn du private Schlüssel vom Verzeichnis fernhalten willst. So oder so wird es einmal signiert, an einem definierten Punkt, unter einer Schlüsselrichtlinie — und der erste Hop ist ein Poll statt eines Push, weil DLZ kein Notify senden kann. Veröffentliche von eigenständigen autoritativen Servern, die keine Schlüssel halten und keinen Weg zum Verzeichnis haben.
Und halte Clients aus der Zone, die zählt. Sicheres dynamisches Update beglaubigt den Schreiber, nicht die Bedeutung, und in einer Standard-AD-integrierten Zone sind die Schreiber Authenticated Users — jedes Konto, nicht nur jede Maschine — also ist eine Zone, die sowohl Laptop-Einträge als auch _msdcs-Locators hält, einen gephishten Benutzer davon entfernt, dass einem Client mit einer völlig gültigen Signatur gesagt wird, der Domänencontroller sei woanders. Client-Registrierungen gehören in eine Sub-Zone, delegiert oder separat in Samba, wo das Schlimmste, das ein kompromittiertes Konto tun kann, ist, über sich selbst zu lügen.
Wobei die bessere Frage ist, ob Clients überhaupt registrieren sollten. Ein 2026er Laptop hat eine Adresse im Heim-WLAN, eine andere im Büro-WLAN, eine andere über die Dockingstation und eine andere im VPN, und er wird die meisten davon fröhlich veröffentlichen. Was den A-Eintrag eines Laptops verbraucht, ist fast nichts — die Werkzeuge, die eine Workstation erreichen müssen, halten ihr eigenes Inventar, weil DNS für mobile Clients ohnehin nie zuverlässig war. Einträge für Server sollten aus dem Provisioning kommen, und die Flotte sollte nichts registrieren.
Dann bring etwas dazu, die Signaturen zu prüfen, denn nichts vom Obigen ist etwas wert, bis ein Client eine Antwort ablehnt. Auf Linux heißt das DNSSEC=yes in systemd-resolved, oder ein echter validierender Resolver auf dem Loopback — niemals allow-downgrade, das ein Angreifer, der Antworten fälschen kann, einfach durch das Fälschen einer Antwort abschalten kann. Auf Windows heißt es zu akzeptieren, dass der DNS-Client selbst nie irgendetwas validiert, und eine NRPT-Regel zu benutzen, um ihn eine Antwort fordern zu lassen, die der Resolver validiert hat, mit geschütztem letztem Hop, weil diese Forderung ein einziges Bit ist. Schalte es zuerst an den Resolvern ein und achte auf SERVFAIL, automatisiere das DS, dann erzwinge die Clients. Es fällt geschlossen aus, was die richtige Richtung ist und trotzdem ein Ausfall.
Nichts davon ist exotisch. Es ist Delegierung, Transfer und Signieren — die drei Dinge, die DNS immer getan hat — so angeordnet, dass die Maschine, die dein Verzeichnis hält, nicht die Maschine ist, die Fragen vom Parkplatz annimmt, und dass, wenn etwas einen Client anlügt, der Client es bemerkt.