DNS sobre TLS - Cifre nuestras consultas DNS usando Stunnel y Lua


fuente de imagen


DNS (Ing. Domain Name System - un sistema de nombres de dominio) - un sistema informático distribuido para obtener información sobre dominios.

TLS (Protocolo de seguridad de la capa de transporte ): proporciona transferencia de datos segura entre nodos de Internet.

Después de la noticia, "Google Public DNS activó silenciosamente DNS sobre TLS" , decidí probarlo. Tengo un Stunnel que creará un túnel TCP encriptado. Pero los programas generalmente se comunican con DNS usando el protocolo UDP . Por lo tanto, necesitamos un proxy que reenvíe los paquetes UDP hacia y desde la secuencia TCP. Lo escribiremos en Lua .


La diferencia entre los paquetes DNS TCP y UDP:


4.2.2 Uso de TCP
Los mensajes enviados a través de conexiones TCP utilizan el puerto del servidor 53 (decimal). El mensaje tiene como prefijo un campo de longitud de dos bytes que proporciona la longitud del mensaje, excluyendo el campo de longitud de dos bytes. Este campo de longitud permite que el procesamiento de bajo nivel reúna un mensaje completo antes de comenzar a analizarlo.

RFC1035: NOMBRES DE DOMINIO - IMPLEMENTACIÓN Y ESPECIFICACIÓN


Es decir, hacemos allí:


  1. tomamos un paquete de UDP
  2. agregue al principio un par de bytes en los que se indica el tamaño de este paquete
  3. enviar al canal TCP

Y en la dirección opuesta:


  1. leemos un par de bytes de TCP, obtenemos el tamaño del paquete
  2. leemos un paquete de TCP
  3. envíalo al destinatario a través de UDP

Personalizar Stunnel


  1. Descargue el certificado raíz Root-R2.crt en el directorio con la configuración Stunnel
  2. Convertir certificado a PEM
    openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text 
  3. Escribimos en 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 


Es decir, Stunnel:


  1. aceptará TCP sin cifrar en 127.0.0.1:53
  2. abrirá un túnel TLS cifrado para direccionar 8.8.8.8:853 (Google DNS)
  3. transferirá datos de ida y vuelta

Lanzar Stunnel


El funcionamiento del túnel se puede verificar con el comando:


 nslookup -vc ya.ru 127.0.0.1 

La opción -vc obliga a nslookup a usar una conexión TCP a un servidor DNS en lugar de UDP.


Resultado:


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

Escribir un guion


Estoy escribiendo en Lua 5.3 . Las operaciones binarias con números ya están disponibles en él. Bueno, necesitaremos el módulo Lua Socket .


Nombre de archivo: simple-udp-to-tcp-dns-proxy.lua


 local socket = require "socket" --  lua socket 

--[[--


Escribamos una función simple que le permitirá enviar un paquete de volcado a la consola. Quiero ver qué hace el 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 a TCP y viceversa


Escribimos dos funciones que operarán con dos canales de transmisión de datos.


--]]--


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

--[[--


Ambas funciones ejecutan coroutine.yield () inmediatamente después del lanzamiento. Esto le permite pasar los parámetros de la función a la primera llamada y luego hacer coroutine.resume (co) sin parámetros adicionales.


principal


Y ahora la función principal que preparará e iniciará el bucle principal.


--]]--


 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 

--[[--


Lanzamos la función principal. Si la conexión se cierra repentinamente, después de un segundo la estableceremos nuevamente llamando a main.


--]]--


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

comprobar


  1. Correr aturdimiento


  2. Ejecute nuestro script


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

  3. Verifique la operación del script con el comando


     nslookup ya.ru 127.0.0.1 

    Esta vez, sin el '-vc', así es como escribimos e iniciamos el proxy, que envuelve las solicitudes UDP DNS en el túnel TCP.



Resultado:


 *** 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 todo está bien, puede especificar en la configuración de conexión como el servidor DNS "127.0.0.1"


conclusión


Ahora nuestras consultas DNS están protegidas por TLS .


PD: no le damos a Google información adicional sobre nosotros


Justo después de la conexión, Windows intenta registrarnos en los servidores DNS de Google a través de nuestro túnel. Esto está deshabilitado en la configuración avanzada de DNS desmarcando.



Todavía hay una solicitud para time.windows.com. Ya no es tan personal, pero nunca encontré cómo apagarlo. La sincronización horaria automática está deshabilitada.


enlaces


  1. RFC1035: NOMBRES DE DOMINIO - IMPLEMENTACIÓN Y ESPECIFICACIÓN
  2. DNS sobre TLS
  3. simple-udp-to-tcp-dns-proxy.lua
  4. Redactar manualmente una consulta DNS

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


All Articles