Route 53 ResolverでDNS over HTTPS(DoH)を使って名前解決をしてみました。

2024.03.11

初めに

2023/12頃ですがRoute 53 Resolverで名前解決を行う際にアウトバウンド・インバウンドエンドポイント経由での名前解決の際にDNS Queries over HTTPS(DoH)が利用できるようになりました。

名前解決を行うにあたり特に内部ネットワークでは内部のみで利用しているようなドメインについては第三者が盗聴を行った場合利用しているドメインやその名前解決の結果から攻撃対象の推定の材料となる可能性がありますが、DoHを利用して経路を暗号化することでそれを防御しよりゼロトラストな環境に近づけることができます。

DNS over HTTPSについて

DoHは名前の通りHTTPS経路上で名前解決を行うプロトコルでRFC 8484で標準化された技術となります。
HTTPS自体がHTTP over SSL(TLS)なので正確に表現するのであればDNS over HTTP over TLSとでもいうべきでしょうか。

従来では名前解決は公開情報であるような面もありプライバシーを保護することは要件とみなされておらず、DNSSECのように通信自体の応答に対する改竄の防止を防ぐ技術はありましたが通信それ自体を秘匿化するようなものではありませんでした。

DoHはそれを解決するためのプロトコルの一つでHTTPS上で通信を行うことで通信内容を秘匿化することができます。
特にシステム内部用のドメインは一般的な公開ホスト名に比べて具体的な内部ネットワーク上のIPを得る材料となったり、ホスト名から何のシステムかを特定する材料となるような値となっていること等で相対的に秘匿性が高くなっている可能性があるためそれを保護することができます。
プライベートでない公開ドメインであっても特徴のある統計が取れれば水飲み場攻撃の材料の一つになるためそういった面の保護も期待できます。

またTLS確立時に利用する証明書の検証を適切に行うことで不正なサーバへの通信を防ぐことも可能でMITMへの対応も可能です。

類似技術としてHTTPを介さず直接TLS通信上に名前解決処理をのせるDNS over TLS(DoT)がRFC 7858で定義されています(先発技術としてはDoTです)。

DoHは専用のポートを用意する必要がなくHTTPSと同様443がデフォルトポートであるため暗号化された通信を確認した際に名前解決を行っているのかWebサイトアクセスのために行っているかの判別ができなくなるためより秘匿性が高いのがメリットです。

ただ良くも悪くも両方ともポート443当ての通信となってしまうためなんらかの事情でこれらの通信を分けたい場合は不便が生じる可能性がある等必ずしもDoHが優れてるとは限らないようです。
(あまり差はない気がしますがhttpを挟む分若干パケットも大きくなりそうのもデメリットでありそうな気はします。)

なお上記はDoHを利用することで得られるメリットの一例ですがりRoute 53 Resolverはマネージドに管理されてサービスですので設定できる範囲上必ずしも全てのメリットが享受可能とは限らない点はご注意ください。

設定

2つのVPC AとVPC Bをピアリング接続で繋ぎもう片側のVPCに紐づけられているプライベートホストゾーンの名前解決を試してみます。

構成的にはエンドポイントを利用せずプライベートホストゾーンをVPC Aにも結びつけるのが構成的にもシンプルですがDoHの利用はエンドポイント経由のみ利用可能となるためそちらを挟みます。

今回はDoHによる名前解決のみができるように従来の通常の名前解決であるDo53は無効化しDoHのみを有効化しておきます。

エンドポイントに割り当てるセキュリティグループもover HTTPSによる通信ですのでポートは443でTCPを許可します。

将来HTTP/3(HTTP over QUIC)が利用される場合はUDPの許可もする形になるのでしょうか?

プライベートホストゾーンとしてexample.comが以下の値を返すように設定しています。

$ dig example.com
; <<>> DiG 9.16.48-RH <<>> example.com
...
;; ANSWER SECTION:
example.com.            300     IN      A       127.0.0.1

;; Query time: 0 msec
;; SERVER: 172.10.0.2#53(172.10.0.2)
;; WHEN: Mon Mar 11 07:30:50 UTC 2024
;; MSG SIZE  rcvd: 56

$ dig doh.example.com
...
;; ANSWER SECTION:
doh.example.com.        300     IN      A       127.0.0.1

;; Query time: 0 msec
;; SERVER: 172.10.0.2#53(172.10.0.2)
;; WHEN: Mon Mar 11 07:30:55 UTC 2024
;; MSG SIZE  rcvd: 60

あくまでインバウンドエンドポイント経由の解決がDoHのみに制限されているため同VPC内のクライアントが直接Resolverに名前解決を行う場合は従来のポート53を利用した名前解決が可能です。

なおそもそもインバウンド・アウトバウンドエンドポイントを介さないとDoHによる通信はできないため、直接Route 53 Resolverに対してDoHでリクエストをかけたところタイムアウトになりました(というより応答が返ってこない)。

DoHで名前解決してみる

digを利用

digの場合+httpsを指定することでover HTTPSで通信可能です。

ただ+httpsオプションについてはAmazon Linux 2023にデフォルトで入っている9.16.48では対応しておらず、Ubuntu 22.04に入っていた9.18.18で対応しているといった状況でバージョンによっては利用できない可能性があるのでご注意ください。

## on Amazon Linux 2023
$ dig -v
DiG 9.16.48-RH
$ dig example.com +https
Invalid option: +https
...
## on Ubuntu 22.04
$ dig -v
DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu
$ dig example.com +https
;; Connection to 127.0.0.53#443(127.0.0.53) for example.com failed: connection refused.
;; Connection to 127.0.0.53#443(127.0.0.53) for example.com failed: connection refused.

まずはインバウンドエンドポインを利用し通常のDNSで解決を試みてみますが、当然インバウンドエンドポイントではポート53の通信を許可していないためこちらの解決は拒否されます。

$ dig example.com @172.10.254.100
;; communications error to 172.10.254.100#53: timed out
;; communications error to 172.10.254.100#53: timed out

+httpsオプションをつけて実行することでHTTPS(443)で名前解決を行うためタイムアウトは起こらず先ほど設定した値が返却されることが確認できます。

$ dig example.com @172.10.254.100 +https

; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> example.com @172.10.254.100 +https
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22852
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      A

;; ANSWER SECTION:
example.com.            300     IN      A       127.0.0.1

;; Query time: 3 msec
;; SERVER: 172.10.254.100#443(172.10.254.100) (HTTPS)
;; WHEN: Mon Mar 11 07:35:05 UTC 2024
;; MSG SIZE  rcvd: 67

curlを利用

前述のとおりHTTPSを利用して通信しているためcurlでリクエストすることでも結果は得られますのでこちらを試してみます。

HTTPSのエンドポイントに関するドキュメントが見えたらないのですが、同RFCの記載を参考に以下のようにリクエストをかけたところ応答が返ってきましたのでこちらで試します。

$ curl -k "https://172.10.254.100/dns-query?dns=aaaaa"
illegal base64 data at input byte 4

aaaaaの部分にはDNSリクエストをbase64でエンコードした値を記載します。

0から組み立てるのは少し辛いですが幸い同RFCのサンプルがwww.example.comのものなのでこのデータをデコードしてファイルに出力し、wwwdohに書き換えてリクエストを作成します。

$ cat doh.example.com | xxd
00000000: 0001 0100 0001 0000 0000 0000 0364 6f68  .............doh
00000010: 0765 7861 6d70 6c65 0363 6f6d 0000 0100  .example.com....
00000020: 01

このデータをbase64でエンコードします。厳密にはBASE64URL Encodingのためパディングで=が入っていたりその他一部エンコード処理が必要なものがある場合は修正が必要ですが今回は含まれていないので不要です。

$ cat doh.example.com | base64
AAEBAAABAAAAAAAAA2RvaAdleGFtcGxlA2NvbQAAAQAB

この値を元にインバウンドエンドポイントに向けてcurlでクエリをかけ結果を取得します。

$ curl -k "https://172.10.254.100/dns-query?dns=AAEBAAABAAAAAAAAA2RvaAdleGFtcGxlA2NvbQAAAQAB" -H 'accept: application/dns-message' | xxd
...
00000000: 0001 8180 0001 0001 0000 0000 0364 6f68  .............doh
00000010: 0765 7861 6d70 6c65 0363 6f6d 0000 0100  .example.com....
00000020: 0103 646f 6807 6578 616d 706c 6503 636f  ..doh.example.co
00000030: 6d00 0001 0001 0000 012c 0004 7f00 0001  m........,......

生のレスポンスなのでぱっと見だとわかりづらいですが末尾の7f 00 00 01が応答で10進数に直すと127 0 0 1で設定している127.0.0.1が得られています。

--verboseで確認してみると以下のとおりです。
CNの値はroute53resolver.ap-northeast-1a.amazonaws.comとなっているためリクエストのホストヘッダの一致を判断する必要があればhostsファイル等なんらかでマッピングを行う必要がありそうです。確認忘れていましたが名前的にAZ毎に異なる証明書となるのでしょうか?
(現状インバウンドエンドポイント側で独自の証明書を設定できるような箇所は見当たらない)

# 暗黙的に証明書のホスト名が内部のみで良い感じにインバウンドエンドポイントのIPを返すわけでもない。
$ dig route53resolver.ap-northeast-1a.amazonaws.com +short | wc -l
0
$ curl -k --verbose "https://172.10.254.100/dns-query?dns=AAEBAAABAAAAAAAAA2RvaAdleGFtcGxlA2NvbQAAAQAB" -H 'accept: application/dns-message' | xxd
*   Trying 172.10.254.100:443...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to 172.10.254.100 (172.10.254.100) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [96 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [5084 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [333 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [70 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=route53resolver.ap-northeast-1a.amazonaws.com
*  start date: Dec 26 00:00:00 2023 GMT
*  expire date: Dec 16 23:59:59 2024 GMT
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M01
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x1dada3a0)
} [5 bytes data]
> GET /dns-query?dns=AAEBAAABAAAAAAAAA2RvaAdleGFtcGxlA2NvbQAAAQAB HTTP/2
> Host: 172.10.254.100
> user-agent: curl/7.79.1
> accept: application/dns-message

証明書自体のルート・中間CAはCNの値を見る限りACMで発行した証明書と同等のものが利用されていそうです。

# openssl s_client -connect 172.10.254.100:443
CONNECTED(00000003)
depth=2 C = US, O = Amazon, CN = Amazon Root CA 1
verify return:1
depth=1 C = US, O = Amazon, CN = Amazon RSA 2048 M01
verify return:1
depth=0 CN = route53resolver.ap-northeast-1a.amazonaws.com
verify return:1
---
Certificate chain
 0 s:/CN=route53resolver.ap-northeast-1a.amazonaws.com
   i:/C=US/O=Amazon/CN=Amazon RSA 2048 M01
 1 s:/C=US/O=Amazon/CN=Amazon RSA 2048 M01
   i:/C=US/O=Amazon/CN=Amazon Root CA 1
 2 s:/C=US/O=Amazon/CN=Amazon Root CA 1
   i:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2
 3 s:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2
   i:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
---

終わりに

今回は名前解決に対して秘匿化を実現するDNS over HTTPSの技術に触れてみました。

こちらの技術自体今回初めて知ったのですが特にTLSで通信内容を暗号化するのみでなくHTTPS経由とすることでWEBシステムへアクセスしているのか名前解決を行っているのかが判別出来なくしているという点で一つ驚きのある技術でした。

長らくDNSは公開情報でそこまで秘匿性の高いものではないという印象がありましたが、プライベートなホスト情報の生の名前解決が流れることで値から内部のシステムが推定可能であったり、パブリックなものであっても統計が取ることができれば水飲み場攻撃のような標的型攻撃の材料の一つになる可能性もあり危険性が0とは言えないためゼロトラストを目指していくと必要となってくる部分となりそうです。
(今回はRoute 53 Resolverへの直接解決は対応せず、まずアウトバウンド・インバウンドエンドポイントの対応となるためまずの部分として単独のシステムというより、ハイブリッドクラウド等で開発者だけではなく社内一般ユーザが入るような利用がターゲットでしょうか?)

DoHを利用することで名前解決を秘匿性高い通信とすることができる一方、元々UDPで非常に軽量なプロトコルであったDNSに対しHTTPやTLSを重ねることによりTCP・TLSハンドシェイクによるレイテンシの増加や通信量の増大は発生するというデメリットはあります。
秘匿化が必ずしも好ましいものとなるとは限りませんので、なんでも暗号化ではなく自身の環境やご時世でどこまで求められているかとあわせて選択していただければと思います。