DNS sobre TLS - Criptografe nossas consultas DNS usando Stunnel e Lua


fonte da imagem


DNS (Eng. Domain Name System - um sistema de nomes de domínio) - um sistema de computador distribuído para obter informações sobre domínios.

TLS ( Transport Layer Security Protocol) - fornece transferência segura de dados entre nós da Internet.

Após a notícia "O DNS público do Google ativou silenciosamente o suporte ao DNS por TLS" , decidi tentar. Eu tenho um Stunnel que criará um túnel TCP criptografado. Mas os programas geralmente se comunicam com o DNS usando o protocolo UDP . Portanto, precisamos de um proxy que encaminhe pacotes UDP de e para o fluxo TCP. Vamos escrever em Lua .


Toda a diferença entre pacotes DNS TCP e UDP:


4.2.2 Uso de TCP
As mensagens enviadas por conexões TCP usam a porta 53 do servidor (decimal). A mensagem é prefixada com um campo de dois bytes que fornece o comprimento da mensagem, excluindo o campo de dois bytes. Esse campo de comprimento permite que o processamento de baixo nível monte uma mensagem completa antes de começar a analisá-la.

RFC1035: NOMES DE DOMÍNIO - IMPLEMENTAÇÃO E ESPECIFICAÇÃO


Ou seja, fazemos lá:


  1. pegamos um pacote do UDP
  2. adicione a ele no começo alguns bytes nos quais o tamanho deste pacote é indicado
  3. enviar para o canal TCP

E na direção oposta:


  1. lemos alguns bytes do TCP, obtemos o tamanho do pacote
  2. lemos um pacote do TCP
  3. envie para o destinatário via UDP

Personalizar Stunnel


  1. Faça o download do certificado raiz Root-R2.crt no diretório com a configuração Stunnel
  2. Converter certificado em PEM
    openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text 
  3. Escrevemos em 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 


Ou seja, Stunnel:


  1. aceitará TCP não criptografado em 127.0.0.1:53
  2. abrirá um túnel TLS criptografado para endereçar 8.8.8.8:853 (DNS do Google)
  3. irá transferir dados para frente e para trás

Launch Stunnel


A operação do túnel pode ser verificada com o comando:


 nslookup -vc ya.ru 127.0.0.1 

A opção -vc força o nslookup a usar uma conexão TCP com um servidor DNS em vez do 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 ) 

Escrevendo um script


Estou escrevendo em Lua 5.3 . Operações binárias com números já estão disponíveis nele. Bem, precisaremos do módulo Lua Socket .


Nome do arquivo: simple-udp-to-tcp-dns-proxy.lua


 local socket = require "socket" --  lua socket 

--[[--


Vamos escrever uma função simples que permita enviar um pacote de despejo para o console. Eu quero ver o que o proxy faz.


--]]--


 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 para TCP e vice-versa


Escrevemos duas funções que funcionarão com dois canais de transmissão de dados.


--]]--


 --    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 as funções executam coroutine.yield () imediatamente após o lançamento. Isso permite que você passe os parâmetros da função para a primeira chamada e, em seguida, coroutine.resume (co) sem parâmetros adicionais.


principal


E agora a função principal que irá preparar e iniciar o loop 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 

--[[--


Lançamos a função principal. Se a conexão for fechada repentinamente, após um segundo a estabeleceremos novamente chamando main.


--]]--


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

verificar


  1. Executar stunnel


  2. Execute nosso script


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

  3. Verifique a operação do script com o comando


     nslookup ya.ru 127.0.0.1 

    Desta vez, sem o '-vc', foi assim que escrevemos e iniciamos o proxy, que envolve solicitações de DNS UDP no 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 ) 

Se estiver tudo bem, você pode especificar nas configurações de conexão como o servidor DNS "127.0.0.1"


conclusão


Agora, nossas consultas DNS estão protegidas por TLS .


PS Não fornecemos informações adicionais ao Google sobre nós


Logo após a conexão, o Windows tenta nos registrar nos servidores DNS do Google através do nosso túnel. Isso está desabilitado nas configurações avançadas de DNS desmarcando.



Ainda há uma solicitação para time.windows.com. Ele não é mais tão pessoal, mas nunca achei como desligá-lo. A sincronização automática de hora está desativada.


ligações


  1. RFC1035: NOMES DE DOMÍNIO - IMPLEMENTAÇÃO E ESPECIFICAÇÃO
  2. DNS sobre TLS
  3. simple-udp-to-tcp-dns-proxy.lua
  4. Redigir manualmente uma consulta DNS

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


All Articles