BLOG | NGINX

Verwenden von NGINX als DoT- oder DoH-Gateway

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Mark Boddington Miniaturbild
Mark Boddington
Veröffentlicht am 22. Februar 2022

Über das Domain Name System (DNS) wird derzeit viel diskutiert und es werden umfassende Änderungen für das 36 Jahre alte Protokoll vorgeschlagen. Der Namendienst des Internets, der seinen Ursprung im ARPANET hat, wies seit seiner Einführung niemals Probleme mit der Abwärtskompatibilität auf. Neue Vorschläge zur Änderung des DNS-Transportmechanismus könnten dies jedoch bald ändern.

In diesem Beitrag betrachte ich zwei neue Technologien zur Sicherung von DNS, DNS over TLS (DoT) und DNS over HTTPS (DoH), und zeige, wie sie mit NGINX Open Source und NGINX Plus implementiert werden.

[ Herausgeber – Dieser Beitrag ist einer von mehreren, die Anwendungsfälle für das NGINX-JavaScript-Modul untersuchen. Eine vollständige Liste finden Sie unter Anwendungsfälle für das NGINX-JavaScript-Modul .

Der Code in diesem Beitrag wurde aktualisiert, um die Direktive js_import zu verwenden, die die veraltete Direktive js_include in NGINX Plus R23<.htmla> und höher ersetzt. Weitere Informationen finden Sie in der Referenzdokumentation für das NGINX-JavaScript-Modul – der Abschnitt „Beispielkonfiguration“ zeigt die richtige Syntax für die NGINX-Konfiguration und JavaScript-Dateien.]

Die Geschichte des DNS

DNS war der zweite Versuch, einen Namensdienst für das frühe Internet der Advanced Research Project Agency (ARPA) zu erstellen. Der erste Versuch war das Internet Name Server-Protokoll, das 1979 von John Postel veröffentlicht wurde als IEN-116. DNS wurde hierarchisch konzipiert und bietet eine Struktur für die Dezentralisierung von Hostnamen in Zonen und die Verwaltung durch viele separate Behörden. Die ersten RFCs für DNS wurden 1983 veröffentlicht (RFC882 Und883 ), und obwohl es im Laufe der Jahre mehrere Erweiterungen gab, würde ein Client, der nach den damals definierten Standards geschrieben wurde, auch heute noch funktionieren.

Warum also jetzt das Protokoll ändern? Es funktioniert offensichtlich wie vorgesehen und rechtfertigt damit die Zuversicht seiner Autoren, die sich ihrer Sache so sicher waren, dass sie keine Versionsnummer in das DNS-Paket aufgenommen haben – mir fallen nicht viele andere Protokolle ein, die das von sich behaupten können. DNS wurde in einer unschuldigeren Zeit konzipiert, als die meisten Protokolle im Klartext und häufig im 7-Bit-ASCII-Format vorlagen. Das Internet von heute ist jedoch ein viel furchteinflößenderer Ort als das ARPANET der 1980er Jahre. Heutzutage nutzen die meisten Protokolle Transport Layer Security (TLS) für Verschlüsselungs- und Verifizierungszwecke. Kritiker des DNS argumentieren, dass zusätzliche Sicherheitsvorkehrungen längst überfällig seien.

So wie DNS der zweite Versuch war, ein Namendienstprotokoll für das Internet bereitzustellen, stellen DNS over TLS (DoT) und DNS over HTTPS (DoH) zweite Versuche dar, das DNS-Protokoll zu sichern. Der erste Versuch war eine Erweiterung namens DNSSEC . Obwohl die meisten Top-Level-Domains (TLDs) DNSSEC verwenden, war es nie dazu gedacht, die in den DNS-Paketen übertragenen Daten zu verschlüsseln. Es dient lediglich als Nachweis dafür, dass die Daten nicht manipuliert wurden. DoT und DoH sind Protokollerweiterungen, die DNS in einen TLS-Tunnel einbinden, und wenn sie übernommen werden, beenden sie 36 Jahre Abwärtskompatibilität.

DoT und DoH im Detail

DoT wird meiner Meinung nach weitgehend als sinnvolle Erweiterung angesehen. Es hat bereits eine eigene Portnummer (TCP/853) von der Internet Assigned Numbers Authority (IANA) zugewiesen bekommen und verpackt TCP-DNS-Pakete einfach in einen TLS-verschlüsselten Tunnel. Viele Protokolle haben dies bereits getan: HTTPS ist HTTP innerhalb eines TLS-Tunnels und SMTPS, IMAPS und LDAPS sind sichere Versionen dieser Protokolle. DNS hat immer UDP (oder in bestimmten Fällen TCP) als Transportprotokoll verwendet, daher stellt das Hinzufügen des TLS-Wrappers keine große Änderung dar.

DoH hingegen ist etwas umstrittener. DoH nimmt ein DNS-Paket und verpackt es in eine HTTP- GET- oder POST- Anfrage, die dann mit HTTP/2 oder höher über eine HTTPS-Verbindung gesendet wird. Dies scheint im Grunde wie jede andere HTTPS-Verbindung zu sein und es ist für Unternehmen oder Dienstanbieter unmöglich zu sehen, welche Anfragen gestellt werden. Mozilla und andere Befürworter sagen, dass diese Vorgehensweise die Privatsphäre der Benutzer erhöht, indem sie die von ihnen besuchten Websites vor neugierigen Blicken schützt.

Aber das stimmt nicht ganz. Kritiker von DoH weisen darauf hin, dass es sich nicht ganz um das Tor des DNS handele, denn wenn der Browser schließlich die Verbindung zu dem Host herstellt, den er mithilfe von DoH gesucht hat, wird die Anfrage mit ziemlicher Sicherheit die TLS-Erweiterung „ Server Name Indication“ (SNI) verwenden – und diese enthält den Hostnamen und wird im Klartext gesendet. Wenn der Browser außerdem versucht, das Zertifikat des Servers mithilfe des Online Certificate Status Protocol (OSCP) zu validieren, erfolgt dieser Vorgang höchstwahrscheinlich ebenfalls im Klartext. Jeder, der DNS-Lookups überwachen kann, hat also auch die Möglichkeit, die SNI in der Verbindung oder den Zertifikatsnamen in der OCSP-Validierung zu lesen.

Für viele Menschen besteht das größte Problem mit DoH darin, dass Browseranbieter die DNS-Server auswählen, an die DoH-Anfragen ihrer Benutzer standardmäßig gesendet werden (im Fall von Firefox-Benutzern in den USA gehören die DNS-Server beispielsweise zu Cloudflare ). Der Betreiber der DNS-Server kann die IP-Adresse des Benutzers und die Domänennamen der Sites sehen, an die er Anfragen stellt. Das mag nicht viel erscheinen, doch Forscher der University of Illinois haben herausgefunden, dass sich allein aus den Zieladressen der Anfragen nach Elementen einer Webseite, dem sogenannten Page Load Fingerprint, ableiten lässt, welche Websites eine Person besucht hat . Diese Informationen können dann verwendet werden, um „Profile des Benutzers zu erstellen und gezielt Werbung für ihn auszuspielen“.

Wie kann NGINX helfen?

DoT und DoH sind nicht grundsätzlich schlecht und es gibt Anwendungsfälle, in denen sie die Privatsphäre der Benutzer erhöhen. Es besteht jedoch zunehmend Konsens darüber, dass ein öffentlicher, zentralisierter DoH-Dienst die Privatsphäre der Benutzer beeinträchtigt, und wir empfehlen, die Verwendung eines solchen Dienstes um jeden Preis zu vermeiden.

In jedem Fall verwalten Sie für Ihre Websites und Apps wahrscheinlich Ihre eigenen DNS-Zonen – einige öffentlich, einige privat und einige mit Split Horizon. Irgendwann möchten Sie möglicherweise Ihren eigenen DoT- oder DoH-Dienst betreiben. Hier kann NGINX helfen.

Die durch DoT bereitgestellten Datenschutzverbesserungen bieten einige großartige Vorteile für die DNS-Sicherheit. Was aber, wenn Ihr aktueller DNS-Server keine Unterstützung für DoT bietet? NGINX kann hier helfen, indem es ein Gateway zwischen DoT und Standard-DNS bereitstellt.

Oder vielleicht profitieren Sie vom Potenzial von DoH, die Firewall zu durchbrechen, wenn der DoT-Port blockiert sein könnte. Auch hier kann NGINX helfen, indem es ein DoH-zu-DoT/DNS- Gateway bereitstellt.

Bereitstellen eines einfachen DoT-DNS-Gateways

Das NGINX Stream-Modul (TCP/UDP) unterstützt SSL-Terminierung, sodass die Einrichtung eines DoT-Dienstes eigentlich ganz einfach ist. Sie können mit nur wenigen Zeilen der NGINX-Konfiguration ein einfaches DoT-Gateway erstellen.

Sie benötigen einen Upstream- Block für Ihre DNS-Server und einen Server- Block für die TLS-Terminierung:

Natürlich können wir auch den umgekehrten Weg gehen und eingehende DNS-Anfragen an einen vorgelagerten DoT-Server weiterleiten. Dies ist jedoch weniger nützlich, da der meiste DNS-Verkehr UDP ist und NGINX nur zwischen DoT und anderen TCP-Diensten, wie etwa TCP-basiertem DNS, übersetzen kann.

Ein einfaches DoH-DNS-Gateway

Im Vergleich zu einem DoT-Gateway ist die Konfiguration eines einfachen DoH-Gateways etwas komplexer. Wir benötigen sowohl einen HTTPS-Dienst als auch einen Stream-Dienst und verwenden JavaScript-Code und das NGINX-JavaScript-Modul <.htmla> (njs), um zwischen den beiden Protokollen zu übersetzen. Die einfachste Konfiguration ist:

Diese Konfiguration führt die minimale Verarbeitungsmenge aus, die zum Senden des Pakets an den DNS-Dienst erforderlich ist. Dieser Anwendungsfall geht davon aus, dass der Upstream-DNS-Server alle anderen Filter-, Protokollierungs- oder Sicherheitsfunktionen ausführt.

Das in dieser Konfiguration verwendete JavaScript-Skript ( nginx_stream.js ) enthält verschiedene DNS-Bibliotheksmoduldateien. Innerhalb des Moduls dns.js legt die Variable dns_decode_level fest, wie viel Verarbeitung an den DNS-Paketen durchgeführt wird. Die Verarbeitung von DNS-Paketen beeinträchtigt offensichtlich die Leistung. Wenn Sie eine Konfiguration wie die oben genannte verwenden, setzen Sie dns_decode_level auf0 .

Ein fortschrittlicheres DoH-Gateway

Hier bei NGINX sind wir wirklich ziemlich gut bei HTTP und daher glauben wir, dass die Verwendung von NGINX lediglich als einfaches DoH-Gateway eine verpasste Gelegenheit ist.

Der hier verwendete JavaScript-Code kann so eingerichtet werden, dass er die DNS-Pakete vollständig oder teilweise dekodiert. Dadurch können wir einen HTTP-Inhaltscache für die DoH-Abfragen mit Expires- und Cache-Control -Headern erstellen, die basierend auf den Mindest-TTLs der DNS-Antworten festgelegt sind.

Es folgt ein ausführlicheres Beispiel mit zusätzlicher Verbindungsoptimierung und Unterstützung für die Zwischenspeicherung und Protokollierung von Inhalten:

Ein erweiterter DNS-Filter mit NGINX Plus-Funktionen

Wenn Sie ein NGINX Plus-Abonnement haben, können Sie die obigen Beispiele mit einigen der erweiterten NGINX Plus-Funktionen kombinieren, etwa aktiven Integritätsprüfungen und hoher Verfügbarkeit, oder sogar die Cache-Bereinigungs-API zum Verwalten zwischengespeicherter DoH-Antworten verwenden.

Darüber hinaus besteht die Möglichkeit, mit dem Schlüssel-Wert-Speicher von NGINX Plus ein DNS-Filtersystem aufzubauen, das Benutzer vor bösartigen Domänen schützt, indem es eine DNS-Antwort zurückgibt, die den Zugriff auf diese Domänen wirksam verhindert. Sie verwalten den Inhalt des Schlüssel-Wert-Speichers dynamisch mit der RESTful NGINX Plus API .

Wir definieren zwei Kategorien bösartiger Domänen: „blockiert“ und „Blackhole“. Das DNS-Filtersystem behandelt eine DNS-Abfrage zu einer Domäne je nach Kategorie unterschiedlich:

  • Für blockierte Domänen wird die Antwort NXDOMAIN zurückgegeben (was bedeutet, dass die Domäne nicht existiert).
  • Für Blackhole -Domänen gibt es einen einzelnen DNS- A- Eintrag von 0.0.0.0 als Antwort auf Anfragen nach A -Einträgen oder einen einzelnen AAAA- Eintrag von :: als Antwort auf Anfragen nach AAAA -Einträgen (IPv6). Für andere Eintragstypen gibt es eine Antwort mit null Antworten zurück.

Wenn unser DNS-Filter eine Anfrage erhält, sucht er zunächst nach einem Schlüssel, der genau mit dem abgefragten FQDN übereinstimmt. Wenn eine solche Anforderung gefunden wird, wird sie „bereinigt“ (blockiert oder sperrt sie), wie durch den zugehörigen Wert angegeben. Wenn keine genaue Übereinstimmung vorliegt, wird in zwei Listen – einer Liste mit blockierten und einer Liste mit Blackhole-Domänen – nach dem Domänennamen gesucht und die Anfrage bei einer Übereinstimmung in der entsprechenden Weise bereinigt.

Handelt es sich bei der abgefragten Domain nicht um Schadsoftware, leitet das System diese zur regulären Weiterverarbeitung an einen Google DNS-Server weiter.

Einrichten des Schlüssel-Wert-Speichers

Wir definieren die Domänen, die wir als bösartig betrachten, in zwei Arten von Einträgen im Schlüssel-Wert-Speicher von NGINX Plus:

  • Einzelne Einträge für vollqualifizierte Domänennamen (FQDNs), jeweils mit dem Wert „ blockiert“ oder „schwarzes Loch“
  • Zwei Listen von Domänen mit den Schlüsseln „blocked_domains“ und „blackholed_domains“ , die jeweils einer Liste von Domänen im CSV-Format (Comma‑Separated Value) zugeordnet sind

Die folgende Konfiguration für den Schlüssel-Wert-Speicher erfolgt im Stream -Kontext. Die Direktive keyval_zone reserviert einen Speicherblock für den Schlüssel-Wert-Speicher, genannt dns_config . Die erste Direktive keyval lädt jedes passende FQDN-Schlüssel-Wert-Paar, das festgelegt wurde, während die zweite und dritte Direktive die beiden Domänenlisten definieren:

Wir können dann die NGINX Plus-API verwenden, um den Wert „blockiert“ oder „Blackhole“ jedem FQDN-Schlüssel zuzuweisen, den wir explizit bereinigen möchten, oder die CSV-formatierte Liste ändern, die mit dem Schlüssel „blocked_domains“ oder „blackhole_domains“ verknüpft ist. Wir können jederzeit genaue FQDNs ändern oder entfernen oder den Domänensatz in einer der Listen ändern. Der DNS-Filter wird dann sofort mit den Änderungen aktualisiert.

Auswählen des Upstreamservers zur Bearbeitung der Anforderung

Die folgende Konfiguration lädt die Variable $dns_response , die durch eine js_preread -Direktive in den Serverblöcken gefüllt wird, die weiter unten im Abschnitt „Konfigurieren der Server, die auf DNS-Abfragen warten“ definiert sind. Wenn der aufgerufene JavaScript-Code feststellt, dass die Anforderung bereinigt werden muss, setzt er die Variable entsprechend auf „Blockiert“ oder „Blackhole“ .

Wir verwenden eine Map- Direktive, um den Wert der Variable „$dns_response “ der Variable „$upstream_pool“ zuzuweisen und so zu steuern, welche der Upstream-Gruppen in „Definieren der Upstream-Server“ unten die Anforderung verarbeitet. Abfragen für nicht bösartige Domänen werden an den standardmäßigen Google-DNS-Server weitergeleitet, während Abfragen für blockierte oder in ein Blackhole verschobene Domänen vom Upstream-Server für diese Kategorien verarbeitet werden.

Definieren der Upstreamserver

Diese Konfiguration definiert die blockierten , Blackhole- und Google- Gruppen von Upstream-Servern, die jeweils Anfragen für blockierte, Blackhole- und nicht bösartige Domänen verarbeiten.

Konfigurieren der Server, die auf DNS-Abfragen warten

Hier definieren wir die Server im Streamkontext , die auf eingehende DNS-Anfragen warten. Die Direktive js_preread ruft den Javascript-Code auf, der das DNS-Paket dekodiert, den Domänennamen aus dem NAME- Feld des Pakets abruft und die Domäne im Schlüssel-Wert-Speicher nachschlägt. Wenn der Domänenname mit einem FQDN-Schlüssel übereinstimmt oder sich innerhalb der Zone einer der Domänen in der Liste „blocked_domains“ oder „blackhole_domains“ befindet, wird er gelöscht. Andernfalls wird es zur Auflösung an den Google DNS-Server gesendet.

Das ist fast alles – wir müssen nur noch unseren letzten Serverblock hinzufügen, der auf Abfragen entweder mit der Blackholed- oder der blockierten Antwort reagiert.

Beachten Sie, dass die Server in diesem Block fast identisch mit den tatsächlichen DNS-Servern direkt darüber sind. Der Hauptunterschied besteht darin, dass diese Server ein Antwortpaket an den Client senden, anstatt die Anforderung an eine vorgelagerte Gruppe von DNS-Servern weiterzuleiten. Unser JavaScript-Code erkennt, wenn er von Port 9953 oder 9853 ausgeführt wird, und anstatt ein Flag zu setzen, um anzuzeigen, dass das Paket blockiert werden soll, füllt er $dns_response mit dem tatsächlichen Antwortpaket. Das ist alles, was dazu zu sagen ist.

Natürlich hätten wir diese Filterung auch auf DoT- und DoH-Dienste anwenden können, aber der Einfachheit halber haben wir Standard-DNS verwendet. Das Zusammenführen der DNS-Filterung mit dem DoH-Gateway bleibt dem Leser als Übung überlassen.

Testen des Filters

Wir haben zuvor die NGINX Plus-API verwendet, um Einträge für zwei FQDNs und einige Domänen zu den beiden Domänenlisten hinzuzufügen:

$ curl -s http://localhost:8080/api/5/stream/keyvals/dns_config | jq { "www.some.bad.host": "blockiert", "www.some.other.host": "schwarzes Loch", "blockierte_Domänen": "bar.com,baz.com", "schwarzes_Loch_Domänen": "foo.com,nginx.com" }

Wenn wir also die Auflösung von www.foo.com (einem Host innerhalb der gesperrten Domäne foo.com ) anfordern, erhalten wir einen A- Eintrag mit der IP-Adresse 0.0.0.0:

$ dig @localhost www.foo.com ; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> @localhost www.foo.com ; (1 Server gefunden) ;; Globale Optionen: +mcd ;; Antwort erhalten: ;; ->>HEADER,,- Opcode: ABFRAGE, Status: KEIN FEHLER, ID: 58558 ;; Flags: qr aa rd ad; ANFRAGE: 1, ANTWORT: 1, AUTORITÄT: 0, ZUSÄTZLICH: 0 ;; WARNUNG: Rekursion angefordert, aber nicht verfügbar ;; FRAGENABSCHNITT: ; www.foo.com.                  IN EINEM ;; ANTWORTABSCHNITT: www.foo.com.           300 IN A 0.0.0.0 ;; Abfragezeit: 0 ms ;; SERVER: 172.0.0.1#53(126.0.0.1) ;; WANN: Montag, 2. Dezember 2019, 14:31:35 UTC ;; MSG SIZEW empfangen: 45

Zugriff auf den Code

Die Dateien für DOH und DOT sind in meinem GitHub-Repo verfügbar:

  • Der JavaScript-Code im Ordner njs.d/dns
  • Die NGINX-Konfiguration im Beispiele -Ordner

Um NGINX Plus auszuprobieren, starten Sie noch heute Ihre 30-tägige kostenlose Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen .


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."