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

NginxのstreamモジュールがDTLSに対応した(パッチ)

NginxのL4レイヤのProxy機能である、streamモジュールのDTLS対応パッチが公開されていたので試す。
(まだ検証段階で、フィードバック募集中らしい)

簡単に言うと、以下のようににDTLS -> Plain UDPや、その逆にPlain UDP -> DTLSへのProxyが出来る。

DTLS -> Plain UDP

f:id:ASnoKaze:20170524014622p:plain

Plain UDP -> DTLS

f:id:ASnoKaze:20170524022625p:plain

Plain UDP -> DTLS -> Plain UDP

2つ組み合わせれば、中間だけDTLSで暗号化することも出来る
f:id:ASnoKaze:20170524014630p:plain

ビルド

今回は、Ubuntu Trustyでビルドする

sudo apt-get install libpcre3-dev

wget http://nginx.org/download/nginx-1.13.0.tar.gz
wget http://nginx.org/patches/dtls/nginx-1.13.0-dtls-experimental.diff

cd nginx-1.13.0
patch -p1 -i ../nginx-1.13.0-dtls-experimental.diff

./configure --with-stream --with-stream_ssl_module --with-debug
make

sudo make install

いつも通り、鍵も準備

openssl genrsa 2048 > server.key
openssl req -new -key server.key > server.csr
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt

DTLS -> Plain UDP

設定
#/usr/local/nginx/conf/nginx.conf
stream {
    # please enable debug log
    error_log logs/error.log debug;

    server {
        # add 'udp' and 'ssl' simultaneously to the listen directive
        listen 127.0.0.1:4443 udp ssl;

        # enable DTLSv1 or DTLSv1.2 or both protocols
        ssl_protocols DTLSv1;

        # set up other SSL options as usually
        ssl_certificate server.crt;
        ssl_certificate_key server.key;

        proxy_pass 127.0.0.1:8080;
    }
}
動作確認

クライアント

$ openssl s_client -dtls1 -connect 127.0.0.1:4443
test

バックエンド

$ nc -ul 8080
test

Plain UDP -> DTLS

設定
stream {
    server {
        listen 127.0.0.1:5555 udp;

        # enable SSL to proxy
        proxy_ssl on;
        # enable DTLSv1 or DTLSv1.2 or both protocols
        proxy_ssl_protocols DTLSv1;

        # set up other proxy SSL options as usually
        proxy_ssl_certificate  server.crt;
        proxy_ssl_certificate_key server.key;

        # the backend is a DTLS server
        proxy_pass 127.0.0.1:4433;
    }

}
動作確認

クライアント

$ echo -en "hello"| nc  -u localhost 5555

バックエンド

$ openssl s_server -cert server.crt -key server.key -dtls1 -accept 4433
ACCEPT
...(略)
hello
その他

省くが、Plain UDP -> DTLS -> Plain UDPの変換も上記の設定の組み合わせで実現できた。
使える機会があるかわからないが、普段使ってるNginxでDTLSが受けられるのは結構楽そうだ

RFC8174「RFC 2119のキーワードにおける大文字と小文字の曖昧性」

RFCやInternet-Draftを読んだことある人であれば、文中にMUSTやSHOULDといった大文字のキーワードが用いられてるのを見たことあるかと思います。

たとえば、このように

An endpoint MUST treat this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR.


これらのキーワードはプロトコルの仕様の曖昧性を無くすために 「RFC 2119 Key words for use in RFCs to Indicate Requirement Levels」で定義されています。

しかし、微妙な曖昧性があるためRFC 8174で更新されました。

RFC8174 Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words

「MUST」「SHOULD」「MAY」といった要請の程度を示すキーワードを定義するRFC 2119ですが、その文章にはこう書かれています。

「These words are often capitalized」
(これらの語句は、よく大文字になっています) IPAの翻訳より

このoftenによる微妙な表現のため、"must"や"should"といった小文字の場合にどう解釈するか微妙な曖昧性があります。

RFC8174では下記2点が明記されるようになりました

  • これらの語句は全て大文字の場合のみ、この文書で定義された特別な意味をもつ
  • 小文字の場合は通常の英語の意味をもち、この文書の影響はありません

また、絶対にこれらの語句を使用すべきというわけではない旨が追記されたほか
別の文書から参照する時の例が更新されています

      The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
      NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
      "MAY", and "OPTIONAL" in this document are to be interpreted as
      described in BCP 14 [RFC2119] [RFC8174] when, and only when, they
      appear in all capitals, as shown here.

TCP Fast Openの闇と、Kernelの緩和コミット

TCP Fast Open

TCP Fast Openと呼ばれる技術があり、RFC 7413として標準化されている。

このTCP Fast Openを使うと、一度コネクションを貼った相手とは、TCPの3ウェイハンドシェイク中にデータを送受信できるようになる。クライアントからSYNとともにデータを送信することで、実際にデータを送受信開始するまでの待ち時間が短縮できる。

Linuxではすでにクライアント/サーバ両方でTCP Fast Openを使用できる。

TCP Fast Openの闇

しかし、数年前よりこのTCP Fast Openには一部のネットワークで奇妙な振る舞いをすることが知られている。Appleの人が実際にデプロイした時に見つけたもので、IETFやNANOGにて報告されており、その時の資料は下記のとおりである

(0.1%の環境で発生したと発表時の質疑にもある)

一部を抜粋すると

TCP Fast OpenのSYN, SYN/ACKは通るが最後のACKがブロックされる
f:id:ASnoKaze:20170509215524p:plain

3ウェイハンドシェイクは完了するが、片方向のみデータ送信に失敗する
f:id:ASnoKaze:20170509215604p:plain

この状態になると、OS的にはハンドシェイクが完了しておりタイムアウトを待ち、その後アプリケーションなどにより再送信されるという繰り返しになりうる(フォールバックされない)。

この問題はクライアントの居るネットワークのファイアウォールやIDSが原因であり、単純にはサーバやクライアントでTCP Fast Openを無効にするという対応をすることになる。

Kernelでの緩和対応

上記問題を緩和するために、Kernelに「net/tcp_fastopen: Disable active side TFO in certain scenarios」というコミットが入った。

基本的には、クライアントサイドにてTCP Fast Openを使ってる時に怪しい挙動があった場合は、globalにTCP Fast Openを無効にする。最初はデフォルトで1時間無効化し、次に失敗したときは2時間と倍々で時間が長くなっていく仕組みになっている。通信に成功した場合は、待ち時間は最初の1時間にリセットされる。待ち時間は、tcp_fastopen_blackhole_timeout_secで設定することも出来る。

globalに無効になるのは、クライアント側のネットワークが原因であることが主なので、全体として向こうにしたほうが良いということだろう。

また怪しい挙動とは

  • クライアント側でTCP Fast Openを利用しており、データが無く、順序が正しくないRSTを受信した時
  • クライアント側でTCP Fast Openを利用しており、順序が正しくないFINを受信した時


コメントを読む限りサーバ側では現状特別なハンドルはしないようだ。大きいサービスだと、TCP Fast Openを有効にするには上記コミットが普及してからの方が良いのだろうか...(TCP Fast Openが悪いってわけじゃないんですけど)

hxxp URIスキームの仕様化

追記 20170510
draft-01 より、hxxpsも予約されました
The "hxxp" and "hxxps" URI Schemes


hxxpの背景

hxxp URIを定義する「The "hxxp" URI Scheme」という仕様が提案されています。

hxxp://... は、例えばセキュリティの話をする際にURLがリンクとして解釈されたり、ブラウザやメーラによって解釈・処理されないようにするために使用されています。

この仕様では、将来的にアプリケーションでこのhxxp:// を使用できないように予約します。

hxxpの定義

hxxp URIによって参照されるリソースは、アプリケーション等によって自動的にパース・処理されないことを意味します。

このリソースは、セキュリティの専門家によって利用されなければなりません(MUST)。

そのた

また、アプリケーション開発者はhxxpスキームURIをHTMLのアンカーやタグで使用しないように推奨されています。

Cookieの仕様改定版、RFC6265bisの議論

Cookieの仕様と拡張仕様

HTTPのCookieの仕様は RFC 6265 - HTTP State Management Mechanism で定義されております。

2015年頃より、IETFのHTTPbisワーキンググループではCookieのセキュリティを向上させる目的で拡張仕様が3つほど議論されていました。

動作確認用のページもあるようです http://rfc6265.biz/tests/

IETF98でのフィードバック

それぞれの拡張仕様はChromeFirefoxで実装が進められており、先月シカゴで行われたIETF98では各拡張仕様の提案者でもあるGoogleのMike West氏よりそれぞれの使用状況についてのフィードバックがありました。短いですが議事録より確認できます。

  • Cookie Prefixes
    • Googleでは、ChromeにおいてSet-Cookieヘッダで__Host-が0.004%, __Secure-が0.00001%観測されているとのこと
  • Same-site cookies
    • 利用率は0.01%
  • Strict secure
    • 沢山のバグレポートがあった

rfc6265bis

上記3つの拡張仕様はそれぞれを標準化するのではなく、RFC 6265の改訂版である6265bisに組み込む形で標準化されることがMLで話されておりましたが(URL)、4/25に rfc6265bisのdraft01 が提出されました。

RFC 6265からの主な変更点は下記のとおりです

  • エラッタの修正(URL)
  • Cookie2とSet-Cookie2をIANA Considerationsから削除
  • Cookie Prefixes の仕様の取り込み
  • Deprecate modification of 'secure' cookies from non-secure origins の仕様取り込み

Same-site cookiesについては、AuthorがML上で言及しているとおり(URL)、もう少し作業があるようだが、7月に行われるIETF98までに議論すべきものが出て来る形になりそうだ。

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

追記 20170510
この提案仕様は、draft01でauthor自ら廃案とされました。


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