読者です 読者をやめる 読者になる 読者になる

WebサーバとのコネクションでDNS通信もする拡張仕様

昨日の「DNS over QUICの提案仕様が出た」引き続きDNS関連の記事

DNSとHTTP

近年注目されている、"DNS over HTTP"は例えば「DNS-over-HTTPS」としてGoogleが提供していたり、IETF97でもdnsoverhttp BarBof(有志によるミーティング)も行われ課題についても議論がされています(議事録URL)。


もちろん仕様としても提案されており、以下などがあります


(IP + Portを見て)外とのDNSの通信がブロックされたりする環境があるようで、これらはDNSをHTTP上で行おうというものです。

Running DNS in Existing Connections

さらに先日、以下の2つのドラフトが提出されています


これらは、Webサービスとの通信といった既存のHTTP/2, QUICコネクション上でさらにDNSのメッセージもやりとり出来るようにする拡張仕様である。サーバ認証も行って確立した暗号路で改ざんを防ぎ、既存のコネクションを使うことでDNS通信がブロックされないようにする。


同じ著者によってHTTP/2とQUICのコネクションを利用するものが別々に提案されているが基本的には、新しくストリームをオープンし、その上でDNSのリクエストとレスポンスを行う。

DNSフレームが定義される

新しくDNSフレーム

   +---------------+
   |Pad Length? (8)|
   +---------------+-----------------------------------------------+
   |                         DNS message (*)                     ...
   +---------------------------------------------------------------+
   |                           Padding (*)                       ...
   +---------------------------------------------------------------+

DNS messageがそのままDNSメッセージであり、1つのストリーム上で問い合わせと応答が実施される

Service Discovery

既存のコネクションを利用しているため、DNSの問い合わせに応答するのはすでに接続しているサーバとなる。もちろんこの拡張に対応しているとは限らないため、下記のようにする。


拡張フレームは対応していない場合は無視されるので、上記拡張hルエームで問い合わせたいクエリ送信する。
もしくはexample.comなどのクエリを問い合わせし、対応していることを確認してから本来確認したクエリを送信するということも出来る。

その他

まだまだエラーハンドリングや、Settingsを使うか、ストリームの遷移・マッピングなど細かい所は決まってはいないが、IETFでのフィードバックにもあった一つのQUICコネクション上で複数のアプリケーションプロトコルという仕組みの提案として面白いと思う。


また、SPDY/4で追加機能として挙げられてはいた「Server push of DNS records」というものを思い出した。
(https://www.chromium.org/spdy/spdy-protocol)

DNS over QUICの提案仕様が出た

QUICの標準化とアプリケーションレイヤ

IETFでQUICの標準化が活発に行われており、トランスポート・TLS・HTTP各レイヤのドラフト仕様の改定が進められております。


標準化を行うにあたって当初より、DNSのトランスポートとしてQUICを使用したいという話題は出ていましたが、QUICワーキンググループのチャーターでは、まずはQUICのアプリケーションレイヤとしてHTTPの標準化を行ってから他のアプリケーションプロトコルについて進める旨書かれている。


とはいえDNS over QUICをやりたい人はいるようで、4/11にインターネットドラフトが出されている。共著者にQUICについてよく発表しているGoogleの方や、SalesforceやFastlyの人もいる。

Specification of DNS over QUIC

Specification of DNS over QUIC」として書かれている。


主な目標として下記があげれている

  • DNS over TLS(RFC7858)と同等のプライバシ保護を提供する。DNS通信の保護を議論している、DPRIVE WGで議論している「Authentication and (D)TLS Profile for DNS-over-(D)TLS」を含み、ドメイン名の権威をもつサーバとしての認証を行う。
  • DNS over UDP(RFC1035)と比べて、送信元IPの検証を提供する
  • 経路のMTUによって送れるDNSレスポンスを制限しないようなトランスポートを提供する
  • DNS over UDPDNS over TLSと対比してパフォーマンスの向上を検討する
  • HTTPとは異なるQUICの利用について概説し、QUICプロトコルとそのAPI定義に参加する


上記の目標のために、想定する通信はスタブリゾルバからリカーシブルリゾルバの通信を想定し、ゾーン転送は考慮しない。中間装置によるブロックを考慮しないことが挙げられている。


HTTP同様DNSでもレイテンシを小さくするため、0-RTTセッション再開、ロスリカバリのサポート、マルチストリームでヘッドオブラインブロッキングの回避の機能について言及がある。

仕様

ALPN識別子

ALPNで使用する識別子として、"qd"を使用する。HTTP同様、ハイフン付きでドラフトバージョンを指定する

ポート番号

具体的なポート番号はTBDとなっているが、DNS over UDPDNS over DTLSとの混用を避けるため53, 853の使用は避けるようだ。サーバはそのポートでリッスンし、クライアントはそのポートにつなぎに行く。

ストリーム

各ストリームがDNSのリクエスト/レスポンスに相当する。クライアントから開始するストリームは、3, 5, ... と続く。


DNS Push NotificationsのようなサーバからPUSHメッセージを送ることも可能で、サーバから開始するストリームは2, 4, ...となる。クライアントはそのストリーム上で応答を返す。

プライバシの問題

DNS Privacy Considerations(RFC7626)で言及されていることは、DSN over TLSの場合と変わらないが、0RTT通信と、セッション再開について言及がある。

0-RTT セッション再開の再送攻撃

観測者は0-RTTを利用した問い合わせを再送することができます。これによりリカーシブルリゾルバから権威リゾルバへのクエリをトリガできます。リカーシブルリゾルバの振る舞いを観測できる場合は、0-RTTの問い合わせを再送させることができもともとの問い合わせを推測リスクが高まります。デフォルトでは無効にしとくことが好ましい。

セッション再開

QUICではIPアドレスが変わってもセッションを維持できるため、それらが同じクライアントだと識別可能です。セッション再開や長いセッションはパフォーマンス上有用だが、クライアントは考慮スべき。

マルチパスQUICにおけるOne Way Latencyの考察

QUICの各仕様のdraft-02が出た一方で、マルチパスQUICに関する考察ドラフトが出ています。


マルチパスQUICはマイルストーン上は2017年の後半に拡張の仕様が出て来る予定ですが、それに先立って考察のドラフトが出ている状況です。今後関連するドラフトも出てくるでしょう。


今回提出されているドラフトは、Huaweiの人らが書かれている「One Way Latency Considerations for Multipath in QUIC」というドラフトです。

One Way Latency Considerations for Multipath in QUIC

"One Way Latency"は既にMultiPath TCPでも議論されている概念であり、そちらでもドラフト(URL)が出ています。


そもそも各通信路は、行きと帰りで同じレイテンシとは限りません。そのため複数経路を使用する場合は、RTTではなく片方向のレイテンシ(One Way Latency)を見て送信する経路を選択すべきということになります。RTTの値は輻輳制御やパケロスの検出に利用されますが、同様に複数経路の場合はOne Way Latencyを見るべきです。またレイテンシ同様、輻輳も片方向には輻輳しているが、反対側は輻輳していないというケースがあります。



たとえば、上記図の用に、クライアント・サーバ間で2経路あった場合。RTTだけみれば上の経路のほうが良いですが、クライアントからサーバに送信する場合は下の経路のほうが低レイテンシということもあるわけです。特に動画のキーフレームや重要なシグナル、ロスしたパケットの再送など急ぐものに関しては下の経路で送信したほうが良いということになります。

OWLの測定

このドラフトでは、OWLの測定としてQUICのAckフレームに含まれるタイムスタンプを利用しますが、絶対値を得る方法と、相対値を得る方法について言及しています。


絶対値は各経路の各方向のOWLを計算します。各エンド間で時刻同期している前提になります。NTPといった別のプロトコルを利用して時刻同期を行えば、タイムスタンプを利用してOWLの絶対値が得られます。


相対値は、OWLの絶対値はわかりませんが、各経路の各方向でどの経路のOWLが小さいのかわかれば経路を選択できるようになります。これは時刻同期は必要なく、Ackフレームに含まれるタイムスタンプより計算されます。

TLSにおける証明書チェーンを圧縮する拡張仕様

GoogleとCloudflareの方による「Transport Layer Security (TLS) Certificate Compression」という、証明書チェーンの圧縮を行う拡張仕様の提案が出ています。


TLSハンドシェイクの大部分は証明書が占めているらしく、サーバ側から送る証明書チェーンをgzipかbrotliで圧縮できるようにする拡張仕様です。


証明書の圧縮についてはGoogle版QUICでは行っており、TLS1.3で圧縮をサポートするかという議論が昨年末にML上でありました( https://www.ietf.org/mail-archive/web/tls/current/msg22065.html )。


複雑性に見合う効果があるかは分からないが、TLS1.3だけに限る必要はないのではないかという意見が出ていたようです。


IETF版QUICにも証明書の圧縮機能が盛り込まれるかもしれませんが、この拡張を取り込む流れになるのかは分からないです。


QUICはUDPですので、サーバ側からの転送量を削減するのはAMP攻撃対策として大事なのではないかと思います。

TLS Certificate Compression

クライアントは証明書の圧縮に対応している場合、ClientHello内でcompress_server_certificates拡張を使って対応してる圧縮アルゴリズムのリストをサーバに送信します。

       enum {
           zlib(0),
           brotli(1),
           (255)
       } CertificateCompressionAlgorithm;

       struct {
           CertificateCompressionAlgorithm algorithms<1..2^8>;
       } CertificateCompressionAlgorithms;


サーバはクライアントが提示したアルゴリズムに対応していた場合、ServerHelloで同じようにどのアルゴリズムを選択したかを返答します。


ServerHelloで使用するアルゴリズムを通知した場合、Certificateメッセージは以下のように圧縮前の長さと圧縮された証明から構成されます。

       struct {
            uint24 uncompressed_length;
            opaque compressed_certificate_message<1..2^24-1>;
       } Certificate;


server_certificate_type 拡張[RFC7250]の場合も、そのメッセージが圧縮されます。cached_info拡張[RFC7924] とは併用できません。


復号されたあとは、Certificateメッセージは圧縮がなかったようにそのまま処理されます。

CSPの仕様に report-sample が追加された

まだ、WIPではあるもののCSPの仕様に "report-sample" と言う機能が追加されました(URL)。


これは、違反したインラインのScriptやStyleの最初の40文字がレポートに追加されます。外部ファイルの場合はレポートされません。昨年から議論がされていましたが、もともとはFirefoxに以前実装されていたscript-sampleプロパティと同等の機能ですが、Styleも対象になります。


これにより、今までレポートは送られてくるもの攻撃なのかそうじゃないのか分からなかったというケースが、多少なりとも少なくなるのではないだろうか。


すでに、Chromeへの実装が進められています(URL)

report-sample

普通のCSPと同様に、HTTPヘッダもしくはmetaタグで report-sampleを指定します。

Content-Security-Policy: script-src 'nonce-abc' 'report-sample'
<meta http-equiv="Content-Security-Policy" content="style-src 'nonce-abc' 'report-sample'">
sample

下記のようなタグが含まれていた場合

<script>alert(1);</script>


このようにレポートにsampleが追加されます

    {
      ...
      "sample": "alert(1);"
    }

HTTP over マルチキャストQUIC とは

Hypertext Transfer Protocol (HTTP) over multicast QUIC」で、マルチキャストのQUIC上でHTTP通信を行う仕様が提案されている。マルチキャストQUICは単方向通信であり、その上でサーバプッシュを行う感じである。



現状QUICの仕様ではIPマルチキャストの利用は想定されていない(もちろん双方向通信)ため、この仕様ではQUICのトランスポートレイヤ、HTTPレイヤを利用しつつも、IPマルチキャストを利用できるように一部の変更(制限)を加えている。


BBC Researchの方が書かれているので、主に動画像配信での利用を考えているんだと思う。


ざっと仕様を読んだのでかいつまんで特徴を紹介する。


また、QUICの仕様はまだ変わる可能性があり、本仕様はそれに併せて変わる旨注意書きがつけられている。

HTTP over マルチキャストQUIC

セッション

マルチキャストQUICでは、単方向通信になる。そのため、コネクションではなくセッションという用語を使用する(コネクションIDではなく、セッションID)。このセッションに参加者が参加する形になる。


セッションのステートは、個々のエンドポイント間で同期されるわけではないが、参加者(送信者も含む)の有無に関して「Idle」、「Half-established」、「Fully-established」、「Finished」の4つの状態が存在する。


Idle状態から開始され、参加者がいなくなるか、明示的に通信を終了することも出来ます。


エンドポイントは他のセッションにマイグレーションすることも出来ます。

セッションの広告

Alt-Svcを用いて、セッションの広告を行います。マルチキャストQUICは単方向通信であるためCryptoハンドシェイクは使用せず、このAlt-SvcにセッションIDやセッションで使用する暗号スイート、暗号化キーが含まれます。受信者は、自身が暗号スイートに対応してるか判断して、セッションに参加するか決めます。また、この時にトランスポートの鍵が提供されます。


プロトコルの識別子は、hqmを使用します。

   Alt-Svc:
       hqm="[ff3e::1234]:2000"; source-address="2001:db8::1"; quic=1;
       session-id=10; session-idle-timeout=60;
       max-concurrent-resources=10; peak-flow-rate=10000;
       cipher-suite=1301; key=4adf1eab9c2a37fd

source-addressは、Source-specific multicast (SSM) に使用される。


また、Alt-Svcはユニキャストの代替サービスも広報出来ます(後述)。

フローコントロール

QUICトランスポートでは、クレジットベースのフロー制御を行っています。しかし、単方向通信では実現不可能であるため、peak-flow-rateというパラメータを使用します。これは、1秒あたりのビット数で表され、Alt-Svcパラメータで通知されます。

送信者はこのレートを超えないようにしなければなりません。しかし、受信者はそれを超えても切断しなくてもよいです。

パケットロスの回復

マルチキャストQUICは単方向通信であるため、ACKフレームは送信者・受信者双方で使用が禁止されます。同様にSTOP_WAITINGフレームも送信できません。


仕様の中では、損失したデータの回復する方法を2つ示しています。

  • Forward Error Correctionを使用する方法。現在IETFの仕様ではFECはオプショナル仕様で、将来仕様策定される予定になっています。
  • ユニキャストで復元する方法。失われたパケットがFECで回復出来ない場合、もしくは閾値を超えて損失した場合は、ユニキャストを用いてデータを取りに行きます。
HTTP2まわり

マルチキャストQUICはHTTP/2のサーバプッシュに依存します。そのため、受信者はサーバプッシュを受け付けられる必要があります。また、マルチキャストQUICは単方向のため、受信者からHEADERSフレームを送ることはありません、そのためサーバからのPUSH_PROMISEフレームの送信は予約されたストリームを参照します


同様に、単方向通信のためにHPACKの動的テーブルを同期するのは不可能です。そのため、動的テーブルは使用できません。


また、単方向通信であるためもちろん受信者からPriorityを指示することも出来ません。

アプリケーションレイヤセキュリティ

HTTPレイヤでコンテンツの完全性と、相手が本当に正しいこと(主に送信者が正しいこと)を確認する事が推奨されます。


RFC3230 Instance Digests in HTTP」を使用し完全性を、「Signing HTTP Messages
」を用いて真正性を担保することを推奨しています。また、再送攻撃については、Dateヘッダを署名対象に含めるように推奨しているが、それ以上については仕様の範囲外としている。


そのほか機密性が必要があれば別途アプリケーションレイヤでの暗号化をするように推奨しているが、鍵の配送については規定していない。

そのほか

Security and Privacy Considerationsとして、セキュリティ(なりすまし、再送攻撃)・プライバシー(広域盗聴など)、DoS攻撃について手厚く書かれています。

たとえば、送信者のなりすましは可能であるため、上記で述べているようなアプリケーションレイヤの対策が推奨されている。

Nginxがstale-while-revalidateに対応した

20170214 追記このコミット(URL)で入った proxy_cache_background_update で、stale-while-revalidateを使わなくてもバックグラウンドでキャッシュ更新するように設定できるようになる模様


このコミット(URL)で、Nginxのproxy_cache機能がstale-while-revalidateとstale-if-errorに対応した。次のNginxのバージョンで使えるようになるだろう。

stale-while-revalidate、stale-if-errorとは

stale-while-revalidateとは、以前「Cache-Controlヘッダのstale-while-revalidateとは」で書いたとおり、Cache-Controlヘッダで指定できる拡張機能である(RFC)。


max-ageでキャッシュが切れたあとに、指定した期間の間それぞれ以下の挙動をする

  • stale-while-revalidate: 一旦キャッシュを使用して、その後にファイルが更新されてないか確認しに行く。まずはキャッシュを使うので待ち時間が少ない
  • stale-if-error: ネットワークエラーがあった場合のみ、キャッシュを使用する


以下のように指定する

Cache-Control: max-age=600, stale-while-revalidate=30

max-age(600sec)後に、30secの間はキャッシュを使用した後にファイルが更新されているか確認し、キャッシュの更新をする。

Nginxの場合

この機能はリバースプロキシ・バックエンドサーバ間でも使用できます。


Nginx側の設定に特殊なことは必要ない。デフォルトでこの機能は有効になっている。

    proxy_cache_path /tmp keys_zone=zone:10m;
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://localhost:8080;
            proxy_cache zone;
...
初回アクセス


初回は普通にリバースプロキシする。バックエンドはHTTPレスポンスにstale-while-revalidateなどを付与する。
Nginxはそのコンテンツをキャッシュする。

stale-while-revalidate、max-age経過後


手元のキャッシュを使用してから、ファイルが更新されてないか確認しに行く。

stale-if-error、max-age経過後


エラーがあった場合のみ手元のキャッシュを使用する。そうでなければいつもどおりに処理する


それぞれ動作確認したところ、上記のように動作していたが。デモが非常にわかりにくいので割愛するorz