DNS sur TLS - Chiffrez nos requêtes DNS en utilisant Stunnel et Lua


source d'image


DNS (Eng. Domain Name System - un système de noms de domaine) - un système informatique distribué pour obtenir des informations sur les domaines.

TLS ( Transport Layer Security Protocol) - fournit un transfert de données sécurisé entre les nœuds Internet.

Après la nouvelle, "Google Public DNS a discrètement activé le support DNS sur TLS", j'ai décidé de l'essayer. J'ai un Stunnel qui créera un tunnel TCP crypté. Mais les programmes communiquent généralement avec DNS en utilisant le protocole UDP . Par conséquent, nous avons besoin d'un proxy qui transmettra les paquets UDP vers et depuis le flux TCP. Nous l'écrirons sur Lua .


Toute la différence entre les paquets DNS TCP et UDP:


4.2.2. Utilisation TCP
Les messages envoyés via des connexions TCP utilisent le port de serveur 53 (décimal). Le message est préfixé par un champ de longueur de deux octets qui donne la longueur du message, à l'exclusion du champ de longueur de deux octets. Ce champ de longueur permet au traitement de bas niveau d'assembler un message complet avant de commencer à l'analyser.

RFC1035: NOMS DE DOMAINE - MISE EN ŒUVRE ET SPÉCIFICATION


Autrement dit, nous faisons là:


  1. nous prenons un paquet de UDP
  2. ajoutez-y au début quelques octets dans lesquels la taille de ce paquet est indiquée
  3. envoyer au canal TCP

Et en sens inverse:


  1. nous lisons quelques octets de TCP, nous obtenons la taille du paquet
  2. nous lisons un paquet de TCP
  3. l'envoyer au destinataire via UDP

Personnaliser Stunnel


  1. Téléchargez le certificat racine Root-R2.crt dans le répertoire avec la configuration Stunnel
  2. Convertir le certificat en PEM
    openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text 
  3. Nous écrivons dans 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 


Autrement dit, Stunnel:


  1. acceptera le TCP non chiffré à 127.0.0.1:53
  2. ouvrira un tunnel TLS chiffré pour l'adresse 8.8.8.8:853 (Google DNS)
  3. va transférer des données dans les deux sens

Lancer Stunnel


Le fonctionnement du tunnel peut être vérifié avec la commande:


 nslookup -vc ya.ru 127.0.0.1 

L'option -vc force nslookup à utiliser une connexion TCP vers un serveur DNS au lieu d'UDP.


Résultat:


 *** 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 ) 

Écrire un script


J'écris en Lua 5.3 . Des opérations binaires avec des nombres y sont déjà disponibles. Eh bien, nous aurons besoin du module Lua Socket .


Nom du fichier: simple-udp-to-tcp-dns-proxy.lua


 local socket = require "socket" --  lua socket 

--[[--


Écrivons une fonction simple qui vous permettra d'envoyer un paquetage de vidage à la console. Je veux voir ce que fait le proxy.


--]]--


 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 à TCP et retour


Nous écrivons deux fonctions qui fonctionneront avec deux canaux de transmission de données.


--]]--


 --    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 

--[[--


Les deux fonctions exécutent coroutine.yield () immédiatement après le lancement. Cela vous permet de passer les paramètres de la fonction au premier appel, puis de faire coroutine.resume (co) sans paramètres supplémentaires.


principal


Et maintenant, la fonction principale qui préparera et démarrera la boucle principale.


--]]--


 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 

--[[--


Nous lançons la fonction principale. Si la connexion est soudainement fermée, après une seconde, nous la rétablirons en appelant le principal.


--]]--


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

vérifier


  1. Run stunnel


  2. Exécutez notre script


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

  3. Vérifiez l'opération de script avec la commande


     nslookup ya.ru 127.0.0.1 

    Cette fois, sans le '-vc', c'est ainsi que nous avons écrit et démarré le proxy, qui encapsule les requêtes DNS UDP dans le tunnel TCP.



Résultat:


 *** 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 ) 

Si tout va bien, vous pouvez spécifier dans les paramètres de connexion le serveur DNS "127.0.0.1"


conclusion


Désormais, nos requêtes DNS sont protégées par TLS .


PS Nous ne donnons pas à Google des informations supplémentaires sur nous


Juste après la connexion, Windows essaie de nous enregistrer sur les serveurs DNS de Google via notre tunnel. Ceci est désactivé dans les paramètres DNS avancés en décochant.



Il y a toujours une demande pour time.windows.com. Il n'est plus aussi personnel, mais je n'ai jamais trouvé comment l'éteindre. La synchronisation automatique de l'heure est désactivée.


liens


  1. RFC1035: NOMS DE DOMAINE - MISE EN ŒUVRE ET SPÉCIFICATION
  2. DNS sur TLS
  3. simple-udp-to-tcp-dns-proxy.lua
  4. Composer manuellement une requête DNS

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


All Articles