DNS über TLS - Verschlüsseln Sie unsere DNS-Abfragen mit Stunnel und Lua


Bildquelle


DNS (English. Domain Name System) ist ein verteiltes Computersystem zum Abrufen von Informationen zu Domänen.

TLS ( Transport Layer Security Protocol) - bietet eine sichere Datenübertragung zwischen Internetknoten.

Nach den Nachrichten "Google Public DNS hat DNS über TLS-Unterstützung leise aktiviert" habe ich beschlossen, es zu versuchen. Ich habe einen Stunnel , der einen verschlüsselten TCP- Tunnel erstellt. Programme kommunizieren jedoch normalerweise über das UDP- Protokoll mit DNS . Daher benötigen wir einen Proxy, der UDP-Pakete zum und vom TCP-Stream weiterleitet. Wir werden es auf Lua schreiben.


Der ganze Unterschied zwischen TCP- und UDP-DNS-Paketen:


4.2.2. TCP-Nutzung
Über TCP-Verbindungen gesendete Nachrichten verwenden den Server-Port 53 (dezimal). Der Nachricht wird ein Feld mit einer Länge von zwei Bytes vorangestellt, das die Nachrichtenlänge mit Ausnahme des Felds mit einer Länge von zwei Bytes angibt. In diesem Längenfeld kann die Verarbeitung auf niedriger Ebene eine vollständige Nachricht zusammenstellen, bevor mit dem Parsen begonnen wird.

RFC1035: DOMAIN-NAMEN - UMSETZUNG UND SPEZIFIKATION


Das heißt, wir machen es dort:


  1. Wir nehmen ein Paket von UDP
  2. Fügen Sie zu Beginn einige Bytes hinzu, in denen die Größe dieses Pakets angegeben ist
  3. an TCP-Kanal senden

Und in die entgegengesetzte Richtung:


  1. Wenn wir ein paar Bytes von TCP lesen, erhalten wir die Paketgröße
  2. Wir lesen ein Paket von TCP
  3. Senden Sie es per UDP an den Empfänger

Passen Sie Stunnel an


  1. Laden Sie das Stammzertifikat Root-R2.crt in das Verzeichnis mit der Stunnel-Konfiguration herunter
  2. Zertifikat in PEM konvertieren
    openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text 
  3. Wir schreiben in stunnel.conf:


     [dns] client = yes accept = 127.0.0.1:53 connect = 8.8.8.8:853 CAfile = Root-R2.pem verifyChain = yes checkIP = 8.8.8.8 


Das heißt, Stunnel:


  1. akzeptiert unverschlüsseltes TCP um 127.0.0.1:53
  2. öffnet einen verschlüsselten TLS-Tunnel unter der Adresse 8.8.8.8:853 (Google DNS)
  3. überträgt Daten hin und her

Stunnel starten


Der Betrieb des Tunnels kann mit dem folgenden Befehl überprüft werden:


 nslookup -vc ya.ru 127.0.0.1 

Die Option -vc zwingt nslookup, anstelle von UDP eine TCP-Verbindung zu einem DNS-Server zu verwenden.


Ergebnis:


 *** Can't find server name for address 127.0.0.1: Non-existent domain Server: UnKnown Address: 127.0.0.1 Non-authoritative answer: Name: ya.ru Address: ( IP ) 

Ein Skript schreiben


Ich schreibe in Lua 5.3 . Binäre Operationen mit Nummern sind bereits verfügbar. Nun, wir brauchen das Lua Socket Modul.


Dateiname: simple-udp-to-tcp-dns-proxy.lua


 local socket = require "socket" --  lua socket 

--[[--


Schreiben wir eine einfache Funktion, mit der Sie ein Dump-Paket an die Konsole senden können. Ich möchte sehen, was der Proxy macht.


--]]--


 function serialize(data) --       az  0-9    HEX  '\xFF' return "'"..data:gsub("[^a-z0-9-]", function(chr) return ("\\x%02X"):format(chr:byte()) end).."'" end 

--[[--


UDP zu TCP und zurück


Wir schreiben zwei Funktionen, die mit zwei Datenübertragungskanälen arbeiten.


--]]--


 --    UDP   TCP  function udp_to_tcp_coroutine_function(udp_in, tcp_out, clients) repeat coroutine.yield() --     packet, err_ip, port = udp_in:receivefrom() --  UDP  if packet then -- > - big endian -- I - unsigned integer -- 2 - 2 bytes size tcp_out:send(((">I2"):pack(#packet))..packet) --       TCP local id = (">I2"):unpack(packet:sub(1,2)) --  ID  if not clients[id] then clients[id] = {} end table.insert(clients[id] ,{ip=err_ip, port=port, packet=packet}) --    print(os.date("%c", os.time()) ,err_ip, port, ">", serialize(packet)) --     end until false end --    TCP      UDP function tcp_to_udp_coroutine_function(tcp_in, udp_out, clients) repeat coroutine.yield() --     -- > - big endian -- I - unsigned integer -- 2 - 2 bytes size local packet = tcp_in:receive((">I2"):unpack(tcp_in:receive(2)), nil) --  TCP  local id = (">I2"):unpack(packet:sub(1,2)) --  ID  if clients[id] then for key, client in pairs(clients[id]) do --  query     if packet:find(client.packet:sub(13, -1), 13, true) == 13 then --   udp_out:sendto(packet, client.ip, client.port) --     UDP clients[id][key] = nil --   --     print(os.date("%c", os.time()) ,client.ip, client.port, "<", serialize(packet)) break end end if not next(clients[id]) then clients[id] = nil end end until false end 

--[[--


Beide Funktionen führen coroutine.yield () unmittelbar nach dem Start aus. Auf diese Weise können Sie die Parameter der Funktion an den ersten Aufruf übergeben und dann coroutine.resume (co) ohne zusätzliche Parameter ausführen.


Haupt


Und jetzt die Hauptfunktion, die die Hauptschleife vorbereitet und startet.


--]]--


 function main() local tcp_dns_socket = socket.tcp() --  TCP  local udp_dns_socket = socket.udp() --  UDP  local tcp_connected, err = tcp_dns_socket:connect("127.0.0.1", 53) --   TCP  assert(tcp_connected, err) --    print("tcp dns connected") --      local udp_open, err = udp_dns_socket:setsockname("127.0.0.1", 53) --  UDP  assert(udp_open, err) --    print("udp dns port open") --   UDP   --     Lua        nil --              local coroutines = { [tcp_dns_socket] = coroutine.create(tcp_to_udp_coroutine_function), --   TCP to UDP [udp_dns_socket] = coroutine.create(udp_to_tcp_coroutine_function) --   UDP to TCP } local clients = {} --      --        coroutine.resume(coroutines[tcp_dns_socket], tcp_dns_socket, udp_dns_socket, clients) coroutine.resume(coroutines[udp_dns_socket], udp_dns_socket, tcp_dns_socket, clients) --    socket.select        local socket_list = {tcp_dns_socket, udp_dns_socket} repeat --    -- socket.select   socket_list          --      .  for       for _, in_socket in ipairs(socket.select(socket_list)) do --       local ok, err = coroutine.resume(coroutines[in_socket]) if not ok then --       udp_dns_socket:close() --  UDP  tcp_dns_socket:close() --  TCP  print(err) --   return --    end end until false end 

--[[--


Wir starten die Hauptfunktion. Wenn die Verbindung plötzlich geschlossen wird, stellen wir sie nach einer Sekunde wieder her, indem wir main aufrufen.


--]]--


 repeat local ok, err = coroutine.resume(coroutine.create(main)) --  main if not ok then print(err) end socket.sleep(1) --      until false 

überprüfen


  1. Stunnel laufen lassen


  2. Führen Sie unser Skript aus


     lua5.3 simple-udp-to-tcp-dns-proxy.lua 

  3. Überprüfen Sie die Skriptoperation mit dem Befehl


     nslookup ya.ru 127.0.0.1 

    Dieses Mal haben wir ohne das '-vc' den Proxy geschrieben und gestartet, der UDP-DNS-Anforderungen in den TCP-Tunnel packt.



Ergebnis:


 *** Can't find server name for address 127.0.0.1: Non-existent domain Server: UnKnown Address: 127.0.0.1 Non-authoritative answer: Name: ya.ru Address: ( IP ) 

Wenn alles in Ordnung ist, können Sie in den Verbindungseinstellungen als DNS-Server "127.0.0.1" angeben.


Schlussfolgerung


Jetzt sind unsere DNS-Abfragen durch TLS geschützt.


PS Wir geben Google keine zusätzlichen Informationen über uns


Unmittelbar nach der Verbindung versucht Windows, uns über unseren Tunnel auf den DNS-Servern von Google zu registrieren. Dies wird in den erweiterten DNS-Einstellungen durch Deaktivieren deaktiviert.



Es gibt noch eine Anfrage für time.windows.com. Er ist nicht mehr so ​​persönlich, aber ich habe nie herausgefunden, wie ich es ausschalten kann. Die automatische Zeitsynchronisation ist deaktiviert.


Links


  1. RFC1035: DOMAIN-NAMEN - UMSETZUNG UND SPEZIFIKATION
  2. DNS über TLS
  3. simple-udp-to-tcp-dns-proxy.lua
  4. Erstellen Sie manuell eine DNS-Abfrage

Source: https://habr.com/ru/post/de427957/


All Articles