2026年6月14日
2026年6月14日
DoT (DNS over TLS) と DNSSEC を併用する設定|BIND 9.18 以降のキャッシュDNSで853番の待ち受けと署名検証を両立する
BIND 9.18 以降は、追加のプロキシなしで DoT(DNS over TLS)の待ち受けができるため、dnssec-validation による署名検証と組み合わせれば「検証済みの結果を暗号化して端末へ返すキャッシュ DNS」を BIND だけで組めます。この記事では、BIND を使っている、または使い慣れている人向けに、tls ブロックと listen-on port 853 の書き方、DNSSEC 検証の有効化、Let's Encrypt 証明書の更新との連携、動作確認と切り分けまでを説明します。Unbound で同じ構成を組む方法は扱わず、BIND 固有の書き方とつまずきどころに絞ります。
構成の考え方
DoT は端末とキャッシュ DNS の間の通信を TLS で暗号化し、DNSSEC はキャッシュ DNS が権威 DNS からの応答の署名を検証します。守る区間が違うので、両方を有効にして初めて「改ざんされていない答えを、盗み見や書き換えをされずに端末へ届ける」形になります。
BIND では次の3つを1つの named.conf の中で設定します。
- **
tlsブロック**:DoT で使う証明書と秘密鍵の場所を名前付きで定義する - **
listen-on port 853 tls 名前**:その証明書で 853 番を待ち受ける - **
dnssec-validation auto**:組み込みのルートのトラストアンカーで署名を検証する
DoT と DoH のネイティブ対応は BIND 9.18 系から安定版に入りました。ディストリビューションのパッケージが 9.16 以前の場合は使えないため、最初に版を確認します(2026年10月現在、9.18 系と 9.20 系がサポート対象として案内されています。最新の状況は ISC のサイトで確認してください)。
named -v
準備:証明書と権限
端末が接続に使うホスト名(例: dns.example.com)の証明書を用意します。社外の端末からも使うなら Let's Encrypt などの公的な証明書、社内専用なら社内 CA でも構いません。
ここで多いつまずきが、BIND の実行ユーザーから証明書が読めないことです。certbot が作る /etc/letsencrypt/live/ 以下は root 以外から読めない権限になっていることが多く、Ubuntu では AppArmor のプロファイルが named の読めるパスを /etc/bind などに制限しています。そのため、証明書を BIND 用のディレクトリへコピーして使う方法が扱いやすくなります。
sudo install -d -m 0750 -o root -g bind /etc/bind/tls
sudo install -m 0644 -o root -g bind /etc/letsencrypt/live/dns.example.com/fullchain.pem /etc/bind/tls/fullchain.pem
sudo install -m 0640 -o root -g bind /etc/letsencrypt/live/dns.example.com/privkey.pem /etc/bind/tls/privkey.pem
グループ名は Debian / Ubuntu では bind、RHEL 系では named が一般的です。証明書は中間証明書を含む fullchain を使います。チェーンが欠けていると、Android の「プライベート DNS」など一部のクライアントが接続に失敗します。
named.conf の設定例
作業の前に、現在の設定ファイルを日付付きでコピーしておきます。
// /etc/bind/named.conf.options(例。アドレスやネットワークは環境に合わせる)
acl "trusted" {
127.0.0.0/8;
::1;
192.0.2.0/24; // DoT を使わせる社内・自宅のネットワーク
};
tls "local-dot" {
cert-file "/etc/bind/tls/fullchain.pem";
key-file "/etc/bind/tls/privkey.pem";
};
options {
directory "/var/cache/bind";
// 平文の 53 番(listen-on を書くと既定の待ち受けが置き換わるので明記する)
listen-on port 53 { 127.0.0.1; 192.0.2.53; };
listen-on-v6 port 53 { ::1; };
// DoT(TCP 853 番)
listen-on port 853 tls "local-dot" { 192.0.2.53; };
listen-on-v6 port 853 tls "local-dot" { 2001:db8::53; };
// キャッシュ DNS として使ってよい範囲を限定する
recursion yes;
allow-query { trusted; };
allow-recursion { trusted; };
allow-query-cache { trusted; };
// DNSSEC 検証(組み込みのルートのトラストアンカーを使う)
dnssec-validation auto;
};
設定のポイントは次のとおりです。
- **
listen-onは置き換えになる。** 853 番の行だけを書くと、53 番の待ち受けが既定の状態から外れることがあります。53 番と 853 番の両方を明記しておくと意図が明確になります。 - オープンリゾルバーにしない。
allow-recursionとallow-query-cacheで利用者を限定します。DoT は TLS で守られていても、誰でも再帰問い合わせができる状態は悪用の対象になります。 - **
dnssec-validation autoを選ぶ。**yesは自分でトラストアンカーを設定する前提の値で、autoは BIND に組み込まれたルートのトラストアンカーを使い、鍵の更新(RFC 5011)にも追従します。古い設定ファイルから引き継いだtrusted-keysなどが残っていないかも確認します。
構文を確認してから反映します。
sudo named-checkconf
sudo systemctl restart named # Debian / Ubuntu ではサービス名が bind9 の別名になっていることがある
sudo ss -lntp | grep -E ':(53|853)\b'
853 番は TCP です。サーバーのファイアウォールとクラウドのセキュリティグループで、trusted に含めたネットワークからの TCP 853 番だけを開けます。
証明書の更新と連携させる
Let's Encrypt の証明書は短い期間で更新されます。更新されたファイルを BIND 用のディレクトリへコピーし、BIND に読み直させる処理を certbot の deploy フックに置いておくと、更新漏れで DoT が止まる事故を防げます。
#!/bin/sh
# /etc/letsencrypt/renewal-hooks/deploy/bind-dot.sh(実行権限を付ける)
# 他の証明書の更新でも呼ばれるため、対象の証明書のときだけ処理する
set -e
case "$RENEWED_LINEAGE" in
*/dns.example.com)
install -m 0644 -o root -g bind "$RENEWED_LINEAGE/fullchain.pem" /etc/bind/tls/fullchain.pem
install -m 0640 -o root -g bind "$RENEWED_LINEAGE/privkey.pem" /etc/bind/tls/privkey.pem
rndc reconfig
;;
esac
rndc reconfig で新しい証明書が読み込まれない版や構成もあるため、更新後に次の「動作確認」の openssl s_client で証明書の有効期限が新しくなったかを確かめます。読み込まれていなければ rndc reload か BIND の再起動に切り替えます。なお、certbot renew --dry-run は既定では deploy フックを実行しません。新しめの certbot には dry-run でもフックを実行させる --run-deploy-hooks オプションがありますが、版によって有無が異なるため certbot --help renew で確認してください。
動作を確認する
DNSSEC の検証
まずサーバー上で平文の 53 番に問い合わせ、検証が働いているかを見ます。
# 署名されたドメイン: flags に ad が付けば検証済み
dig @127.0.0.1 +dnssec isc.org A
# 署名がわざと壊されたテスト用ドメイン: SERVFAIL になれば検証が働いている
dig @127.0.0.1 dnssec-failed.org A
# 検証を無効にして問い合わせると応答が返る(比較用)
dig @127.0.0.1 +cd dnssec-failed.org A
# 組み込みのトラストアンカーの状態
sudo rndc managed-keys status
テスト用ドメインは外部のものなので、状態が変わる可能性があります。想定と違う結果のときは DNSViz などで現状を確かめてください。
DoT の接続と証明書
次に、端末側から DoT で問い合わせます。
# BIND 9.18 以降の dig(+tls で 853 番に TLS 接続する)
dig @192.0.2.53 +tls isc.org A
# 証明書の検証とホスト名の照合まで行う(Knot DNS の kdig)
kdig @192.0.2.53 +tls-ca +tls-hostname=dns.example.com +dnssec isc.org A
# 証明書チェーンと有効期限を直接見る
openssl s_client -connect 192.0.2.53:853 -servername dns.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate
dig +tls は、版やオプションによっては証明書を検証せずに接続します。証明書が正しいかどうかの判断には、kdig +tls-ca か openssl s_client の結果を使います。kdig の結果で ad フラグが付いていれば、「BIND が検証した結果が DoT で届いている」ことまで確かめられます。
端末から使う
Android の「プライベート DNS」はホスト名(dns.example.com)を指定して DoT で接続します。IP アドレスではなくホスト名で指定するため、証明書のホスト名と一致していること、そのホスト名が端末から引けることが条件です。Linux の systemd-resolved など、他のクライアントの設定は各クライアントのドキュメントに従ってください。
うまく動かないときの切り分け
- BIND が起動しない、853 番で待ち受けていない。
journalctl -u namedを見ます。証明書や秘密鍵が読めない(権限・AppArmor)、ファイルのパスの誤り、秘密鍵と証明書の組み合わせ違いが多い原因です。 - 平文の 53 番では引けるが DoT だけ失敗する。 ファイアウォールの TCP 853 番、証明書のホスト名、中間証明書の欠落を順に確認します。853 番を通さないネットワーク(公衆無線 LAN や一部の企業網)からは、そもそも接続できないことがあります。
- **
+cdを付けると引ける。** DNSSEC の検証失敗です。BIND のログに検証失敗の理由が出ます。自分のドメインなら、DS の登録ミスや鍵の更新漏れが典型例です。 - ほぼすべてのドメインで SERVFAIL になる。 サーバーの時刻ずれを疑います。DNSSEC の署名にも TLS の証明書にも有効期間があり、時計がずれると両方が失敗します。
timedatectlで NTP の同期を確認します。
上流へも DoT で転送したい場合、新しい版の BIND では forwarders に tls を指定できます。ただし対応した版や、上流の証明書をどう検証するかの設定は版によって異なるため、使っている版のリファレンスマニュアルで確認してから導入してください。
まとめ
- BIND 9.18 以降なら、
tlsブロックとlisten-on port 853 tlsで DoT を待ち受け、dnssec-validation autoで署名検証と両立できます。 listen-onを書くと既定の待ち受けが置き換わるため、53 番と 853 番の両方を明記します。allow-recursionなどで利用者を限定します。- 証明書は BIND 用のディレクトリへコピーし、certbot の deploy フックで更新と読み直しを自動化します。
- 確認は
dig +dnssecの ad フラグ、テスト用ドメインの SERVFAIL、kdig +tls-caでの DoT 接続の3点で行います。 - 失敗時は、起動ログ → 853 番と証明書 →
+cdでの比較 → 時刻、の順に切り分けます。