Loo oma DNS-over-HTTPS serveri

DNSi erinevate aspektide kasutamise üle on autor rääkinud korduvalt mitmes artikleid blogis avaldatud. Sellega kaasnevalt on keskendutud alati selle võtmetähtsusega internetiteenuse turvalisuse tõstmisele.

Loo oma DNS-over-HTTPS serveri

Kuni viimase ajani, vaatamata DNSi liikluse haavatavuse ilmsele iseloomule, mis enamikul juhtudel edastatakse endiselt avatud kujul, on üritustes, mis on suunatud teenusepakkujate kasumite suurendamisele reklaamiga sisu integreerimise kaudu, riiklike jõustruktuuride ja tsensuuri, samuti lihtsalt kurjategijate poolt, kaitse tõhustamise protsess, vaatamata erinevate tehnoloogiate olemasolule, nagu DNSSEC/DANE, DNScrypt, DNS-over-TLS ja DNS-over-HTTPS, on takerdunud. Ja kuigi serverilahendused, millest mõned on juba pikka aega olemas, on laialdaselt tuntud ja saadaval, jätab nende toetamine klienditarkvaras palju soovida.

Õnneks on olukord muutumas. Eriti on populaarse brauseri Firefox arendajad teatasid teatanud kavatsusest lisada vaikimisi toetuse režiimi DNS-over-HTTPS (DoH) lähitulevikus. See peaks aitama kaitsta WWW kasutaja DNSi liiklust eespool mainitud ohtude eest, kuid võib potentsiaalselt tekitada uusi.

1. DNS-over-HTTPS probleemid

Esmapilgul kutsub internetis töötavate programmide laienev DNS-over-HTTPS kasutuselevõtt esile ainult positiivset vastukaja. Kuid nagu öeldakse, peitub kurat detailides.

Esimene probleem, mis piirab DoH-i ulatuslikku rakendamist, on selle fookus ainult veebiliiklusele. Tõepoolest, HTTP-protokoll ja selle praegune versioon HTTP/2, millel DoH põhineb, on WWW aluseks. Kuid Internet ei ole ainult veeb. On palju populaarseid teenuseid, nagu e-post, erinevad sõnumirakendused, failide edastamise süsteemid, meedia voogedastus ja nii edasi, mis ei kasuta HTTP-d. Seega, hoolimata paljude nägemisest DoH-i kui imevahendit, osutub see rakendamatuks ilma täiendavate (ja niikuinii mitte vajalikud) pingutusteta, mitte millegi muu jaoks kui brauseritehnoloogiate jaoks. Juhul, kui DNS-over-TLS, mis pakub standardse DNS-i liikluse kapseldamist kaitstud standardprotokolli TLS-i, on sellel rollil palju sobivam kandidaat.

Teine probleem, mis on potentsiaalselt palju olulisem kui esimene, on tegelik loobumine DNS-i sisemisest detsentraliseeritusest, et kasutada brauseri seadetes määratud ühte DoH-serverit. Eelkõige pakub Mozilla kasutamiseks Cloudflare'i teenust. Sellise teenuse on käivitanud ka teised tuntud Interneti tegelased, sealhulgas Google. Tulemuseks on, et DNS-over-HTTPS rakendamine sellisel kujul, nagu seda praegu pakutakse, suurendab lõppkasutajate sõltuvust suurimatest teenustest. Pole saladus, et DNS-päringute analüüsi kaudu saadud teave suudab koguda veelgi rohkem andmeid isiku kohta ning suurendada nende täpsust ja asjakohasust.

Seetõttu on autor olnud ja jääb massilise rakendamise toetajaks mitte DNS-over-HTTPS-i, vaid DNS-over-TLS-i koos DNSSEC/DANE-ga kui universaalset, ohutut ja mitte edasist Interneti keskset struktuuri soosivat vahendit DNS-i liikluse turvalisuse tagamiseks. Kahjuks ei saa oodata kiiret massilist toetust DoH-i alternatiivide rakendamiseks kliendi tarkvaras, arvestades arusaadavaid põhjuseid, ja selle saatuseks jäävad seni entusiaste turvatehnoloogiate seas.

Kuid kuna me nüüd saame DoH-i, siis miks mitte seda kasutada, liikudes samal ajal potentsiaalse jälgimise vältimiseks korporatsioonide serverite käest omaenda DNS-over-HTTPS serverisse?

2. Protokoll DNS-over-HTTPS

Kui vaadata standardit RFC8484 Kirjeldades DNS-over-HTTPS protokolli, saab näha, et see on sisuliselt veeb API, mis võimaldab kapseldada standardset DNS-paketti HTTP/2 protokolli. See saavutatakse spetsiaalsete HTTP-peade ja edastatavate DNS-andmete binaarformaadi konversiooni abil (vt. RFC1035 ja järgnevad dokumendid) kujule, mis võimaldab neid edastada ja vastu võtta ning töötada vajalike metaandmetega.

Standardi kohaselt toetatakse ainult HTTP/2 ja turvalist TLS-ühendust.

DNS-päringu saatmine võib toimuda standardsete GET ja POST meetoditega. Esimesel juhul muudetakse päring base64URL-koodeeritud stringiks, teisel juhul — POST-päringu kehas binaarses vormis. Samuti, päringu ja vastuse puhul kasutatakse DNS-i spetsiaalset MIME-type andmeid. application/dns-message.

root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domaint/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' -v
*   Ühendamine 2001:100:200:300::400:443...
* TCP_NODELAY seadistatud
* Ühendatud eprove.net (2001:100:200:300::400) port 443 (#0)
* ALPN, pakub h2
* ALPN, pakub http/1.1
* sertifikaadi kontrollimise asukoht on edukalt seadistatud:
*   CAfile: /usr/local/share/certs/ca-root-nss.crt
  CApath: puudu
* TLSv1.3 (VÄLJAS), TLS käteadagamine, Klient teretulek (1):
* TLSv1.3 (SISSE), TLS käteadagamine, Serveri teretulek (2):
* TLSv1.3 (SISSE), TLS käteadagamine, Krüpteeritud laiendused (8):
* TLSv1.3 (SISSE), TLS käteadagamine, Sertifikaadi (11):
* TLSv1.3 (SISSE), TLS käteadagamine, Sertifikaadi kontrollimine (15):
* TLSv1.3 (SISSE), TLS käteadagamine, Lõpetatud (20):
* TLSv1.3 (VÄLJAS), TLS vaheta krüptimist, Krüptimise spetsifikatsioon (1):
* TLSv1.3 (VÄLJAS), TLS käteadagamine, Lõpetatud (20):
* SSL-ühendus, kasutades TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server nõustus kasutama h2
* Serveri sertifikaat:
*  teema: CN=my.domain
*  alguskuupäev: Juuli 22 00:07:13 2019 GMT
*  aegumiskuupäev: Oktoober 20 00:07:13 2019 GMT
*  subjectAltName: host "my.domain" vastab sertifikaadi "my.domain"
*  väljaandja: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL sertifikaadi kontrollimine õnnestus.
* Kasutades HTTP2, server toetab mitmikulisi kasutusi
* Ühenduse olek muutus (HTTP/2 kinnitatud)
* Kopeerides HTTP/2 andmeid voolupuhvrisse pärast uuendamist: len=0
* Kasutades Voogu ID: 1 (lihtne käitleja 0x801441000)
> GET /dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/2
> Host: eprove.net
> User-Agent: curl/7.65.3
> accept: application/dns-message
>
* TLSv1.3 (SISSE), TLS käteadagamine, Uus sessiooni pilet (4):
* Ühenduse olek muutus (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< server: h2o/2.3.0-beta2
< content-type: application/dns-message
< cache-control: max-age=86274
< date: Neljapäev, 12 Sept 2019 13:07:25 GMT
< strict-transport-security: max-age=15768000; includeSubDomains; preload
< content-length: 45
<
Hoiatus: Binaarne väljund võib häirida teie terminali. Kasutage "--output -" et öelda
Hoiatus: curl, et väljund seda teie terminalile anyway, või kaaluge "--output
Hoiatus: " salvestada faili.
* Keha kirjutamine ebaõnnestus (0 != 45)
* peatatud paus voog!
* Ühendus #0 hostiga eprove.net jääb puutumatuks.

Pöörake tähelepanu ka päisele cache-control: veebiserveri vastuses. Parameetris max-age on TTL väärtus tagastatud DNS-kirje jaoks (või minimaalne väärtus, kui tagastatakse nende kogum).

Nagu eespool mainitud, koosneb DoH serveri töö mitmest etapist.

  • Saada HTTP päring. Kui see on GET, siis dekodeeri pakett base64URL kodeeringust.
  • Saada see pakett DNS serverile.
  • Saada vastus DNS serverilt.
  • Leida saadud kirjetes minimaalne TTL väärtus.
  • Tagastada klientidele vastus HTTP kaudu.

3. Oma DNS-over-HTTPS server

Kõige lihtsam, kiirem ja tõhusam viis oma DNS-over-HTTPS serveri käivitamiseks tundub olevat HTTP/2 veebiserveri kasutamine. H2O, millest autor on juba lühidalt rääkinud (vt 'Kõrge jõudlusega veebiserver H2O«).

See valik on soodne, kuna kogu oma DoH serveri kood saab täielikult teostada H2O-sse integreeritud tõlgiga. mruby. Lisaks standardsetele raamatukogudele, on andmevahetuseks DNS serveriga vajalik raamatukogu (mrbgem) Socket, mis on õnneks juba kaasatud H2O 2.3.0-beta2 praeguses arenduses. olemas FreeBSD portides. Siiski pole seda raske lisada ka igas varasemas versioonis, kloonides reposi. Socket raamatukogu kausta /deps enne kompileerimist.

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 aug  16:09 brotli/
drwxr-xr-x   2 root  wheel   4  12 aug  16:09 cloexec/
drwxr-xr-x   2 root  wheel   5  12 aug  16:09 golombset/
drwxr-xr-x   4 root  wheel  35  12 aug  16:09 klib/
drwxr-xr-x   2 root  wheel   5  12 aug  16:09 libgkc/
drwxr-xr-x   4 root  wheel  26  12 aug  16:09 libyrmcds/
drwxr-xr-x  13 root  wheel  32  12 aug  16:09 mruby/
drwxr-xr-x   5 root  wheel  11  12 aug  16:09 mruby-digest/
drwxr-xr-x   5 root  wheel  10  12 aug  16:09 mruby-dir/
drwxr-xr-x   5 root  wheel  10  12 aug  16:09 mruby-env/
drwxr-xr-x   4 root  wheel   9  12 aug  16:09 mruby-errno/
drwxr-xr-x   5 root  wheel  14  12 aug  16:09 mruby-file-stat/
drwxr-xr-x   5 root  wheel  10  12 aug  16:09 mruby-iijson/
drwxr-xr-x   5 root  wheel  11  12 aug  16:09 mruby-input-stream/
drwxr-xr-x   6 root  wheel  11  12 aug  16:09 mruby-io/
drwxr-xr-x   5 root  wheel  10  12 aug  16:09 mruby-onig-regexp/
drwxr-xr-x   4 root  wheel  10  12 aug  16:09 mruby-pack/
drwxr-xr-x   5 root  wheel  10  12 aug  16:09 mruby-require/
drwxr-xr-x   6 root  wheel  10  12 sep  16:10 mruby-socket/
drwxr-xr-x   2 root  wheel   9  12 aug  16:09 neverbleed/
drwxr-xr-x   2 root  wheel  13  12 aug  16:09 picohttpparser/
drwxr-xr-x   2 root  wheel   4  12 aug  16:09 picotest/
drwxr-xr-x   9 root  wheel  16  12 aug  16:09 picotls/
drwxr-xr-x   4 root  wheel   8  12 aug  16:09 ssl-conservatory/
drwxr-xr-x   8 root  wheel  18  12 aug  16:09 yaml/
drwxr-xr-x   2 root  wheel   8  12 aug  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
...

Veebiserveri konfiguratsioon on üheselt öeldes standardne.

root@beta: /usr/ports/www/h2o # cd /usr/local/etc/h2o/
root@beta: /usr/local/etc/h2o # cat h2o.conf
# see this configuration as a sample for how H2O can be utilized
# providing a high-security setup for TLS and HTTP headers
# for detailed documentation visit https://h2o.examp1e.net/
# and use 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-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
        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

Ainus on URL töötlemise käitleja /dns-query mille eest vastutab meie DNS-over-HTTPS server, mis on kirjutatud mruby keeles ja mida kutsutakse käsu käitleja kaudu mruby.handler-file.

root@beta:/usr/local/etc/h2o # cat h2odoh.rb
# H2O HTTP/2 veebiserver DNS-over-HTTP teenusena
# 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 dekodeerimine
                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' }, [ "Vale päring" ]]
        else
            # --- küsi DNS serverilt
            sock = UDPSocket.new
            sock.connect("localhost", 53)
            sock.send(req, 0)
            str = sock.recv(4096)
            sock.close
            # --- leia madalaim TTL vastuses
            nans = str[6, 2].unpack1('n') # vastuste arv
            if nans > 0 # pole DNS viga
                shift = 12
                ttl = 0
                while nans > 0
                    # töötle domeeninime kokkusurumine
                    if str[shift].unpack1("C")  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' }, [ "Toetamata meedia tüüp" ]]
    end
}

Märkus, et DNS pakettide töötlemise eest vastutab kohalik vahemälu server, antud juhul Unbound FreeBSD tavapakettidest. Turvalisuse seisukohalt on see optimaalne lahendus. Siiski ei takista miski vahetada localhost teise DNS aadressi vastu, mida plaanite kasutada.

root@beta:/usr/local/etc/h2o # local-unbound versioon
kasutus:  local-unbound [valikud]
        alustab unbound daemon DNS lahendajat.
-h      see abi
-c fail konfiguratsioonifail, mida lugeda selle asemel /var/unbound/unbound.conf
        faili formaat on kirjeldatud unbound.conf(5).
-d      ärge taanduge taustale.
-p      ärge looge pidfaili.
-v      üksikasjalik (rohkem kordi, et suurendada üksikasjalikkust)
Versioon 1.8.1
Lingitud teegid: mini-event sisemine (kasutab select), OpenSSL 1.1.1a-freebsd 20 Nov 2018
Lingitud moodulid: dns64 respip validator iterator
BSD litsentsiga, vt LICENSE allika paketi üksikasjade jaoks.
Teata vigadest 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          *:*

Peab H2O uuesti käivitama ja vaatama, mis sellest sai.

root@beta: /usr/local/etc/h2o # service h2o restart
Stopping h2o.
Waiting for PIDS: 69871.
Starting h2o.
start_server (pid:70532) starting now...

4. Testimine

Nüüd kontrollime tulemusi, saates uuesti proovipäringu ja jälgides võrgu liiklust tööriista abil 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: " 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

Väljastus näitab, kuidas päring aadressi lahendamiseks example.com oli vastu võetud ja edukalt töödeldud DNS-serveri poolt.

Nüüd peab meie server Firefoxi brauseris aktiveerima. Selleks tuleb konfiguratsioonilehel teha mõned seadistused about:config.

Loo oma DNS-over-HTTPS serveri

Esiteks, see on meie API aadress, mille kaudu brauser küsib DNS-i teavet network.trr.uri. Soovitatav on samuti määrata selle URL-i domeeni IP, et tagada IP-lahendamine otse brauseri kaudu ilma DNS-i poole pöördumata network.trr.bootstrapAddress. Ja lõpuks, tegelikult see parameeter network.trr.mode seal, mis lubab DoH-i kasutamist. Kui seadistate väärtuse "3", sunnib see brauserit kasutama ainult DNS-over-HTTPS nime lahendamiseks, samas kui usaldusväärsem ja turvalisem "2" eelistab DoH-i, jättes standardse DNS-i pöördumise varuvariandina.

5. KASUM!

Kas artikkel oli kasulik? Siis palun ärge piinake end ja toetage rahaga annetusvormi kaudu (allpool).

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster