ブログ | NGINX

NGINX を DoT または DoH ゲートウェイとして使用する

NGINX-F5 水平黒タイプ RGB の一部
マーク・ボディントン サムネイル
マーク・ボディントン
2022年2月22日公開

現在、ドメイン ネーム システム (DNS) をめぐって多くの議論が交わされており、 36 年の歴史を持つこのプロトコルに大規模な変更が提案されています。 インターネットの名前サービスはARPANETに起源を持ち、開始以来、下位互換性が損なわれたことはありません。 しかし、DNS トランスポート メカニズムを変更する新しい提案により、状況は変わる可能性があります。

この記事では、DNS を保護するための 2 つの新しいテクノロジー、DNS over TLS (DoT) と DNS over HTTPS (DoH) について説明し、NGINX Open Source と NGINX Plus を使用してそれらを実装する方法を示します。

[編集者– この投稿は、NGINX JavaScript モジュールの使用例を探るいくつかの投稿のうちの 1 つです。 完全なリストについては、 「NGINX JavaScript モジュールの使用例」を参照してください。

この投稿のコードは、 js_インポート ディレクティブは廃止された js_include 指令の NGINX プラス R23<.htmla> そしてその後。 詳細については、 NGINX JavaScript モジュールのリファレンス ドキュメントを参照してください。構成例のセクションに、NGINX 構成と JavaScript ファイルの正しい構文が示されています。

DNSの歴史

DNSは、高等研究計画局(ARPA)の初期のインターネットのためのネーミングサービスを作成する2番目の試みであり、最初の試みは1979年にジョン・ポステルによって公開されたインターネットネームサーバープロトコルでした。 IEN-116。 DNS は階層的に設計されており、ホスト名をゾーンに分散し、多くの個別の機関によって管理するための構造を提供します。 DNSの最初のRFCは1983年に発行されました(RFC882そして883) であり、長年にわたっていくつかの拡張が行われてきましたが、当時定義された標準に従って作成されたクライアントは今日でも動作します。

では、なぜ今プロトコルを変更するのでしょうか? これは明らかに意図したとおりに機能しており、DNS パケットにバージョン番号を含めなかったほど正しいと確信していた作者の自信を正当化しています。このような主張ができるプロトコルは他にあまり思い浮かびません。 DNS は、ほとんどのプロトコルがクリアテキストで、多くの場合 7 ビット ASCIIであった、もっと無邪気な時代に考案されましたが、今日のインターネットは 1980 年代の ARPANET よりもはるかに恐ろしい場所になっています。 現在、ほとんどのプロトコルは暗号化と検証の目的でトランスポート層セキュリティ (TLS) を採用しています。 DNS の批評家は、追加のセキュリティ保護がかなり遅れていると主張しています。

したがって、DNS がインターネットに名前サービス プロトコルを提供する 2 番目の試みであったのと同様に、DNS over TLS (DoT) と DNS over HTTPS (DoH) は、DNS プロトコルを保護する 2 番目の試みとして登場しています。 最初の試みはDNSSECと呼ばれる拡張機能であり、ほとんどのトップレベルドメイン (TLD) が DNSSEC を利用しています。ただし、DNSSEC は DNS パケットで運ばれるデータを暗号化することを意図したものではなく、データが改ざんされていないことの検証のみを提供します。 DoT と DoH は、DNS を TLS トンネル内にラップするプロトコル拡張であり、採用されると 36 年間の下位互換性が終了します。

DoT と DoH の詳細

DoT は、大抵は賢明な拡張機能として認識されていると思います。 すでに Internet Assigned Numbers Authority (IANA) によって独自のポート番号 (TCP/853) が割り当てられており、TCP DNS パケットを TLS 暗号化トンネル内にラップするだけです。 これまでにも多くのプロトコルがこれを実行してきました。 HTTPS は TLS トンネル内の HTTP であり、SMTPS、IMAPS、および LDAPS はそれらのプロトコルのセキュリティ保護されたバージョンです。 DNS は常に UDP (または場合によっては TCP) をトランスポート プロトコルとして使用しているため、TLS ラッパーを追加しても大きな変更にはなりません。

一方、DoH は、もう少し議論の余地があります。 DoH は DNS パケットを取得し、それを HTTP GETまたはPOSTリクエスト内にラップし、HTTPS 接続を介して HTTP/2 以上を使用して送信します。 これは事実上、他の HTTPS 接続とまったく同じように見え、企業やサービス プロバイダーがどのようなリクエストが行われているかを確認することは不可能です。 Mozilla やその他の支持者は、この方法により、ユーザーが訪問するサイトを詮索好きな目から守ってプライバシーを確保することで、ユーザーのプライバシーが向上すると主張している。

しかし、それは必ずしも真実ではありません。 DoH の批評家は、ブラウザが最終的に DoH を使用して検索したホストに接続するときに、リクエストがほぼ確実に TLS Server Name Indication (SNI) 拡張機能を使用するため、DoH は DNS のTorではないと指摘しています。これにはホスト名が含まれており、クリアテキストで送信されます。 さらに、ブラウザが Online Certificate Status Protocol (OSCP) を使用してサーバーの証明書を検証しようとする場合、そのプロセスもクリアテキストで実行される可能性が高くなります。 したがって、DNS ルックアップを監視できるユーザーは、接続の SNI や OCSP 検証の証明書名を読み取ることもできます。

多くの人にとって、DoH の最大の問題は、ブラウザ ベンダーが、ユーザーによる DoH リクエストがデフォルトで送信される DNS サーバーを選択していることです (たとえば、米国の Firefox ユーザーの場合、DNS サーバーはCloudflareに属しています)。 DNS サーバーの運営者は、ユーザーの IP アドレスと、ユーザーがリクエストを送信したサイトのドメイン名を確認できます。 大したことではないように思えるかもしれませんが、イリノイ大学の研究者は、Web ページの要素に対するリクエストの宛先アドレス、いわゆるページ ロード フィンガープリントだけから、その人がどの Web サイトを訪問したかを推測できることを発見しました。 その情報は「ユーザーをプロファイリングし、広告のターゲットにする」ために使用できます。

NGINX はどのように役立ちますか?

DoT と DoH は本質的に悪質なものではなく、ユーザーのプライバシーを向上させるユースケースもあります。 ただし、パブリックで集中化された DoH サービスはユーザーのプライバシーに悪影響を及ぼすというコンセンサスが高まっており、絶対に使用を避けることをお勧めします。

いずれにしても、サイトやアプリでは独自の DNS ゾーン(パブリック、プライベート、スプリット ホライズン)を管理することになります。 ある時点で、独自の DoT または DoH サービスを実行したいと考えるかもしれません。 ここで NGINX が役立ちます。

DoT が提供するプライバシー強化は DNS セキュリティに大きな利点をもたらしますが、現在の DNS サーバーが DoT をサポートしていない場合はどうなるでしょうか? NGINX は、DoT と標準 DNS の間にゲートウェイを提供することで、ここで役立ちます。

あるいは、DoT ポートがブロックされる可能性がある場合に、DoH のファイアウォール破壊能力が役立つかもしれません。 ここでも、NGINX はDoH から DoT/DNS へのゲートウェイを提供することで役立ちます。

シンプルなDoT-DNSゲートウェイの導入

NGINX Stream (TCP/UDP) モジュールは SSL ターミネーションをサポートしているため、DoT サービスのセットアップは実に簡単です。 わずか数行の NGINX 設定で、シンプルな DoT ゲートウェイを作成できます。

DNS サーバー用のアップストリームブロックと、TLS 終了用のサーバーブロックが必要です。

もちろん、逆に、受信した DNS 要求を上流の DoT サーバーに転送することもできます。 ただし、ほとんどの DNS トラフィックは UDP であり、NGINX は DoT と TCP ベースの DNS などの他の TCP サービス間でのみ変換できるため、これはあまり役に立ちません。

シンプルなDoH-DNSゲートウェイ

DoT ゲートウェイと比較すると、シンプルな DoH ゲートウェイの構成は少し複雑です。 HTTPS サービスと Stream サービスの両方が必要であり、JavaScript コードとNGINX JavaScript モジュール <.htmla> (njs) を使用して 2 つのプロトコル間の変換を行います。 最も単純な構成は次のとおりです。

この構成では、パケットを DNS サービスに送信するために必要な最小限の処理が実行されます。 このユースケースでは、アップストリーム DNS サーバーがその他のフィルタリング、ログ記録、またはセキュリティ機能を実行することを前提としています。

この構成で使用される JavaScript スクリプト ( nginx_stream.js ) には、さまざまな DNS ライブラリ モジュール ファイルが含まれています。 dns.jsモジュール内では、 dns_decode_level変数によって DNS パケットに対して実行される処理の量が決まります。 DNS パケットを処理すると、明らかにパフォーマンスが低下します。 上記のような設定を使用している場合は、 dns_decode_level0

より高度なDoHゲートウェイ

NGINX では HTTP に非常に長けているため、NGINX を単純な DoH ゲートウェイとしてのみ使用するのは機会の無駄であると考えています。

ここで使用される JavaScript コードは、DNS パケットの完全または部分的なデコードを実行するように設定できます。 これにより、DNS 応答の最小 TTL に基づいて Expires ヘッダーとCache-Controlヘッダーが設定された DoH クエリの HTTP コンテンツ キャッシュを構築できるようになります。

追加の接続最適化とコンテンツのキャッシュおよびログ記録のサポートを備えた、より完全な例を以下に示します。

NGINX Plus 機能を使用した高度な DNS フィルター

NGINX Plus サブスクリプションをお持ちの場合は、上記の例を、アクティブ ヘルス チェックや高可用性などのより高度な NGINX Plus 機能と組み合わせたり、キャッシュされた DoH 応答を管理するためにキャッシュ パージ API を使用したりすることもできます。

NGINX Plus キー値ストアを使用して、悪意のあるドメインへのアクセスを効果的に防止する DNS 応答を返すことで、悪意のあるドメインからユーザーを保護する DNS フィルタリング システムを構築することもできます。 RESTful NGINX Plus APIを使用して、キー値ストアの内容を動的に管理します。

悪意のあるドメインを「ブロック」と「ブラックホール」の 2 つのカテゴリに定義します。 DNS フィルタリング システムは、ドメインに関する DNS クエリをそのカテゴリに応じて異なる方法で処理します。

  • ブロックされたドメインの場合、 NXDOMAIN応答を返します(ドメインが存在しないことを示します)
  • ブラックホールドメインの場合、 Aレコードの要求に対しては0.0.0.0の単一のDNS Aレコードを返し、 AAAA (IPv6)レコードの要求に対しては::の単一のAAAAレコードを返します。その他のレコードタイプの場合、応答はゼロです。

DNS フィルターはリクエストを受信すると、まずクエリされた FQDN と正確に一致するキーをチェックします。 見つかった場合は、関連付けられた値で指定されたとおりにリクエストを「スクラブ」(ブロックまたはブラックホール化)します。 完全に一致するものがない場合、2 つのリスト(ブロックされたドメインのリストとブラックホール化されたドメインのリスト)でドメイン名を検索し、一致するものがあれば適切な方法でリクエストをスクラブします。

照会されたドメインが悪意のあるものではない場合は、システムは通常の処理のためにそれを Google DNS サーバーに転送します。

キーバリューストアの設定

NGINX Plus キー値ストアの 2 種類のエントリで、悪意があるとみなされるドメインを定義します。

  • 完全修飾ドメイン名 (FQDN) の個別のエントリ。それぞれにブロックまたはブラックホールの値があります。
  • 2 つのドメイン リスト。キーはblocked_domainsblackholed_domainsで、それぞれがコンマ区切り値 (CSV) 形式のドメイン リストにマッピングされています。

キー値ストアの次の構成は、ストリームコンテキストに含まれます。 keyval_zoneディレクティブは、 dns_configと呼ばれるキー値ストアにメモリ ブロックを割り当てます。最初のkeyvalディレクティブは、設定されている一致する FQDN キー値ペアをロードし、2 番目と 3 番目はドメインの 2 つのリストを定義します。

次に、 NGINX Plus API を使用して、明示的にスクラブする任意の FQDN キーに値blockedまたはblackhole を割り当てたり、 blocked_domainsまたはblackhole_domainsキーに関連付けられた CSV 形式のリストを変更したりすることができます。 正確な FQDN を変更または削除したり、いずれかのリスト内のドメイン セットをいつでも変更することができ、DNS フィルターは変更内容に応じて即座に更新されます。

リクエストを処理する上流サーバーの選択

次の設定は、以下のDNS クエリをリッスンするサーバーの設定で定義されているサーバーブロック内のjs_prereadディレクティブによって設定される$dns_response変数をロードします。 呼び出された JavaScript コードがリクエストをスクラブする必要があると判断すると、変数を必要に応じてblockedまたはblackholeに設定します。

マップディレクティブを使用して、 $dns_response変数の値を$upstream_pool変数に割り当て、以下のアップストリーム サーバーの定義のどのアップストリーム グループがリクエストを処理するかを制御します。 悪意のないドメインに対するクエリはデフォルトの Google DNS サーバーに転送されますが、ブロックされたドメインやブラックホール化されたドメインに対するクエリは、それらのカテゴリのアップストリーム サーバーによって処理されます。

アップストリームサーバーの定義

この構成は、ブロックされたドメイン、ブラックホール ドメイン、悪意のないドメインのリクエストをそれぞれ処理する、アップストリーム サーバーのブロックされたグループ、ブラックホール グループ、およびGoogleグループを定義します。

DNSクエリをリッスンするサーバーの構成

ここでは、受信 DNS 要求をリッスンするストリームコンテキスト内のサーバーを定義します。 js_prereadディレクティブは、DNS パケットをデコードし、パケットのNAMEフィールドからドメイン名を取得し、キー値ストアでドメインを検索する Javascript コードを呼び出します。 ドメイン名が FQDN キーと一致するか、 blocked_domainsまたはblackhole_domainsリストのいずれかのドメインのゾーン内にある場合、ドメイン名はスクラブされます。 そうでない場合、解決のために Google DNS サーバーに送信されることになります。

これでほぼ完了です。最後に、ブラックホール化された応答またはブロックされた応答のいずれかでクエリに応答する最後のサーバーブロックを追加するだけです。

このブロック内のサーバーは、そのすぐ上にある実際の DNS サーバーとほぼ同じですが、主な違いは、これらのサーバーが要求を上流の DNS サーバー グループに転送するのではなく、クライアントに応答パケットを送信することです。 JavaScript コードは、ポート 9953 または 9853 から実行されていることを検知し、パケットをブロックする必要があることを示すフラグを設定する代わりに、実際の応答パケットを$dns_responseに入力します。 以上です。

もちろん、このフィルタリングを DoT および DoH サービスに適用することもできますが、シンプルさを保つために標準の DNS を使用しました。 DNS フィルタリングと DoH ゲートウェイの統合は、読者の課題として残しておきます。

フィルターのテスト

以前、 NGINX Plus API を使用して、2 つの FQDN のエントリと、いくつかのドメインを 2 つのドメイン リストに追加しました。

$ curl -s http://localhost:8080/api/5/stream/keyvals/dns_config | jq { "www.some.bad.host": "blocked", "www.some.other.host": "blackhole", "blocked_domains": "bar.com,baz.com", "blackhole_domains": "foo.com,nginx.com" }

したがって、 www.foo.com (ブラックホール化されたfoo.comドメイン内のホスト) の解決を要求すると、IP アドレス 0.0.0.0 のAレコードが取得されます。

$ dig @localhost www.foo.com ; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> @localhost www.foo.com ; (1 台のサーバーが見つかりました) ;; グローバル オプション: +mcd ;; 回答を取得しました: ;; ->>HEADER,,- オペコード: クエリ、ステータス: NOERROR、ID: 58558 ;; フラグ: qr aa rd ad; クエリ: 1、答え: 1、権限: 0、追加: 0 ;; 警告: 再帰が要求されましたが利用できません ;; 質問セクション: ; www.foo.com.                  ;; 回答セクション: www.foo.com。           300 IN A 0.0.0.0 ;; クエリ時間: 0 ミリ秒 ;; サーバー: 172.0.0.1#53(126.0.0.1) ;; 場合: 2019年12月2日月曜日14:31:35 UTC ;; MSG SIZEW 受信: 45

コードへのアクセス

DOH と DOT のファイルは私のGitHub リポジトリで入手できます:

  • njs.d/dnsフォルダ内の JavaScript コード
  • サンプルフォルダ内のNGINX設定

NGINX Plus を試すには、今すぐ30 日間の無料トライアルを開始するか、お問い合わせの上、使用事例についてご相談ください


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"