2026年6月7日

2026年6月7日

DoT (DNS over TLS) の導入手順|今のキャッシュDNSを変えずに、dnsdist か nginx を前段に置いて853番で提供する

社内や自宅で使っているキャッシュ DNS に DoT(DNS over TLS)を導入するなら、今のリゾルバーはそのままにして、前段に TLS を受ける入口を置く方法が手堅い選択肢です。BIND・Unbound・dnsmasq など、平文の 53 番で動いている既存のリゾルバーを作り直さずに、853 番の DoT を追加できます。この記事では、導入の範囲の決め方、証明書の用意、dnsdist と nginx(stream モジュール)それぞれでの設定、確認、利用者への展開と戻し方までを説明します。

端末側を公開リゾルバーの DoT に切り替える手順は「DoT への切り替え手順」の記事で扱っています。この記事は、DoT を「提供する側」として導入する人向けです。

導入の範囲を決める

何を守るために入れるのか

DoT は、端末からリゾルバーまでの区間を TLS で暗号化します。社内ネットワークの外から使う端末(在宅勤務の PC や外出先のスマートフォン)がある、同じネットワークに信頼しにくい機器がつながる、といった場合に効果があります。リゾルバーから権威 DNS への問い合わせは暗号化されず、応答の正しさの確認は DNSSEC の役割です。DNSSEC の検証を後ろのリゾルバーで有効にしておくと、DoT と役割を分担できます。

誰に使わせるか

最初に、DoT の入口に接続を許す範囲を決めます。インターネット全体に開いたリゾルバー(オープンリゾルバー)は、DNS の増幅攻撃の踏み台になったり、負荷や運用の負担が大きくなったりします。社内の拠点や VPN のアドレス、特定の利用者の固定 IP など、許可する範囲をはっきりさせてから進めます。

構成の選び方

  • dnsdist: PowerDNS が開発している DNS 用のロードバランサーです。DNS の内容を理解するため、接続元ごとの制限や問い合わせ単位のルール、統計が使えます。DoT のほか DoH なども同じ仕組みで追加できます。
  • nginx(stream モジュール): すでに nginx を運用していて、TLS の設定や証明書の更新の手順が整っているなら扱いやすい方法です。ただし nginx は TCP の通信を中継するだけで、DNS の内容は見ません。

どちらの場合も、後ろのリゾルバーから見た接続元は入口のアドレス(127.0.0.1 など)になります。リゾルバー側の接続元による制限は効かなくなるため、アクセスの制限は入口側で行います。

事前に用意するもの

  • DoT のホスト名(例:dns.example.com)と、その A / AAAA レコード
  • そのホスト名の証明書と秘密鍵(中間証明書を含む fullchain)
  • 受信側のファイアウォールとクラウドのセキュリティグループで、許可する範囲からの TCP 853 番を開ける設定
  • 既存のリゾルバーの設定ファイルのバックアップと、可能なら検証用の環境

証明書は、社外の端末も使うなら Let's Encrypt などの公的な認証局のものが扱いやすく、端末側で個別に信頼させる手間がかかりません。DoT 専用のサーバーで Web サーバーを動かしていない場合は、DNS-01 チャレンジで取得すると 80 番を開けずに済みます。社内の端末だけなら、社内 CA の証明書でも構いません。

既存のリゾルバーが TCP の問い合わせを受け付けているかも確かめます。前段からは TCP で問い合わせが届くためです。

dig @127.0.0.1 +tcp wordpress.org +short

方法1:dnsdist で DoT の入口を作る

dnsdist の設定ファイル(Lua 形式)の例です。後ろの既存リゾルバーは 127.0.0.1:53 で動いている前提です。

-- /etc/dnsdist/dnsdist.conf の例
-- 後ろの既存リゾルバー
newServer({address = '127.0.0.1:53'})

-- 平文の待ち受けは、既存リゾルバーとぶつからないポートに明示する
setLocal('127.0.0.1:5300')

-- DoT の待ち受け(TCP 853)
addTLSLocal('0.0.0.0:853', '/etc/dnsdist/tls/fullchain.pem', '/etc/dnsdist/tls/privkey.pem',
  { minTLSVersion = 'tls1.2' })

-- 接続を許す範囲(例は文書用アドレス)
setACL({'127.0.0.0/8', '192.0.2.0/24', '2001:db8::/32'})

つまずきやすい点は次のとおりです。

  • 53 番の取り合い。 dnsdist は平文の待ち受けを書かないと、既定のアドレスで待ち受けようとすることがあります。既存のリゾルバーと同じサーバーで動かす場合は、setLocal でぶつからないアドレスとポートを明示します。
  • **setACL の範囲。** dnsdist の既定の許可範囲はプライベートアドレスなどに限られています。外から使わせたい範囲は明示的に書き、広げすぎないようにします。
  • 鍵の権限。 秘密鍵は dnsdist の実行ユーザーだけが読めるようにします。
sudo dnsdist --check-config
sudo systemctl restart dnsdist
sudo ss -lntp | grep ':853'

dnsdist の機能はビルドの内容とバージョンによって変わります。DoT が使えるかは dnsdist --version の出力で確かめ、オプション名は手元のバージョンのドキュメントで確認してください(2026年10月現在)。

方法2:nginx の stream モジュールで DoT の入口を作る

nginx で TCP を中継するには stream モジュールが必要です。Debian・Ubuntu では別パッケージになっていることがあり、nginx -V の出力や、モジュールの読み込み設定で有無を確かめます。

# /etc/nginx/nginx.conf の最上位(http ブロックの外)に書く
stream {
    upstream dns_backend {
        server 127.0.0.1:53;
    }

    server {
        listen 853 ssl;
        listen [::]:853 ssl;

        ssl_certificate     /etc/nginx/tls/dns-fullchain.pem;
        ssl_certificate_key /etc/nginx/tls/dns-privkey.pem;
        ssl_protocols       TLSv1.2 TLSv1.3;

        # 接続を許す範囲
        allow 192.0.2.0/24;
        allow 2001:db8::/32;
        deny  all;

        proxy_pass    dns_backend;
        proxy_timeout 30s;
    }
}

いちばん多い失敗は、stream ブロックを書く場所です。Debian・Ubuntu の conf.d や sites-enabled は http ブロックの中で読み込まれるため、そこに stream を書くと構文エラーになります。nginx.conf の最上位に書くか、最上位で別のディレクトリを include します。

sudo nginx -t && sudo systemctl reload nginx

proxy_timeout は、やり取りが無い接続を切るまでの時間です。短すぎると端末が接続を使い回せず、問い合わせのたびに TLS の接続をやり直すことになります。

動作を確かめる

許可した範囲の端末から、証明書の検証を含めて確かめます。

# 証明書の検証結果(0 (ok) になること)
openssl s_client -connect dns.example.com:853 -servername dns.example.com \
  -verify_hostname dns.example.com </dev/null 2>/dev/null | grep 'Verify return code'

# DoT で名前解決できるか(kdig は knot-dnsutils に含まれる)
kdig @dns.example.com +tls-ca +tls-hostname=dns.example.com wordpress.org

許可していない接続元から試して、つながらないことも確かめておきます。外部から誰でも使える状態になっていないかの確認は、導入の確認と同じくらい大切です。

利用者へ展開する

いきなり全員を切り替えるより、段階を踏むほうが問題を見つけやすくなります。

  1. 管理者の端末で試す。 systemd-resolved なら DNS=IPアドレス#dns.example.com と DNSOverTLS=opportunistic で始め、問題がなければ yes に変えます。Android ならプライベート DNS にホスト名を入れます。
  2. 一部の利用者に広げる。 dnsdist なら統計、nginx ならアクセスログで接続数を見て、負荷や失敗が増えていないかを確かめます。
  3. 全体に広げる。 移行の期間は平文の 53 番も残しておき、DoT に対応しない機器の扱いを決めてから閉じるかどうかを判断します。

利用者が増えると TCP の接続数も増えます。前段と後ろのリゾルバーの両方で、接続数やファイルディスクリプタの上限に余裕があるかを確かめておきます。また、証明書の自動更新の後に dnsdist や nginx が新しい証明書を読み込むよう、certbot の deploy フックなどで再読み込みを設定しておきます。

DoT の入口のログには、接続元の IP アドレスが残ります。他人に使わせる場合は、何をどれだけの期間残すかを決め、必要に応じて利用者に説明します。

戻し方

  • dnsdist: sudo systemctl stop dnsdist で DoT の入口だけを止められます。後ろのリゾルバーには手を入れていないため、平文の 53 番はそのまま使えます。
  • nginx: 追加した stream ブロックを外して nginx -t の後に再読み込みします。
  • 端末: DoT の設定を外すか、日和見の設定に戻します。

まとめ

  • 既存のキャッシュ DNS を変えずに DoT を導入するには、dnsdist か nginx の stream モジュールを前段に置き、853 番で TLS を受けて後ろのリゾルバーへ中継します。
  • 後ろのリゾルバーから見た接続元は入口のアドレスになるため、アクセスの制限は入口側(setACL や allow/deny)で行い、オープンリゾルバーにしないようにします。
  • dnsdist は 53 番の取り合い、nginx は stream ブロックを書く場所がつまずきやすい点です。
  • 確認は openssl s_client と kdig +tls-ca で行い、展開は管理者の端末、一部の利用者、全体の順に進めます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲