Berbagai aspek pengoperasian DNS telah berulang kali dibahas oleh penulis dalam sejumlah artikel yang diterbitkan sebagai bagian dari blog. Pada saat yang sama, penekanan utama selalu pada peningkatan keamanan layanan utama ini untuk seluruh Internet.

Sampai baru-baru ini, terlepas dari kerentanan lalu lintas DNS yang jelas, yang, sebagian besar, masih ditransmisikan secara jelas, untuk tindakan jahat oleh penyedia yang berusaha meningkatkan pendapatan mereka dengan menanamkan iklan dalam konten, lembaga penegak hukum negara dan penyensoran, serta hanya penjahat, proses meningkatkan perlindungannya , meskipun ada berbagai teknologi, seperti DNSSEC / DANE, DNScrypt, DNS-over-TLS dan DNS-over-HTTPS, tergelincir. Dan jika solusi server, dan beberapa di antaranya telah ada selama beberapa waktu, diketahui dan tersedia secara luas, maka dukungan mereka dari perangkat lunak klien meninggalkan banyak hal yang diinginkan.
Untungnya, situasinya berubah. Secara khusus, pengembang browser Firefox yang populer mengumumkan rencana untuk mengaktifkan mode dukungan default untuk DNS-over-HTTPS (DoH) dalam waktu dekat. Ini harus membantu melindungi lalu lintas DNS pengguna WWW dari ancaman yang disebutkan di atas, tetapi berpotensi menyebabkan yang baru.
1. Masalah DNS-over-HTTPS
Sepintas, permulaan pengenalan besar-besaran DNS-over-HTTPS ke dalam perangkat lunak yang berjalan di Internet hanya menimbulkan reaksi positif. Namun, iblis, seperti yang mereka katakan, ada dalam rinciannya.
Masalah pertama yang membatasi ruang lingkup penggunaan massa DoH adalah fokusnya hanya pada lalu lintas web. Memang, protokol HTTP dan versi HTTP / 2 saat ini, yang menjadi dasar DoH, adalah fondasi WWW. Namun internet bukan hanya web. Ada banyak layanan populer, seperti e-mail, semua jenis messenger, sistem transfer file, streaming multimedia, dll. Yang tidak menggunakan HTTP. Dengan demikian, terlepas dari persepsi banyak DoH sebagai obat mujarab, ternyata tidak dapat diterapkan tanpa upaya tambahan (dan tidak perlu), tidak lain adalah teknologi browser. Omong-omong, DNS-over-TLS terlihat seperti kandidat yang jauh lebih layak untuk peran ini, yang mengimplementasikan enkapsulasi lalu lintas DNS standar ke dalam protokol TLS standar yang aman.
Masalah kedua, yang berpotensi jauh lebih signifikan daripada yang pertama, adalah penolakan aktual dari DNS desentralisasi yang melekat dengan desain demi menggunakan server DoH tunggal yang ditentukan dalam pengaturan browser. Secara khusus, Mozilla menawarkan untuk menggunakan layanan dari Cloudflare. Layanan serupa juga diluncurkan oleh tokoh-tokoh terkemuka Internet lainnya, khususnya Google. Ternyata implementasi DNS-over-HTTPS dalam bentuk yang diusulkan sekarang, hanya meningkatkan ketergantungan pengguna akhir pada layanan terbesar. Bukan rahasia lagi bahwa informasi yang dapat diberikan oleh analisis kueri DNS dapat mengumpulkan lebih banyak data tentangnya, serta meningkatkan akurasi dan relevansinya.
Dalam hal ini, penulis adalah dan tetap menjadi pendukung implementasi massal bukan DNS-over-HTTPS, tetapi DNS-over-TLS bersama-sama dengan DNSSEC / DANE sebagai universal, aman dan tidak berkontribusi pada sentralisasi lebih lanjut dari sarana Internet untuk memastikan keamanan lalu lintas DNS. Sayangnya, tidak ada alasan untuk mengharapkan pengenalan cepat dukungan massa untuk alternatif DoH dalam perangkat lunak klien untuk alasan yang jelas, dan penggemar teknologi aman tetap takdirnya.
Tetapi karena kita sekarang mendapatkan DoH, mengapa tidak menggunakannya setelah pergi dari pengawasan potensial oleh perusahaan melalui server mereka ke server DNS-over-HTTPS Anda sendiri?
2. DNS-over-HTTPS
Jika Anda melihat standar RFC8484 yang menggambarkan protokol DNS-over-HTTPS, Anda dapat melihat bahwa itu, pada kenyataannya, adalah API web yang memungkinkan Anda untuk merangkum paket DNS standar ke dalam protokol HTTP / 2. Ini diimplementasikan melalui tajuk HTTP khusus, serta mengonversi format biner dari data DNS yang ditransmisikan (lihat RFC1035 dan dokumen-dokumen berikutnya) ke dalam bentuk yang memungkinkan Anda mengirim dan menerimanya, serta bekerja dengan metadata yang diperlukan.
Secara standar, hanya HTTP / 2 dan koneksi TLS aman yang didukung.
Permintaan DNS dapat dikirim menggunakan metode GET dan POST standar. Dalam kasus pertama, permintaan diubah menjadi string yang disandikan base64URL, dan yang kedua, melalui tubuh permintaan POST dalam bentuk biner. Dalam hal ini, saat menanyakan dan merespons DNS, aplikasi tipe data MIME khusus / dns-message digunakan .
root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domain/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' -v * Trying 2001:100:200:300::400:443... * TCP_NODELAY set * Connected to eprove.net (2001:100:200:300::400) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /usr/local/share/certs/ca-root-nss.crt CApath: none * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=my.domain * start date: Jul 22 00:07:13 2019 GMT * expire date: Oct 20 00:07:13 2019 GMT * subjectAltName: host "my.domain" matched cert's "my.domain" * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x801441000) > GET /dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/2 > Host: eprove.net > User-Agent: curl/7.65.3 > accept: application/dns-message > * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * Connection state changed (MAX_CONCURRENT_STREAMS == 100)! < HTTP/2 200 < server: h2o/2.3.0-beta2 < content-type: application/dns-message < cache-control: max-age=86274 < date: Thu, 12 Sep 2019 13:07:25 GMT < strict-transport-security: max-age=15768000; includeSubDomains; preload < content-length: 45 < Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file. * Failed writing body (0 != 45) * stopped the pause stream! * Connection #0 to host eprove.net left intact
Perhatikan juga kontrol-cache: header dalam respons dari server web. Parameter usia maksimum berisi nilai TTL untuk catatan DNS yang dikembalikan (atau nilai minimum jika set dikembalikan).
Berdasarkan hal tersebut di atas, fungsi server DoH terdiri dari beberapa tahap.
- Terima permintaan HTTP. Jika GET maka decode paket dari encoding base64URL.
- Kirim paket ini ke server DNS.
- Dapatkan tanggapan dari server DNS
- Temukan nilai TTL minimum dalam catatan yang diterima.
- Kembalikan respons HTTP ke klien.
3. Server DNS-over-HTTPS sendiri
Cara termudah, tercepat dan paling efektif untuk memulai server DNS-over-HTTPS Anda sendiri adalah dengan menggunakan server web HTTP / 2 H2O , yang sudah ditulis oleh penulis secara singkat (lihat " server web H2O berkinerja tinggi ").
Yang mendukung pilihan ini adalah kenyataan bahwa semua kode server DoH Anda sendiri dapat sepenuhnya diimplementasikan menggunakan mruby interpreter yang diintegrasikan ke dalam H2O itu sendiri . Selain pustaka standar, untuk berkomunikasi dengan server DNS, Anda memerlukan pustaka Socket (mrbgem), yang, untungnya, sudah termasuk dalam versi pengembangan saat ini dari H2O 2.3.0-beta2 yang ada di port FreeBSD. Namun, tidak sulit untuk menambahkannya ke versi sebelumnya dengan mengkloning repositori Socket library ke direktori / deps sebelum kompilasi.
root@beta:~ # uname -v FreeBSD 12.0-RELEASE-p10 GENERIC root@beta:~ # cd /usr/ports/www/h2o root@beta:/usr/ports/www/h2o # make extract ===> License MIT BSD2CLAUSE accepted by the user ===> h2o-2.2.6 depends on file: /usr/local/sbin/pkg - found ===> Fetching all distfiles required by h2o-2.2.6 for building ===> Extracting for h2o-2.2.6. => SHA256 Checksum OK for h2o-h2o-v2.2.6_GH0.tar.gz. ===> h2o-2.2.6 depends on file: /usr/local/bin/ruby26 - found root@beta:/usr/ports/www/h2o # cd work/h2o-2.2.6/deps/ root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # git clone https://github.com/iij/mruby-socket.git «mruby-socket»… remote: Enumerating objects: 385, done. remote: Total 385 (delta 0), reused 0 (delta 0), pack-reused 385 : 100% (385/385), 98.02 KiB | 647.00 KiB/s, . : 100% (208/208), . root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # ll total 181 drwxr-xr-x 9 root wheel 18 12 . 16:09 brotli/ drwxr-xr-x 2 root wheel 4 12 . 16:09 cloexec/ drwxr-xr-x 2 root wheel 5 12 . 16:09 golombset/ drwxr-xr-x 4 root wheel 35 12 . 16:09 klib/ drwxr-xr-x 2 root wheel 5 12 . 16:09 libgkc/ drwxr-xr-x 4 root wheel 26 12 . 16:09 libyrmcds/ drwxr-xr-x 13 root wheel 32 12 . 16:09 mruby/ drwxr-xr-x 5 root wheel 11 12 . 16:09 mruby-digest/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-dir/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-env/ drwxr-xr-x 4 root wheel 9 12 . 16:09 mruby-errno/ drwxr-xr-x 5 root wheel 14 12 . 16:09 mruby-file-stat/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-iijson/ drwxr-xr-x 5 root wheel 11 12 . 16:09 mruby-input-stream/ drwxr-xr-x 6 root wheel 11 12 . 16:09 mruby-io/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-onig-regexp/ drwxr-xr-x 4 root wheel 10 12 . 16:09 mruby-pack/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-require/ drwxr-xr-x 6 root wheel 10 12 . 16:10 mruby-socket/ drwxr-xr-x 2 root wheel 9 12 . 16:09 neverbleed/ drwxr-xr-x 2 root wheel 13 12 . 16:09 picohttpparser/ drwxr-xr-x 2 root wheel 4 12 . 16:09 picotest/ drwxr-xr-x 9 root wheel 16 12 . 16:09 picotls/ drwxr-xr-x 4 root wheel 8 12 . 16:09 ssl-conservatory/ drwxr-xr-x 8 root wheel 18 12 . 16:09 yaml/ drwxr-xr-x 2 root wheel 8 12 . 16:09 yoml/ root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # cd ../../.. root@beta:/usr/ports/www/h2o # make install clean ...
Konfigurasi server web umumnya standar.
root@beta:/usr/ports/www/h2o # cd /usr/local/etc/h2o/ root@beta:/usr/local/etc/h2o # cat h2o.conf # this sample config gives you a feel for how h2o can be used # and a high-security configuration for TLS and HTTP headers # see https://h2o.examp1e.net/ for detailed documentation # and h2o --help for command-line options and settings # v.20180207 (c)2018 by Max Kostikov http://kostikov.co e-mail: max@kostikov.co user: www pid-file: /var/run/h2o.pid access-log: path: /var/log/h2o/h2o-access.log format: "%h %v %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\"" error-log: /var/log/h2o/h2o-error.log expires: off compress: on file.dirlisting: off file.send-compressed: on file.index: [ 'index.html', 'index.php' ] listen: port: 80 listen: port: 443 ssl: cipher-suite: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 cipher-preference: server dh-file: /etc/ssl/dhparams.pem certificate-file: /usr/local/etc/letsencrypt/live/eprove.net/fullchain.pem key-file: /usr/local/etc/letsencrypt/live/my.domain/privkey.pem hosts: "*.my.domain": paths: &go_tls "/": redirect: status: 301 url: https://my.domain/ "my.domain:80": paths: *go_tls "my.domain:443": header.add: "Strict-Transport-Security: max-age=15768000; includeSubDomains; preload" paths: "/dns-query": mruby.handler-file: /usr/local/etc/h2o/h2odoh.rb
Satu-satunya pengecualian adalah penangan URL / dns-query , yang bertanggung jawab, pada kenyataannya, untuk server DNS-over-HTTPS h2odoh kami, ditulis dalam mruby dan dipanggil melalui opsi penangan file mruby.handler-file .
root@beta:/usr/local/etc/h2o # cat h2odoh.rb # H2O HTTP/2 web server as DNS-over-HTTP service # v.20190908 (c)2018-2019 Max Kostikov https://kostikov.co e-mail: max@kostikov.co proc {|env| if env['HTTP_ACCEPT'] == "application/dns-message" case env['REQUEST_METHOD'] when "GET" req = env['QUERY_STRING'].gsub(/^dns=/,'') # base64URL decode req = req.tr("-_", "+/") if !req.end_with?("=") && req.length % 4 != 0 req = req.ljust((req.length + 3) & ~3, "=") end req = req.unpack1("m") when "POST" req = env['rack.input'].read else req = "" end if req.empty? [400, { 'content-type' => 'text/plain' }, [ "Bad Request" ]] else # --- ask DNS server sock = UDPSocket.new sock.connect("localhost", 53) sock.send(req, 0) str = sock.recv(4096) sock.close # --- find lowest TTL in response nans = str[6, 2].unpack1('n') # number of answers if nans > 0 # no DNS failure shift = 12 ttl = 0 while nans > 0 # process domain name compression if str[shift].unpack1("C") < 192 shift = str.index("\x00", shift) + 5 if ttl == 0 # skip question section next end end shift += 6 curttl = str[shift, 4].unpack1('N') shift += str[shift + 4, 2].unpack1('n') + 6 # responce data size if ttl == 0 or ttl > curttl ttl = curttl end nans -= 1 end cc = 'max-age=' + ttl.to_s else cc = 'no-cache' end [200, { 'content-type' => 'application/dns-message', 'content-length' => str.size, 'cache-control' => cc }, [ str ] ] end else [415, { 'content-type' => 'text/plain' }, [ "Unsupported Media Type" ]] end }
Harap dicatat bahwa server caching lokal bertanggung jawab untuk memproses paket DNS, dalam hal ini Tidak Terikat dari distribusi FreeBSD standar. Dari sudut pandang keamanan, ini adalah solusi terbaik. Namun, tidak ada yang mencegah mengganti localhost dengan alamat DNS lain yang ingin Anda gunakan.
root@beta:/usr/local/etc/h2o # local-unbound verison usage: local-unbound [options] start unbound daemon DNS resolver. -h this help -c file config file to read instead of /var/unbound/unbound.conf file format is described in unbound.conf(5). -d do not fork into the background. -p do not create a pidfile. -v verbose (more times to increase verbosity) Version 1.8.1 linked libs: mini-event internal (it uses select), OpenSSL 1.1.1a-freebsd 20 Nov 2018 linked modules: dns64 respip validator iterator BSD licensed, see LICENSE in source package for details. Report bugs to unbound-bugs@nlnetlabs.nl root@eprove:/usr/local/etc/h2o # sockstat -46 | grep unbound unbound local-unbo 69749 3 udp6 ::1:53 *:* unbound local-unbo 69749 4 tcp6 ::1:53 *:* unbound local-unbo 69749 5 udp4 127.0.0.1:53 *:* unbound local-unbo 69749 6 tcp4 127.0.0.1:53 *:*
Tetap memulai ulang H2O dan melihat apa yang terjadi.
root@beta:/usr/local/etc/h2o # service h2o restart Stopping h2o. Waiting for PIDS: 69871. Starting h2o. start_server (pid:70532) starting now...
4. Pengujian
Jadi, mari kita periksa hasilnya dengan mengirimkan permintaan tes lagi dan melihat lalu lintas jaringan menggunakan utilitas tcpdump .
root@beta/usr/local/etc/h2o # curl -H 'accept: application/dns-message' 'https://my.domain/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file. ... root@beta:~ # tcpdump -n -i lo0 udp port 53 -xx -XX -vv tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes 16:32:40.420831 IP (tos 0x0, ttl 64, id 37575, offset 0, flags [none], proto UDP (17), length 57, bad cksum 0 (->e9ea)!) 127.0.0.1.21070 > 127.0.0.1.53: [bad udp cksum 0xfe38 -> 0x33e3!] 43981+ A? example.com. (29) 0x0000: 0200 0000 4500 0039 92c7 0000 4011 0000 ....E..9....@... 0x0010: 7f00 0001 7f00 0001 524e 0035 0025 fe38 ........RN.5.%.8 0x0020: abcd 0100 0001 0000 0000 0000 0765 7861 .............exa 0x0030: 6d70 6c65 0363 6f6d 0000 0100 01 mple.com..... 16:32:40.796507 IP (tos 0x0, ttl 64, id 37590, offset 0, flags [none], proto UDP (17), length 73, bad cksum 0 (->e9cb)!) 127.0.0.1.53 > 127.0.0.1.21070: [bad udp cksum 0xfe48 -> 0x43fa!] 43981 q: A? example.com. 1/0/0 example.com. A 93.184.216.34 (45) 0x0000: 0200 0000 4500 0049 92d6 0000 4011 0000 ....E..I....@... 0x0010: 7f00 0001 7f00 0001 0035 524e 0035 fe48 .........5RN.5.H 0x0020: abcd 8180 0001 0001 0000 0000 0765 7861 .............exa 0x0030: 6d70 6c65 0363 6f6d 0000 0100 01c0 0c00 mple.com........ 0x0040: 0100 0100 0151 8000 045d b8d8 22 .....Q...].." ^C 2 packets captured 23 packets received by filter 0 packets dropped by kernel
Output menunjukkan bagaimana permintaan resolusi alamat example.com diterima dan berhasil diproses oleh server DNS.
Sekarang tinggal mengaktifkan server kami di browser Firefox. Untuk melakukan ini, Anda perlu mengubah beberapa tentang: pengaturan konfigurasi pada halaman konfigurasi.

Pertama, ini adalah alamat API kami di mana browser akan menanyakan informasi DNS di network.trr.uri . Anda juga disarankan untuk menentukan IP domain dari URL ini untuk resolusi aman dalam IP menggunakan browser itu sendiri tanpa mengakses DNS di network.trr.bootstrapAddress . Dan, akhirnya, parameter network.trr.mode itu sendiri, yang mencakup penggunaan DoH. Menetapkan nilai ke "3" akan memaksa browser untuk menggunakan DNS-over-HTTPS secara eksklusif untuk menyelesaikan nama, dan "2" yang lebih andal akan memberikan prioritas DoH, menjadikan akses DNS standar sebagai mundur.
5. KEUNTUNGAN!
Apakah artikelnya bermanfaat? Maka tolong jangan malu dan mendukung uang melalui formulir sumbangan (di bawah).