DNS over TLS - Enkripsi permintaan DNS kami menggunakan Stunnel dan Lua


sumber gambar


DNS (Bahasa Inggris. Sistem Nama Domain) adalah sistem komputer terdistribusi untuk memperoleh informasi tentang domain.

TLS ( Transport Layer Security Protocol) - menyediakan transfer data yang aman antara node Internet.

Setelah beritanya, "Google Public DNS diam-diam menyalakan dukungan DNS over TLS," saya memutuskan untuk mencobanya. Saya memiliki Stunnel yang akan membuat terowongan TCP terenkripsi. Tetapi program biasanya berkomunikasi dengan DNS menggunakan protokol UDP . Oleh karena itu, kita memerlukan proxy yang akan meneruskan paket UDP ke dan dari aliran TCP. Kami akan menulisnya di Lua .


Seluruh perbedaan antara paket DNS dan UDP DNS:


4.2.2. Penggunaan TCP
Pesan yang dikirim melalui koneksi TCP menggunakan port server 53 (desimal). Pesan diawali dengan bidang panjang dua byte yang memberikan panjang pesan, tidak termasuk bidang panjang dua byte. Bidang panjang ini memungkinkan pemrosesan tingkat rendah untuk mengumpulkan pesan lengkap sebelum mulai menguraikannya.

RFC1035: NAMA DOMAIN - IMPLEMENTASI DAN SPESIFIKASI


Yaitu, kita lakukan di sana:


  1. kami mengambil paket dari UDP
  2. tambahkan padanya di awal beberapa byte di mana ukuran paket ini ditunjukkan
  3. kirim ke saluran TCP

Dan di arah yang berlawanan:


  1. kami membaca beberapa byte dari TCP, kami mendapatkan ukuran paket
  2. kami membaca paket dari TCP
  3. kirimkan ke penerima melalui UDP

Sesuaikan Stunnel


  1. Unduh sertifikat root Root-R2.crt di direktori dengan konfigurasi Stunnel
  2. Ubah sertifikat menjadi PEM
    openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text 
  3. Kami menulis di 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 


Yaitu, Stunnel:


  1. akan menerima TCP yang tidak terenkripsi pada 127.0.0.1:53
  2. akan membuka terowongan TLS terenkripsi ke alamat 8.8.8.8:853 (Google DNS)
  3. akan mentransfer data bolak-balik

Luncurkan Stunnel


Pengoperasian terowongan dapat diperiksa dengan perintah:


 nslookup -vc ya.ru 127.0.0.1 

Opsi -vc memaksa nslookup untuk menggunakan koneksi TCP ke server DNS, bukan UDP.


Hasil:


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

Menulis naskah


Saya menulis di Lua 5.3 . Operasi biner dengan angka sudah tersedia di dalamnya. Nah, kita akan membutuhkan modul Lua Socket .


Nama File: simple-udp-to-tcp-dns-proxy.lua


 local socket = require "socket" --  lua socket 

--[[--


Mari kita menulis fungsi sederhana yang akan memungkinkan Anda mengirim paket dump ke konsol. Saya ingin melihat apa yang dilakukan 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 ke TCP dan kembali


Kami menulis dua fungsi yang akan beroperasi dengan dua saluran transmisi data.


--]]--


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

--[[--


Kedua fungsi menjalankan coroutine.yield () segera setelah diluncurkan. Ini memungkinkan Anda untuk melewatkan parameter fungsi ke panggilan pertama dan kemudian melakukan coroutine.resume (co) tanpa parameter tambahan.


utama


Dan sekarang fungsi utama yang akan mempersiapkan dan memulai loop utama.


--]]--


 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 

--[[--


Kami meluncurkan fungsi utama. Jika koneksi tiba-tiba ditutup, setelah sedetik kami akan membangun kembali dengan memanggil utama.


--]]--


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

periksa


  1. Jalankan stunnel


  2. Jalankan skrip kami


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

  3. Periksa operasi skrip dengan perintah


     nslookup ya.ru 127.0.0.1 

    Kali ini, tanpa '-vc', ini adalah bagaimana kami menulis dan memulai proksi, yang membungkus permintaan DNS UDP ke dalam terowongan TCP.



Hasil:


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

Jika semuanya baik-baik saja, Anda dapat menentukan dalam pengaturan koneksi sebagai server DNS "127.0.0.1"


kesimpulan


Sekarang permintaan DNS kami dilindungi oleh TLS .


PS Kami tidak memberikan informasi tambahan kepada google tentang kami


Tepat setelah koneksi, Windows mencoba mendaftarkan kami di server DNS Google melalui terowongan kami. Ini dinonaktifkan di pengaturan DNS tingkat lanjut dengan menghapus centang.



Masih ada permintaan untuk time.windows.com. Dia tidak begitu pribadi lagi, tetapi saya tidak pernah menemukan cara mematikannya. Sinkronisasi waktu otomatis dinonaktifkan.


tautan


  1. RFC1035: NAMA DOMAIN - IMPLEMENTASI DAN SPESIFIKASI
  2. DNS melalui TLS
  3. simple-udp-to-tcp-dns-proxy.lua
  4. Menulis permintaan DNS secara manual

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


All Articles