DNS-over-HTTPS ์„œ๋ฒ„๋ฅผ ๋†’์ž…๋‹ˆ๋‹ค.

์ €์ž๋Š” DNS ์šด์˜์˜ ๋‹ค์–‘ํ•œ ์ธก๋ฉด์„ ์ด๋ฏธ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ฐ˜๋ณตํ•ด์„œ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์กฐํ•ญ ๋ธ”๋กœ๊ทธ์˜ ์ผ๋ถ€๋กœ ๊ฒŒ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— ์ด ํ•ต์‹ฌ ์ธํ„ฐ๋„ท ์„œ๋น„์Šค์˜ ๋ณด์•ˆ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ํ•ญ์ƒ ์ค‘์ ์„ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

DNS-over-HTTPS ์„œ๋ฒ„๋ฅผ ๋†’์ž…๋‹ˆ๋‹ค.

์ตœ๊ทผ๊นŒ์ง€ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ๋ช…ํ™•ํ•˜๊ฒŒ ์ „์†ก๋˜๋Š” DNS ํŠธ๋ž˜ํ”ฝ์˜ ๋ช…๋ฐฑํ•œ ์ทจ์•ฝ์„ฑ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ฝ˜ํ…์ธ , ์ •๋ถ€ ๋ณด์•ˆ ๊ธฐ๊ด€ ๋ฐ ๊ฒ€์—ด์— ๊ด‘๊ณ ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ์ˆ˜์ž…์„ ๋Š˜๋ฆฌ๋ ค๋Š” ๊ณต๊ธ‰์ž ์ธก์˜ ์•…์˜์ ์ธ ํ–‰์œ„์— ๋Œ€ํ•ด ๋‹จ์ˆœํžˆ ๋ฒ”์ฃ„์ž๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ทธ ๊ณผ์ •๋„ ๋ณดํ˜ธ ๊ฐ•ํ™”, DNSSEC/DANE, DNScrypt, DNS-over-TLS ๋ฐ DNS-over-HTTPS์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ ์ด ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ •์ฒด๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„ ์†”๋ฃจ์…˜๊ณผ ๊ทธ ์ค‘ ์ผ๋ถ€๊ฐ€ ๊ฝค ์˜ค๋žซ๋™์•ˆ ์กด์žฌํ•ด์™”๊ณ  ๋„๋ฆฌ ์•Œ๋ ค์ ธ ์žˆ๊ณ  ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์†Œํ”„ํŠธ์›จ์–ด์˜ ์ง€์›์ด ๋งŽ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค.

๋‹คํ–‰ํžˆ๋„ ์ƒํ™ฉ์€ ๋ณ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ธ๊ธฐ ์žˆ๋Š” Firefox ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž๋Š” ์ •ํ•ด์ง„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์› ๋ชจ๋“œ๋ฅผ ํ™œ์„ฑํ™”ํ•  ๊ณ„ํš์— ๋Œ€ํ•ด DNS๋ฅผ ํ†ตํ•œ HTTPS (DoH) ๊ณง. ์ด๋Š” ์œ„์˜ ์œ„ํ˜‘์œผ๋กœ๋ถ€ํ„ฐ WWW ์‚ฌ์šฉ์ž์˜ DNS ํŠธ๋ž˜ํ”ฝ์„ ๋ณดํ˜ธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜์ง€๋งŒ ์ž ์žฌ์ ์œผ๋กœ ์ƒˆ๋กœ์šด ์œ„ํ˜‘์ด ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

1. DNS-over-HTTPS ๋ฌธ์ œ

์–ธ๋œป ๋ณด๋ฉด DNS-over-HTTPS๊ฐ€ ์ธํ„ฐ๋„ท ์†Œํ”„ํŠธ์›จ์–ด์— ๋Œ€๋Ÿ‰ ๋„์ž…๋˜๊ธฐ ์‹œ์ž‘ํ•œ ๊ฒƒ์€ ๊ธ์ •์ ์ธ ๋ฐ˜์‘๋งŒ์„ ๋ถˆ๋Ÿฌ์ผ์œผํ‚ต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•…๋งˆ๋Š” ๋””ํ…Œ์ผ์— ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

DoH์˜ ๊ด‘๋ฒ”์œ„ํ•œ ์‚ฌ์šฉ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” DoH๊ฐ€ ์›น ํŠธ๋ž˜ํ”ฝ์—๋งŒ ์ดˆ์ ์„ ๋งž์ถ”๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ DoH์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” HTTP ํ”„๋กœํ† ์ฝœ๊ณผ ํ˜„์žฌ ๋ฒ„์ „์ธ HTTP/2๋Š” WWW์˜ ๊ธฐ๋ฐ˜์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ธํ„ฐ๋„ท์€ ๋‹จ์ˆœํ•œ ์›น์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋ฉ”์ผ, ๋‹ค์–‘ํ•œ ์ธ์Šคํ„ดํŠธ ๋ฉ”์‹ ์ €, ํŒŒ์ผ ์ „์†ก ์‹œ์Šคํ…œ, ๋ฉ€ํ‹ฐ๋ฏธ๋””์–ด ์ŠคํŠธ๋ฆฌ๋ฐ ๋“ฑ๊ณผ ๊ฐ™์ด HTTP๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธ๊ธฐ ์žˆ๋Š” ์„œ๋น„์Šค๊ฐ€ ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋งŽ์€ DoH๊ฐ€ ๋งŒ๋ณ‘ํ†ต์น˜์•ฝ์œผ๋กœ ์ธ์‹ํ•˜๊ณ  ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ € ๊ธฐ์ˆ  ์ด์™ธ์˜ ์ถ”๊ฐ€์ ์ธ (๊ทธ๋ฆฌ๊ณ  ๋ถˆํ•„์š”ํ•œ) ๋…ธ๋ ฅ ์—†์ด๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ DNS-over-TLS๋Š” ๋ณด์•ˆ ํ‘œ์ค€ TLS ํ”„๋กœํ† ์ฝœ์—์„œ ํ‘œ์ค€ DNS ํŠธ๋ž˜ํ”ฝ์˜ ์บก์Šํ™”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ด ์—ญํ• ์— ํ›จ์”ฌ ๋” ๊ฐ€์น˜ ์žˆ๋Š” ํ›„๋ณด์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ๋ณด๋‹ค ์ž ์žฌ์ ์œผ๋กœ ํ›จ์”ฌ ๋” ์‹ฌ๊ฐํ•œ ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” ๋ธŒ๋ผ์šฐ์ € ์„ค์ •์— ์ง€์ •๋œ ๋‹จ์ผ DoH ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„์ƒ DNS์˜ ๋ณธ์งˆ์ ์ธ ๋ถ„์‚ฐํ™”๋ฅผ ์‹ค์ œ๋กœ ํฌ๊ธฐํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ Mozilla๋Š” Cloudflare์˜ ์„œ๋น„์Šค ์‚ฌ์šฉ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ €๋ช…ํ•œ ์ธํ„ฐ๋„ท ์ธ๋ฌผ, ํŠนํžˆ Google์—์„œ๋„ ์œ ์‚ฌํ•œ ์„œ๋น„์Šค๋ฅผ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ œ์•ˆ๋œ ํ˜•ํƒœ์˜ DNS-over-HTTPS ๊ตฌํ˜„์€ ์ตœ๋Œ€ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ตœ์ข… ์‚ฌ์šฉ์ž์˜ ์˜์กด๋„๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. DNS ์ฟผ๋ฆฌ ๋ถ„์„์„ ํ†ตํ•ด ์ œ๊ณต๋˜๋Š” ์ •๋ณด๋ฅผ ํ†ตํ•ด ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ •ํ™•์„ฑ๊ณผ ๊ด€๋ จ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ๋น„๋ฐ€์ด ์•„๋‹™๋‹ˆ๋‹ค.

์ด์™€ ๊ด€๋ จํ•˜์—ฌ ์ €์ž๋Š” DNS-over-HTTPS๊ฐ€ ์•„๋‹ˆ๋ผ DNSSEC/DANE๊ณผ ํ•จ๊ป˜ DNS-over-TLS์˜ ๋Œ€๋Ÿ‰ ๊ตฌํ˜„์„ ๋ณดํŽธ์ ์ด๊ณ  ์•ˆ์ „ํ•˜๋ฉฐ ์ธํ„ฐ๋„ท ์ˆ˜๋‹จ์˜ ์ถ”๊ฐ€ ์ค‘์•™ ์ง‘์ค‘ํ™”์— ๋„์›€์ด ๋˜์ง€ ์•Š๋Š” ๋Œ€๋Ÿ‰ ๊ตฌํ˜„์˜ ์ง€์ง€์ž์˜€์œผ๋ฉฐ ์ง€๊ธˆ๋„ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. DNS ํŠธ๋ž˜ํ”ฝ์˜ ๋ณด์•ˆ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ถˆํ–‰ํ•˜๊ฒŒ๋„ ๋ถ„๋ช…ํ•œ ์ด์œ ๋กœ DoH ๋Œ€์•ˆ์— ๋Œ€ํ•œ ๋Œ€๊ทœ๋ชจ ์ง€์›์ด ํด๋ผ์ด์–ธํŠธ ์†Œํ”„ํŠธ์›จ์–ด์— ์‹ ์†ํ•˜๊ฒŒ ๋„์ž…๋  ๊ฒƒ์ด๋ผ๊ณ ๋Š” ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์ด๋Š” ์—ฌ์ „ํžˆ ๋ณด์•ˆ ๊ธฐ์ˆ  ์• ํ˜ธ๊ฐ€๋“ค์˜ ์˜์—ญ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด์ œ DoH๊ฐ€ ์ƒ๊ฒผ์œผ๋‹ˆ, ๊ธฐ์—…์˜ ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ๊ธฐ์—…์˜ ์ž ์žฌ์ ์ธ ๊ฐ์‹œ๋ฅผ ํ”ผํ•ด ์šฐ๋ฆฌ ์ž์ฒด DNS-over-HTTPS ์„œ๋ฒ„๋กœ DoH๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–จ๊นŒ์š”?

2. DNS-over-HTTPS ํ”„๋กœํ† ์ฝœ

๊ธฐ์ค€์œผ๋กœ ๋ณด๋ฉด RFC8484 DNS-over-HTTPS ํ”„๋กœํ† ์ฝœ์„ ์„ค๋ช…ํ•˜๋ฉด ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ HTTP/2 ํ”„๋กœํ† ์ฝœ์— ํ‘œ์ค€ DNS ํŒจํ‚ค์ง€๋ฅผ ์บก์Šํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์›น API๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํŠน์ˆ˜ HTTP ํ—ค๋”์™€ ์ „์†ก๋œ DNS ๋ฐ์ดํ„ฐ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•์‹ ๋ณ€ํ™˜์„ ํ†ตํ•ด ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค(์ฐธ์กฐ: RFC1035 ๋ฐ ํ›„์† ๋ฌธ์„œ)๋ฅผ ์ „์†ก ๋ฐ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•„์š”ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

ํ‘œ์ค€์— ๋”ฐ๋ฅด๋ฉด HTTP/2 ๋ฐ ๋ณด์•ˆ TLS ์—ฐ๊ฒฐ๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

ํ‘œ์ค€ GET ๋ฐ POST ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DNS ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๊ฒฝ์šฐ ์š”์ฒญ์€ base64URL ์ธ์ฝ”๋”ฉ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋˜๊ณ , ๋‘ ๋ฒˆ์งธ ๊ฒฝ์šฐ์—๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•์‹์˜ POST ์š”์ฒญ ๋ณธ๋ฌธ์„ ํ†ตํ•ด ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ DNS ์š”์ฒญ ๋ฐ ์‘๋‹ต ์ค‘์— ํŠน๋ณ„ํ•œ MIME ๋ฐ์ดํ„ฐ ์œ ํ˜•์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜/DNS-๋ฉ”์‹œ์ง€.

root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domaint/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

์ œ๋ชฉ๋„ ์ฃผ๋ชฉํ•ด์ฃผ์„ธ์š” ์บ์‹œ ์ œ์–ด: ์›น ์„œ๋ฒ„์˜ ์‘๋‹ต์—์„œ. ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ ์ตœ๋Œ€ ์—ฐ๋ น ๋ฐ˜ํ™˜๋˜๋Š” DNS ๋ ˆ์ฝ”๋“œ์— ๋Œ€ํ•œ TTL ๊ฐ’(๋˜๋Š” ํ•ด๋‹น ๋ ˆ์ฝ”๋“œ ์ง‘ํ•ฉ์ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒฝ์šฐ ์ตœ์†Œ๊ฐ’)์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์œ„ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ DoH ์„œ๋ฒ„์˜ ๊ธฐ๋Šฅ์€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

  • HTTP ์š”์ฒญ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. GET์ธ ๊ฒฝ์šฐ base64URL ์ธ์ฝ”๋”ฉ์—์„œ ํŒจํ‚ท์„ ๋””์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ํŒจํ‚ท์„ DNS ์„œ๋ฒ„๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • DNS ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์‘๋‹ต ๋ฐ›๊ธฐ
  • ์ˆ˜์‹ ๋œ ๋ ˆ์ฝ”๋“œ์—์„œ ์ตœ์†Œ TTL ๊ฐ’์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
  • HTTP๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

3. ์ž์‹ ๋งŒ์˜ DNS-over-HTTPS ์„œ๋ฒ„

์ž์ฒด DNS-over-HTTPS ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅด๋ฉฐ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์€ HTTP/2 ์›น ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. H2O, ์ €์ž๊ฐ€ ์ด๋ฏธ ๊ฐ„๋žตํ•˜๊ฒŒ ์ž‘์„ฑํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค (โ€œ๊ณ ์„ฑ๋Šฅ H2O ์›น ์„œ๋ฒ„").

์ด ์„ ํƒ์€ H2O ์ž์ฒด์— ํ†ตํ•ฉ๋œ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DoH ์„œ๋ฒ„์˜ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์— ์˜ํ•ด ๋’ท๋ฐ›์นจ๋ฉ๋‹ˆ๋‹ค. ๋ฏ€๋ฃจ๋น„. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์™ธ์—๋„ DNS ์„œ๋ฒ„์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตํ™˜ํ•˜๋ ค๋ฉด (mrbgem) ์†Œ์ผ“ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” H2O 2.3.0-beta2์˜ ํ˜„์žฌ ๊ฐœ๋ฐœ ๋ฒ„์ „์— ์ด๋ฏธ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ์˜ FreeBSD ํฌํŠธ์—์„œ. ํ•˜์ง€๋งŒ ์ €์žฅ์†Œ๋ฅผ ๋ณต์ œํ•˜์—ฌ ์ด์ „ ๋ฒ„์ „์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์†Œ์ผ“ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์นดํƒˆ๋กœ๊ทธ์— /๋Ž์Šค ์ปดํŒŒ์ผ ์ „.

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

์›น ์„œ๋ฒ„ ๊ตฌ์„ฑ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค.

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: [email protected]

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

์œ ์ผํ•œ ์˜ˆ์™ธ๋Š” URL ์ฒ˜๋ฆฌ๊ธฐ์ž…๋‹ˆ๋‹ค. /dns-์ฟผ๋ฆฌ mruby๋กœ ์ž‘์„ฑ๋˜๊ณ  ํ•ธ๋“ค๋Ÿฌ ์˜ต์…˜์„ ํ†ตํ•ด ํ˜ธ์ถœ๋˜๋Š” DNS-over-HTTPS ์„œ๋ฒ„๊ฐ€ ์‹ค์ œ๋กœ ์ฑ…์ž„์„ ์ง€๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. mruby.handler ํŒŒ์ผ.

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: [email protected]

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
}

์ด ๊ฒฝ์šฐ ๋กœ์ปฌ ์บ์‹ฑ ์„œ๋ฒ„๋Š” DNS ํŒจํ‚ท ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋งค์—ฌ ์žˆ์ง€ ์•Š์€ ํ‘œ์ค€ FreeBSD ๋ฐฐํฌํŒ์—์„œ. ๋ณด์•ˆ ๊ด€์ ์—์„œ ๋ณด๋ฉด ์ด๊ฒƒ์ด ์ตœ์ ์˜ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ต์ฒด๋ฅผ ๋ฐฉํ•ดํ•˜๋Š” ๊ฒƒ์€ ์—†์Šต๋‹ˆ๋‹ค. ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋‹ค๋ฅธ DNS ์ฃผ์†Œ๋กœ.

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 [email protected]
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          *:*

๋‚จ์€ ๊ฒƒ์€ H2O๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

4. ํ…Œ์ŠคํŠธ

๊ทธ๋Ÿผ ๋‹ค์‹œ ํ…Œ์ŠคํŠธ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ์„ ์‚ดํŽด๋ณด๋ฉฐ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. TCP ๋คํ”„.

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

์ถœ๋ ฅ์€ ์š”์ฒญ์ด ์ฃผ์†Œ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. example.com DNS ์„œ๋ฒ„์—์„œ ์ˆ˜์‹ ๋˜์–ด ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋‚จ์€ ๊ฒƒ์€ Firefox ๋ธŒ๋ผ์šฐ์ €์—์„œ ์„œ๋ฒ„๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ ํŽ˜์ด์ง€์—์„œ ์—ฌ๋Ÿฌ ์„ค์ •์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ •๋ณด : ์„ค์ •.

DNS-over-HTTPS ์„œ๋ฒ„๋ฅผ ๋†’์ž…๋‹ˆ๋‹ค.

์ฒซ์งธ, ์ด๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ DNS ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๋Š” API์˜ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ.trr.uri. ๋˜ํ•œ DNS์— ์•ก์„ธ์Šคํ•˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ € ์ž์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ˆ์ „ํ•œ IP ํ™•์ธ์„ ์œ„ํ•ด ์ด URL์—์„œ ๋„๋ฉ”์ธ IP๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. network.trr.bootstrap์ฃผ์†Œ. ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ž์ฒด ๋„คํŠธ์›Œํฌ.trr.๋ชจ๋“œ DoH ์‚ฌ์šฉ๋„ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ฐ’์„ "3"์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด๋ฆ„ ํ™•์ธ์„ ์œ„ํ•ด DNS-over-HTTPS๋งŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉฐ, ๋ณด๋‹ค ์•ˆ์ •์ ์ด๊ณ  ์•ˆ์ „ํ•œ "2"๋Š” DoH์— ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ถ€์—ฌํ•˜๊ณ  ํ‘œ์ค€ DNS ์กฐํšŒ๋ฅผ ๋Œ€์ฒด ์˜ต์…˜์œผ๋กœ ๋‚จ๊ฒจ๋‘ก๋‹ˆ๋‹ค.

5. ์ด์ต!

๊ธฐ์‚ฌ๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”? ๊ทธ๋ ‡๋‹ค๋ฉด ๋ถ€๋„๋Ÿฌ์›Œํ•˜์ง€ ๋ง๊ณ  ๊ธฐ๋ถ€ํผ(์•„๋ž˜)์„ ํ†ตํ•ด ๋ˆ์œผ๋กœ ํ›„์›ํ•ด ์ฃผ์„ธ์š”.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€