こんにちは,id:hanazuki です.いよいよ明日からRubyKaigi 2023 が始まりますね.私は昨日から松本入りして,今日は会場の設営をやっていて,ちょうどいま(17:00),お昼休憩をとっています…….会期が終わったあとにまとめを書こう〜なんて思っているとたいていそのまま忘れてしまうので,今年は準備と並行して記事をしたためている次第です.
私は,RubyKaigiで来場者向けに提供しているWi-Fi の構築・運用を2017年から手伝っています.毎年おなじことをやっていてもおもしろくないというか,だんだんとやることが目減りしてしまいますよね.今年は何か新しいことをできないかと考えて,RubyKaigi会場ネットワークのDNS リゾル バをDNS -over-HTTPS に対応させることにしました.
この記事では,
DNS -over-HTTPS が普通のDNS とはどう違うのかを見てから,
RubyKaigi会場ネットワークのDNS リゾル バがどのように作られているのか紹介し,
どうすれば今年のRubyKaigiの会場でDNS -over-HTTPS を試せるかを説明したいと思います.
(2023-05-17追記)実際に運用をして分かったことと分からなかったことを記事末尾に追記しました.
そもそもDNS とは何だったか
まずは,用語の整理も兼ねてDNS をおさらいしましょう.この記事ではDNS に関連する用語はできるだけRFC8499 に従いたいと思います.また,JPRSによるRFC8499の日本語訳 も参考にしています.
DNS は,ドメイン 名(例えばkmc.gr.jp.
)をキーとして様々な情報を引ける階層型の分散データベースシステムです.たとえば,kmc.gr.jp.
のAレコードをクエリすると,KMC部室のIPv4 アドレス192.50.220.129
がわかります.みなさんがブラウザでhttps://kmc.gr.jp
にアクセスしようとすると,裏でこっそりこのようなクエリが行われています.ドメイン 名からIPアドレス を得る手続きなので「名前解決」とも呼ばれます.
kmc.gr.jp.
以下のドメイン に関するコンテンツはKMCが管理しています.これは,jp.
ドメイン を管理しているJPRS に利用料を払ってkmc.gr.jp.
以下を使う権利を買うことで実現しています.JPRS もルートドメイン .
を管理してるICANN から国を代表してjp.
を使う権利を取得しています.kmc.gr.jp.
やjp.
,.
といった管理の単位はゾーンと呼ばれています.特定のゾーンに関するコンテンツを保持しているDNS サーバを権威サーバと呼びます.権威サーバはそれぞれのゾーンを管理する組織が責任を持って運用しています.このようにドメイン の親子関係によって管理主体がわかれていることから,DNS は階層型かつ分散的であると言えます.この記事では権威サーバは脇役なのであまり登場しません.
一方で,権威サーバに世界中のクライアント端末からクエリが殺到すると大変なので,キャッシュサーバを設置するのが一般的です.キャッシュサーバは,伝統的にはISP やオフィスなど,ある程度まとまった利用者のいるネットワーク管理主体が設置していました.最近はGoogle Public DNS やCloudflare DNS など,世界中からアクセス可能なキャッシュサーバも増えています.クライアント端末からのクエリを取りまとめて,情報を持っている権威サーバからレコードを取得し,後に備えてキャッシュするDNS サーバのことをフルサービスリゾル バと呼びます.
この記事ではフルサービスリゾル バの意味で「DNS リゾル バ」という言葉を用います.RubyKaigiの会場にも1,000人ほどのRubyist が2,000台*1 くらいのガジェットを携えてやってくるので,伝統的な流儀に則ってDNS リゾル バを設置してRubyist のみなさんに提供しています.この記事ではRubyKaigi会場で提供しているDNS リゾル バの話をします.
古き良きインターネット時代のDNS
通信プロトコル としてのDNS はごく素朴に設計されました.ドメイン 名(たとえばkmc.gr.jp.
)と欲しい情報の種類(たとえばIPv4 アドレスが欲しいときはAレコードを表す整数1
)を何ビットかのフラグと一緒に送ると,いくらかの付加情報をともに192.50.220.129
が返ってくるだけです.
ハンドシェイクの不要なUDP プロトコル が用いられていましたが,おそらく当時の技術的な制限でメッセージあたり512バイトまでに限られていました.クエリやレスポンスの大きさがこの上限を超えた場合はTCP にフォールバックする決まりでしたが,TCP はハンドシェイクが必要なので余分な時間がかかります.
後にプロトコル を拡張する中でフラグのビット数が足りなくなったり,メッセージが512バイトに収まらないことが増えたりしたことから,拡張規格のEDNS0が考案され,通信経路が許せばUDP で65,535バイトまで送ってもよいことになりました.
このプロトコル が今日まで使われ続けています.当初のインターネットは平和だったので暗号化の機能はありません.
暗号化インターネット時代のDNS
2000年代ごろにもDNS プロトコル を暗号化しようとする試みはあったそうですが,あまり普及はしなかったようです.2010年代にさまざまな事件があり,実は国家規模の権力と動機と資金があればインターネットをまるごと盗聴することも不可能ではないらしいと分かってきました(本当のところはよく知りませんが……).これによってさまざまなインターネットプロトコル を暗号化しようという機運が高まります[RFC7435 ].
そして,TCP 上でTLS を用いて通信を暗号化するDNS -over-TLS (DoT)が考案されました[RFC7858 ].TCP ハンドシェイクに加えて,TLS ハンドシェイクのオーバーヘッドがかかるも,背に腹は変えられないというわけです.とはいえ無策ではなく,TCP /TLS コネクションをキープアライブして,クエリをパイプライン化するなどの工夫はされます.現在このプロトコル はAndroid やsystemdなどのクライアント環境に実装されています.
さて,伝統的な平文のDNS プロトコル は53/udp と53/tcp ポートを使います(それゆえレトロニムでDo53とも呼ばれています).一方,DoTは853/tcp を利用します.みなれないポート番号なので,環境によってはファイアウォール などがあやしい通信とみなして遮断してしまうことがありました.
そこで,DNS のリクエス トレスポンスをHTTPS 上で行うDNS -over-HTTPS (DoH)が考案されました(Google Public DNS などで実装され,RFC8484 で標準化).いわばDNS over HTTP over TLS over TCP ということになりますね.443/tcp で通信をするため,外見上は普通のHTTPS と区別がつかず多くの環境で使えるはずという狙いです.前のクエリ・レスポンスが完了するまで次のクエリを送れないHoLブロッキング の問題を回避するために,マルチストリーム通信の可能なHTTP/2 RFC9113 が主に利用されます.
HTTPS はおそらく現代で最も開発研究の労力が投じられているインターネットプロトコル の一つで,DoHは自動的にHTTPS の改善の恩恵にあずかれるというメリットもあります.例を挙げると,Google Public DNS とCloudflare DNS にはHTTP/3を使ったDoHがすでに実装されていて,Firefox やChrome はこれを使うことができます(DNS over HTTP over QUIC over UDP ).HTTP/3には,HTTP/2と違ってTCP 層でのHoLブロッキング を回避できる利点があり,この特徴がDoHにも自動的に導入されるというわけです.
DNS -over-???
その他に,UDP 上でDTLSを用いて通信を暗号化するDNS -over-DTLS (DoD )も考案されましたが(DNS over DTLS over UDP )[RFC8094 ],実験以上の規模で実装されたという話は聞きません.
また,DNS -over-QUIC (DoQ)というプロトコル も提案されています(DNS over QUIC over UDP )[RFC9250 ].HTTP/3を使うDoHに比べて,HTTP層を省くことでより軽量になるという謳いのようです.まだDoQをサポートするクライアントOS・ブラウザの話は聞かないですが,はやるでしょうか? 一部の公開DNS リゾル バには実装が始まっています*2 .ちなみにデフォルトでは853/udp ポートを使うことになっていて,DoD のポート番号がリサイクルされています.
2. RubyKaigiのDNS リゾル バはいかに作られているか
RubyKaigi 2023の物理ネットワークの一部.ケーブルをタップすれば,個人規模の権力と動機と資金でもDNS トラフィック の盗聴ができる(忙しいのでめんどうごとは増やさないでくださいね).
サーバソフトウェア
RubyKaigiのDNS リゾル バには,フルサービスリゾル バの機能をもったUnbound を使っています.Unboundの最新版はDo53に加えてDoTとDoH (HTTP/2)をサポートしています.Ubuntu 22.04でパッケージされているバージョン1.13.1ではDoH機能に不具合があり,特定の条件でリクエス トに応答しそこねるようでした*3 .RubyKaigi程度の規模であれば,通常のチューニング をしておけば問題はないと考えています.DoT/DoH特有の事情として,これらのプロトコル はTCP ソケットを使うのでincoming-num-tcp
を十分に大きく取る必要があります(デフォルトの10では足りません).
UnboundのDoHサーバはHTTP/2を喋りますが,クライアントはすでにHTTP/3を使うものも現れているので(後述),Envoy をHTTPS リバースプロキシとして置いています.DoHだからといって特別な設定は必要なく,信頼できないクライアントからのリクエス トを受けるために通常のエッジプロキシとしての設定 をしておけばよかろうと思います.UnboundのHTTP/2サーバ機能にまだあまり利用実績はないでしょうからやや心配で,HTTP/2のリクエス トも一旦Envoyで受けてプロキシする形にしています.
Unboundのメトリクスを取るためにletsencrypt/unbound_exporter にいくつかパッチを当てたものを利用しています.パッチの一部はプルリクエス トにしており,マージされると良いのですが.グラフを眺めるのが好きな人のために会期中限定でGrafanaのダッシュ ボードを公開しています.公開終了しました.スナップショット をご覧ください.
EKSとNLB
EnvoyやUnboundのような細々としたサーバソフトウェアたちを走らせるために,AWS のマネージドKubernetes サービスのAmazon EKSを利用しています.そして,Kubernetes で管理されるコンテナを束ねて冗長化 するため,AWS のNetwork Load Balancer (NLB)を使っています.
伝統的なDo53のクライアントは,UDP データグラムにメッセージが収まりきらなかった場合,同じIPアドレス 宛のTCP 通信にフォールバックします.ポート番号は53から変えられません.また,日和見 的にDoTにアップグレードするクライアントは,Do53と同じIPアドレス の853/tcp に接続を試みます.これらの要請から,Do53 (UDP /TCP ),DoTのリゾル バはすべて同じIPアドレス で提供する必要があります.さらに,今回はDoTとDoHのリゾル バをどちらもresolver.rubykaigi.net
というドメイン 名で使えるようにしたかったため,DoTとDoH (UDP /TCP )も同じIPアドレス で提供する必要が生まれました.
AWS Load Balancer Controller を使うと,Kubernetes 上のServiceリソースの定義によってAWS 上のロードバランサを管理できるようになります.しかしながら,1つのServiceでTCP とUDP 両方の通信を受けることができない制限があります(これを可能にする機能追加はMixedProtocolLBService
として開発中です).さらに,Kubernetes のServiceの抽象化では,53番ポートへの通信をunboundのPodに流し,443番ポートへの通信をenvoyのPodへ流すといった構成も取れません.こういった制約から,NLB自体はKubernetes の外部で管理して,Service配下のPod一覧とNLBのtarget groupを同期するTargetGroupBinding という機構を使っています.
ネットワーク
ここまででDNS リゾル バを起動するところまでできました.では,松本のRubyKaigi会場と東京のAWS ,そして世界はどのように繋がっているのでしょうか.この話をするとかなり長くなってしまうので,この記事では大枠だけを紹介したいと思います.ネットワークのL1/L2の設計,L3/L4部分の構築はWi-Fi スポンサーのクックパッド さんが担当してくれているので,そちらから本格的な解説が出るのを一緒に願いましょう.その代わりに私からは,分かった気持ちになれるかもしれない絵を用意しました.
右側がAWS 東京リージョン,左上が都内DC,左下が松本の会場.会場・DC間はフレッツNGN 網内折り返しにより,DC・AWS 間はAWS Direct Connectにより接続されている.AWS 上にはDNS リゾル バがデプロイされている.実際はそれぞれの部分をさまざまな層で冗長化 しているが図では省略している.
まず,松本の会場にはNTT東日本 のフレッツ光ネクスト を引いています.これは一般のご家庭で使っているものと同じグレードの光ファイバー 回線なのでなじみがあると思います.そして,クックパッド さん提供の都内データセンター(DC)のラックスペースにRubyKaigi用のルータを設置してあります.このラックにもフレッツ回線が引かれていて,会場とこのルータの間はフレッツNGN 網内の折返し通信によって接続されています.NGN については,部員のid:sora_h による解説記事『NTTフレッツ光における通信速度などの現状について、背景や仕組みから正しく理解する2020 』*4 をおすすめします.
実はKMCもクックパッド さんから同じラックとキャリア回線をお借りして,KMCが運用する自律システムAS59128 / KMC-AS のPOPを開設しています(このプロジェクトについても解説を読めるとうれしいですね!).KMCはこのPOPからRubyKaigiネットワークに対して,IPアドレス と対外接続を提供しています.RubyKaigi会場でRubyist たちがインターネットと通信をする場合,会場からフレッツ網を通って都内DCへ,都内DCからKMCのASを経由してインターネットへと抜けてゆきます.実際,会場Wi-Fi に接続した端末がどのパブリックIPアドレスを使って通信しているか調べる と,KMCが保有 するIPアドレス 帯192.50.220.0/24
を使っていることがわかると思います.
さて,AWS との接続はどうでしょう.これもクックパッド さんの提供で,AWS Direct Connectの専用線 でRubyKaigiのルータとAWS VPC を結んでいます.Wi-Fi に繋がった端末から名前解決すると,フレッツ網を通って都内DCへ,都内DCからDirect Connectを通ってVPC へ向かいます.VPC ではNLBによってEnvoyやUnboundのコンテナのどれかへ振り分けられます.
会場のクライアントからインターネットへのトラフィック は,NGN を通って都内DCへゆき,KMC-AS経由でインターネットへ出る(左側の水色矢印). クライアントからのDNS 再帰 クエリは,Direct Connectを通ってAWS へたどり着き,NLBによってEnvoyコンテナに振り分けられ,EnvoyからUnboundへプロクシされる(右下側の赤色矢印). Unboundからの反復検索クエリは,Private NAT Gateway でNAPTされ,Direct Connectを通って都内DCへ向かい,KMC-AS経由でインターネットへ到達する(上側の黄色矢印). 一部のドメイン 名の名前解決は,UnboundからRoute53 Resolverに転送されて,Route53のprivate hosted zoneを参照している(右上側の赤色矢印).
こうしてDNS リゾル バがクライアントからのクエリを受けられるようになりました.あとは,リゾル バがインターネットの向こうの権威サーバーへクエリできるようにしたいです.通常,AWS VPC で稼働しているサービスがIPv4 インターネットと通信する場合,パブリックサブネットに設置したNAT Gateway でNAPTして,Internet Gateway 経由で外部のホストと通信する構成が一般的です.
ところがRubyKaigiでは,Unboundから権威サーバへのクエリは,VPC からDirect Connectで都内DCへ抜けて,そこからKMC-AS経由でインターネットへ出すようにしています.これは,DNS を使った広域負荷分散(GSLB)に悪影響を与えないようにするためです.一部のCDN などでは,アクセス元の地理的位置・インターネット上の論理的な位置を考慮して,ユーザをできるだけ近いサーバに接続させるために,権威サーバがどのネットワークからクエリが来たかを認識して異なるレスポンスを返し分けるということを行っています.したがって,ユーザのトラフィック はKMC-ASからインターネットへ出ているのに,権威サーバはAWS のネットワークからインターネットに繋がっているのでは都合が悪いのです.たとえば,会場のWi-Fi に接続して,o-o.myaddr.l.google.com.
のTXTレコード(このDNS サーバからみてクライアントのIPアドレス を返してくれるおもしろいエンドポイントです)を引いてみると,KMCの保有 するIPアドレス 192.50.220.0/24
になっていると思います.残念ながら,暗号化通信に対応している権威サーバは未だ少なく,リゾル バから権威サーバ間の通信は平文で行われているのが現状です.
また,rubykaigi.org
のサブドメイン など一部のドメイン についてsplit-horizon DNS を行っています.特定のサービスに会場内からアクセスするのにインターネットを経由せずプライベートなDirect Connect経由でVPC に通信できるよう,プライベートアドレスを返すようにしています.これを実現するために,それらのドメイン に関する再帰 クエリはUnboundからRoute53 Resolverに転送してRoute53でホストしているプライベートゾーンのコンテンツを返すようにしています.
クックパッド 社のid:sora_h がRubyKaigiネットワークの解説を書いてくれました.もっと詳しく知りたい!と思った方はあわせてお読みください.
3. RubyKaigiの会場でDNS -over-HTTPS を試す
クライアントOS・ブラウザの対応状況
PCのChrome ,EdgeやFirefox を利用している場合,ブラウザの設定でカスタムDNS プロバイダを使用するよう選択し,プロバイダとしてhttps://resolver.rubykaigi.net/dns-query{?dns}
と入力すると,DoHで名前解決するようにできます.会場外ではこのリゾル バを使えないので,外へ出たら設定を戻し忘れないように気をつけてください.Windows 11のInsider Preview にもDoHが実装された*5 と小耳に挟みましたが,まだ試せていません.
最近Android にもDoHが実装され,Android 11までバックポートされているようですが,特定の公開リゾル バに対してしかDoHを使わないそうです*6 .残念ですね.そのかわりAndroid は「プライベートDNS モード」が「自動」に設定されていると,DoTを使った日和見 暗号化を行います.DoTはAndroid Pieから実装されているそうなので*7 ,多くの現役スマートフォン は対応しているのではないでしょうか.
iOS /iPadOS 16とmacOS Venturaでは何もする必要がありません.最近リリースされたこれらのOSには,DNS リゾル バがDoHに対応していることを自動的に検出してアップグレードする仕組みが実装されています.この機能はDiscovery of Designated Resolvers (DDR )と呼ばれています.Apple のOSのセキュアDNS への対応は,DNSSECの話と併せてWWDC22でも紹介されていたようです*8 .
RubyKaigiの参加者はMacBook を使っている割合が比較的たかいと思いますが,せっかくなので他OSのユーザ向けにDDR をどのように実装すればよいか解説してみたいと思います.DDR の仕様draft-ietf-add-ddr はIETF で起稿中で,Apple のOSにもおおよそこのドラフト通りに実装されているようでした.draft-ietf -add-ddr は,同じくまだドラフト段階のdraft-ietf-dnsop-svcb-https やietf-add-svcb-dns に依存しています.これらの規格は,DNS リゾル バがどのようなプロトコル に対応しているかをDNS 上で公開・取得する方法を定義しています.
この記事を書いた時点での最新版はそれぞれdraft-ietf-add-ddr-10 ,draft-ietf-dnsop-svcb-https-12 ,draft-ietf-add-svcb-dns-08 です.以下ではこれらの版の内容に従います.
さて,みなさんの端末がどのようなOSで動いているとしてもRuby はインストールされているでしょうから,ここからはRuby のコードを使って説明をします.
まず,Bundlerで必要なライブラリをインストールするためにGemfileを用意します.
source ' https://rubygems.org '
gem ' net-http '
gem ' base64 '
gem ' addressable '
gem ' resolv ' , git : ' https://github.com/hanazuki/ruby-resolv ' , branch : ' svcb-dns '
そして,Ruby のスクリプト を書きます.
#!/usr/bin/env ruby
require ' addressable '
require ' base64 '
require ' net/http '
require ' resolv '
DoHResolver = Data .define(:hostname , :socket , :dohpath )
RESOLVERS = %w[ 192.50.220.164 192.50.220.165 ]
def connect_doh_resolver (unencrypted_resolvers)
unencrypted_resolvers.each do |unencrypted_resolver|
Resolv ::DNS .open(nameserver : [unencrypted_resolver]) do |dns|
dns.timeouts = 3
svcb_rrset = dns.getresources(' _dns.resolver.arpa. ' , Resolv ::DNS ::Resource ::IN ::SVCB )
svcb_rrset.sort_by!(&:priority )
fail ' TODO: AliasMode is not supported ' if svcb_rrset.any?(:alias_mode? )
svcb_rrset.select! do |svcb|
svcb.params[:dohpath ] &&
(svcb.params[:alpn ]&.protocol_ids&.include?(' http/1.1 ' ) ||
svcb.params[:alpn ]&.protocol_ids&.include?(' h2 ' ))
end
svcb_rrset.each do |svcb_doh|
a_rrset = dns.getresources(svcb_doh.target, Resolv ::DNS ::Resource ::IN ::A )
fail "#{ svcb_doh.target} does not serve over IPv4 " if a_rrset.empty?
a_rrset.shuffle.each do |a|
socket = TCPSocket .new(a.address.to_s, svcb_doh.params[:port ]&.port || 443 )
ctx = OpenSSL ::SSL ::SSLContext .new
ctx.set_params(alpn_protocols : %w[ http/1.1 ] )
ssl_socket = OpenSSL ::SSL ::SSLSocket .new(socket, ctx)
ssl_socket.sync_close = true
ssl_socket.hostname = unencrypted_resolver
Timeout .timeout(3 ) { ssl_socket.connect }
return DoHResolver .new(
hostname : unencrypted_resolver,
socket : Net ::BufferedIO .new(ssl_socket),
dohpath : Addressable ::Template .new(svcb_doh.params[:dohpath ].template),
)
rescue
warn $!
end
rescue
warn $!
end
end
rescue
warn $!
end
fail ' No resolver supports DoH '
end
def doh_query (resolver, qname, qtype)
query = Resolv ::DNS ::Message .new(0 ).tap do |m|
m.rd = 1
m.add_question(qname, qtype)
end
path = resolver.dohpath.expand(
dns : Base64 .urlsafe_encode64(query.encode, padding : false )
)
req = Net ::HTTP ::Get .new(path)
req[' Connection ' ] = ' keep-alive '
req[' Host ' ] = resolver.hostname
req[' Accept ' ] = ' application/dns-message '
req.exec(resolver.socket, ' 1.1 ' , req.path)
begin
res = Net ::HTTPResponse .read_new(resolver.socket)
end while res.is_a?(Net ::HTTPInformation )
res.reading_body(resolver.socket, req.response_body_permitted?) do
if res.code == ' 200 ' && res[' Content-Type ' ] == ' application/dns-message '
return Resolv ::DNS ::Message .decode(res.body).answer
end
end
fail " Request failed: #{ res.inspect}"
end
doh_resolver = connect_doh_resolver(RESOLVERS )
doh_query(doh_resolver, ' kmc.gr.jp ' , Resolv ::DNS ::Resource ::IN ::A ).each do |_, _, a|
p a.address
end
さて,いま見たようにDDR は,DHCP などで非暗号化DNS リゾル バのIPアドレス が与えられたときに,安全にDoHやDoTなどの暗号化プロトコル にアップグレードを行う枠組みです.この安全性はPKI で検証したTLS 証明書によって暗号化DNS リゾル バを認証することで担保されています.TLS 証明書をうまく使うことによって,非暗号化DNS リゾル バと暗号化DNS リゾル バを別のIPアドレス で運用することも許されています.
非暗号化DNS リゾル バと暗号化DNS リゾル バのIPアドレス が一致する場合にはTLS 証明書の検査を省略してもよい——とも書いてありますが,そうしないクライアントが存在する限りは正規の証明書を用意する必要があります.Subject Alt Name (SAN)にIPアドレス を載せた証明書を手に入れる必要がありますが,Let's Encryptのような有名どころの認証局 はIP SANの証明書を発行してくれないので,すこし手間がかかりますね.RubyKaigiでどの認証局 を使ったか気になる方は,会場に来て証明書を確認するか192.220.50.0/24のCTログ を覗いてみてください.
また,パブリック認証局 はプライベートIPアドレス を使う証明書を発行しないため,DHCP で配布するリゾル バのIPアドレス にはパブリックIPアドレス を使う必要があります(リゾル バがインターネットからアクセス可能である必要はありませんし,深く考えずにそうすべきではありません).IPv4 アドレスの調達に難がある場合も多いかもしれません.社内環境など,全てのクライアントにプライベート認証局 証明書をインストールできる条件であればこれらの要件は緩和されるでしょう.
一方,DDR の枠組みでは防げない攻撃手段も存在します.たとえば,a)攻撃者がDNS リゾル バのIPアドレス への経路を侵害し,認証局 から正規のTLS 証明書の発行を受けた場合,b)攻撃者にDHCP パケットを捏造された場合,c)攻撃者にSVCBレコードのクエリ・レスポンスを遮断された場合,d)攻撃者の用意した偽のWi-Fi アクセスポイントにつないでしまった場合などです.これらの攻撃からユーザを守るには,a) RPKIなどによる経路情報の認証・証明書透明性(CT)ログの監視による攻撃検知,b) Wi-Fi アクセスポイントでのDHCP スヌーピング,d) IEEE 802.1X によるネットワーク認証など,多層的な防衛が必要です.
このうちc)に関しては,DNR という別の規格が考えられている途中です[draft-ietf-add-dnr ].おおまかには,DDR で平文のクエリによって取得していた暗号化DNS リゾル バの情報を,最初からDHCP などのオプションに詰め込んで渡してしまおうという作戦です.
DDR やDNR をはじめとした,クライアントがネットワーク環境に応じて適切なDNS リゾル バやプロトコル を選ぶための技術が,IETF のAdaptive DNS Discovery (ADD) ワーキンググループで開発されています.この分野のRFC ドラフトの著者の所属を見ると,クライアントOS・ブラウザの開発元,公開DNS リゾル バの運営元,セキュリティアプライアンス の開発元などの企業が揃っていますね.近いうちに一般の環境にも対応が広がるのではないかと期待しています.RubyKaigi 2023のDNS リゾル バには,安全なWi-Fi を提供するためと言いつつおもしろ半分でDoT/DoH/DDR を実装したので,みなさんもぜひ遊んでみてください.
RubyKaigi 2023を終えて(Day 7追記)
RubyKaigiはあっという間に終わってしまいましたね.私はDay 5に会場で機材整理をしたあと,名残惜しさから予定外に1泊増やして松本の街を楽しみました.3日間の会期は短いですが(かといって,それ以上の期間Wi-Fi を運用し続ける体力はありませんが),ひさしぶりのオフライン・フルスケールでのRubyKaigiは濃密な時間に感じられました.Kaigi Wi-Fi がRubyistsのコラボレーションに役立っていたなら幸いです.
会期中にメトリクス を眺めていたところ,いくつか分かったことと分からなかったことがありました.
Wi-Fi の同時接続数1300くらいに対して,クエリレートは300–400 qps程度でした.このくらいであればなにも考えずに運用できますね.UnboundもEnvoyもたいへん安定しています.
タイプ別のクエリ数では,A
/AAAA
がほぼ同数でトップで,HTTPS
がその7割程度でした.AAAA
やHTTPS
へのアンサーが存在した割合のメトリクスも取れていればもう少しおもしろいデータになったかもしれないですね.PTR
のリクエス トもそこそこありました.これはmacOS が自機のホスト名を調べるために逆引きを行っているようです.
1/3くらいのクエリがDoHを使っていて,午後休憩の時間帯ではこの割合が1/2近くに達していた瞬間もありました.みんなApple 製品を大好きなことがわかりますね.実際のところは,iOS /macOS のスタブリゾル バはDoHリクエス トのヘッダでUser-Agentを名乗らないようで,どのくらいの割合がApple のOSなのか,その他の実装でDoHに対応しているものがあるのかないのかはよくわからないのです.User-Agentがフィンガープリンティングに悪用されがちな問題があるのはわかりますが,相互運用性が気になる立場ではもどかしい気持ちです.
DoHクエリのうち10%程度がHTTP/3を使っていて,残りはHTTP/2でした.自宅でドッグフーディング をしていたときにも,iPadOSが極稀にHTTP/3を喋っていたのですが発動条件がよくわかりませんでした.
正しくDNSSEC署名されていたドメイン はクエリに対する割合で5%弱でした.まだまだ普及しませんね.
Ruby のKaigiでネットワークの記事を書いてもあまり読まれないかと思っていましたが,現地で感想を伝えてくださったRubyist がいたのも大変うれしかったです.Kaigi Wi-Fi はあくまでRubyKaigiを支えるインフラですが,せっかくコミュニティメンバーで手作りしているので,Rubyistsにおもしろがってもらえるコンテンツも提供してゆけたらと思っています.その点で,今年は現地で手を動かせるおもちゃを用意できて,刺さる人には刺さっていたようでよかったです.半ばNOC メンバーが好き勝手あそんでいる風にもなっていますが,今年のWi-Fi は落ちなかったのでよしとしてもらいましょう.実は会期明けすぐにKMCのご近所にDDoSが着弾していて,ヒヤッとしたりしました.
RubyKaigi 2024開催地の沖縄は,インターネット上のコンテンツが集まる東京から地理的に遠く,ややチャレンジングな立地ですが,安定したインターネットを提供できるようにがんばりたいと思います.インターネット標準も引き続きウォッチしてゆくつもりなので,また来年もなにかおもしろいものを試せるとよいですね.
それでは沖縄でお会いしましょう!
おまけ
いい話すぎない(???)