2026年6月9日
2026年6月9日
DNSSEC と DoH (DNS over HTTPS) を併用する設定|検証をどこで行うかを決め、Unbound と dnscrypt-proxy で手元検証する構成と確認方法
DNSSEC と DoH を併用するときに最初に決めるのは、「DNSSEC の署名検証をどこで行うか」です。DoH の接続先の公開リゾルバーに任せるか、手元のリゾルバーで検証して上流へは DoH で問い合わせるかの2通りがあり、どちらでも DoH の JSON API や dig で検証結果を確かめられます。この記事では、両方の構成の違いと、Unbound と dnscrypt-proxy を組み合わせて手元で検証する設定例、自分のドメインの DNSSEC が DoH 利用者にも正しく届いているかの確認方法を説明します。
DoH と DNSSEC の役割分担
DoH は、端末からリゾルバーまでの問い合わせを HTTPS で暗号化します。DNSSEC は、権威 DNS が付けた署名をリゾルバーが検証し、応答が改ざんされていないことを確かめます。守る区間も目的も違うため、どちらか一方で他方の代わりにはなりません。
ここで押さえておきたいのは、ブラウザや OS の DoH 機能は、ふつう DNSSEC の検証を自分では行わないことです。検証は DoH の接続先のリゾルバーが行い、端末はその結果を受け取るだけです。したがって次の2点が、併用の質を決めます。
- 接続先のリゾルバーが DNSSEC の検証をしているか
- 検証結果が端末まで改ざんされずに届くか(DoH の暗号化がここを守る)
構成を選ぶ
構成A:DoH の接続先に検証を任せる
ブラウザや OS の DoH 設定で、DNSSEC を検証する公開リゾルバーを指定する方法です。Cloudflare(1.1.1.1)、Google Public DNS、Quad9 などの主要な公開リゾルバーは DNSSEC を検証すると案内しています(2026年10月現在。方針は変わることがあるので各事業者のドキュメントで確認してください)。設定は簡単ですが、検証するかどうかは事業者の方針次第で、検証結果を手元で確かめる手段も限られます。
構成B:手元で検証し、上流へは DoH で問い合わせる
自分の PC やサーバー、社内のキャッシュ DNS で DNSSEC を検証し、上流への問い合わせだけを DoH で暗号化する方法です。検証を自分の管理下に置けるのが利点です。Unbound は上流への転送で DoT には対応していますが、DoH での転送には対応していません。そこで、DoH での問い合わせを代わりに行うプロキシ(ここでは dnscrypt-proxy)を間に挟みます。
端末 → Unbound(127.0.0.1:53、DNSSEC を検証)→ dnscrypt-proxy(127.0.0.1:5053)→ DoH → 公開リゾルバー
以下では構成B の設定例を示します。作業の前に既存の設定ファイルを控え、端末の DNS を切り替える前に検証用の環境で動かしてください。
構成B の設定例
dnscrypt-proxy を DoH のプロキシとして設定する
dnscrypt-proxy は、DNSCrypt と DoH に対応したオープンソースの DNS プロキシで、多くのディストリビューションでパッケージが提供されています。設定ファイル dnscrypt-proxy.toml の主な項目は次のとおりです(項目の一部抜粋。設定ファイルのサンプルに各項目の説明があります)。
# dnscrypt-proxy.toml(例。Unbound からだけ使うのでループバックの別ポートで待ち受ける)
listen_addresses = ['127.0.0.1:5053']
# DoH のサーバーだけを使う
dnscrypt_servers = false
doh_servers = true
# DNSSEC に対応しているとされるサーバーだけを選ぶ
require_dnssec = true
require_nolog = true
# 使うサーバーを明示する場合(名前は公開リストに載っているものから選ぶ)
# server_names = ['cloudflare', 'google']
require_dnssec は「DNSSEC に対応していると申告しているサーバーを選ぶ」ための条件で、dnscrypt-proxy 自身が署名を検証するわけではありません。検証はこの後の Unbound で行います。
dnscrypt-proxy を起動したら、単体で応答するかを確かめます。
dig @127.0.0.1 -p 5053 +dnssec isc.org A
応答に RRSIG レコードが含まれていれば、署名データが上流から届いています。署名データが届かないと、手前の Unbound は検証ができません。
Unbound で検証し、dnscrypt-proxy へ転送する
# /etc/unbound/unbound.conf.d/dnssec-doh.conf(例)
server:
interface: 127.0.0.1
access-control: 127.0.0.0/8 allow
# DNSSEC 検証
module-config: "validator iterator"
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# 転送先がローカルホストなので、問い合わせを許可する
do-not-query-localhost: no
forward-zone:
name: "."
forward-addr: 127.0.0.1@5053
ポイントは次のとおりです。
- **
do-not-query-localhost: noが必要です。** Unbound は既定でローカルホストへの問い合わせをしないため、これがないと dnscrypt-proxy へ転送されません。 - トラストアンカーの設定を二重に書かない。 Debian / Ubuntu のパッケージでは、
auto-trust-anchor-fileが別のファイルで設定済みのことがあります。既存の設定を確認してから追記します。 - 転送でも検証は手元で行われる。
validatorモジュールが有効なら、上流から受け取った署名を Unbound が検証します。
sudo unbound-checkconf
sudo systemctl restart unbound
最後に、端末(または OS)の DNS を 127.0.0.1 に向けます。systemd-resolved を使っている環境では、53 番を systemd-resolved が使っていて Unbound が起動しないことがあるため、どちらに 53 番を持たせるかを先に決めておきます。
ブラウザの DoH 設定に注意する
構成B にしたあとでブラウザ独自の DoH をオンにすると、ブラウザは OS の DNS(手元の Unbound)を通らずに直接公開リゾルバーへ問い合わせます。手元で検証する意味がなくなるため、構成B を使う端末では、ブラウザの DoH 設定がどうなっているかも確認します。
検証が効いているか確かめる
手元のリゾルバー(構成B)
# 署名されたドメイン: 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
テスト用ドメインは外部のものなので、状態が変わることがあります。結果が想定と違うときは DNSViz などで現状を確認してください。
DoH の接続先(構成A)
公開リゾルバーの中には、DoH の JSON 形式の API を提供しているところがあります。応答の AD が true なら、そのリゾルバーで検証済みです。
# Google Public DNS の JSON API
curl -s "https://dns.google/resolve?name=isc.org&type=A&do=1"
# Cloudflare の JSON API
curl -s -H 'accept: application/dns-json' "https://cloudflare-dns.com/dns-query?name=isc.org&type=A&do=true"
JSON の Status は DNS の応答コードで、0 が NOERROR、2 が SERVFAIL です。JSON API は事業者ごとの独自の形式で、パラメーターや出力の項目は変わることがあります。BIND 9.18 以降の dig なら、dig @1.1.1.1 +https +dnssec isc.org A のように DoH で問い合わせて、flags の ad で同じ確認ができます。
自分のドメインの DNSSEC が DoH 利用者に届いているか
WordPress サイトのドメインに DNSSEC を設定している場合、DoH を使う利用者も公開リゾルバーの検証を経由してサイトにたどり着きます。DS の登録ミスや鍵の更新漏れがあると、検証に失敗するリゾルバー経由では SERVFAIL になり、サイトが表示されなくなります。
# 検証付きで引けるか(AD が true になるか)
curl -s "https://dns.google/resolve?name=example.com&type=A&do=1"
# 検証を無効にして引く(cd=1)。こちらだけ引けるなら DNSSEC の不備
curl -s "https://dns.google/resolve?name=example.com&type=A&cd=1"
cd=1 で引けるのに通常の問い合わせで SERVFAIL になる場合は、DNSSEC の設定に問題があります。応答に失敗の理由が含まれることもありますが、詳しい原因は DNSViz で DS と DNSKEY の対応や署名の期限を確認します。DNSSEC の有効化や移転の作業をしたあとは、この確認を複数の公開リゾルバーで行っておくと、一部の利用者だけサイトが見えない状態に早く気付けます。
うまく動かないときの切り分け
- すべての名前で SERVFAIL になる(構成B)。 まず
dig @127.0.0.1 -p 5053で dnscrypt-proxy が単体で応答するかを確認します。応答しなければ dnscrypt-proxy 側(サーバーリストの取得失敗、443 番の遮断など)の問題です。応答するなら Unbound 側で、時刻のずれやトラストアンカーのファイルを疑います。 - **
+cdを付けると引ける。** 該当ドメインの DNSSEC の不備です。自分のドメインなら上の手順で確認します。 - 社内の名前が引けない。 公開リゾルバーは社内 DNS にしかない名前を知りません。Unbound で社内ドメインだけを社内 DNS へ転送する
forward-zoneを追加し、そのドメインが署名されていない場合はdomain-insecureで検証の対象から外します。
まとめ
- DoH は経路の暗号化、DNSSEC は応答の検証です。併用では「検証をどこで行うか」を最初に決めます。
- DoH の接続先に任せるなら、DNSSEC を検証する公開リゾルバーを選び、JSON API や
dig +httpsの AD で確認します。 - 手元で検証するなら、Unbound で検証し、DoH での問い合わせは dnscrypt-proxy に任せます。
do-not-query-localhost: noが必要です。 - 手元検証の端末では、ブラウザ独自の DoH が手元のリゾルバーを迂回しないか確認します。
- 自分のドメインは、通常の問い合わせと
cd=1の結果を比べて、DoH 利用者にも DNSSEC が正しく届いているかを確かめます。