2026年6月22日
2026年6月22日
CAAレコードと DoH (DNS over HTTPS) を併用する設定|DoH サーバーの証明書を CAA で守り、CAA の見え方を DoH で確かめる
CAA レコードと DoH を併用する意味は、大きく二つあります。一つは、自分で公開している DoH サーバー(例: doh.example.com)の証明書を、決めた認証局と ACME アカウントからしか発行できないよう CAA で制限すること。もう一つは、CAA が外からどう見えているかを DoH で点検することです。この記事では、DoH サーバー向けの CAA の書き方、accounturi と validationmethods による絞り込み、DoH を使った確認と定期監視、証明書の更新で発行を止めないための注意点までを説明します。
最初に押さえる前提: 認証局は自分の DoH を使わない
CAA は、認証局が証明書を発行する前に確認する DNS レコードです。認証局は自前のリゾルバーで CAA を引き、自分の名前が許可されていなければ発行を断ります。
ここでよくある誤解が、「自分のサーバーの名前解決を DoH にすれば CAA の確認も暗号化される」というものです。認証局がどの経路で問い合わせるかは認証局側の仕組みで決まり、こちらの DoH の設定は関係しません。DoH を併用して得られるのは、次の二点です。
- DoH サーバーを運営している場合:DoH のクライアントは、サーバーの TLS 証明書で接続先を確かめます。第三者が不正に
doh.example.comの証明書を手に入れると、DoH サーバーになりすまして偽の応答を返せる可能性があります。CAA はその入口を狭める手段です。 - 点検の道具として:53 番の外向き通信が塞がれた環境でも、443 番だけで公開リゾルバーに CAA を問い合わせ、検証付きのリゾルバーからどう見えるかを確かめられます。
CAA は「これから発行される証明書」を制限するもので、すでに発行済みの証明書や、認証局そのものの不正には効きません。証明書の透明性ログ(CT ログ)の監視と組み合わせると、見落としを減らせます。
手順1: 今どこから証明書が出ているかを洗い出す
CAA を書く前に、対象の名前とその親ドメインで実際に使われている認証局を確認します。ここを飛ばすと、CDN やメールサービスなど別の経路で発行されている証明書の更新を止めてしまいます。
- crt.sh などの CT ログ検索サービスで、ドメイン名を検索して発行元を一覧にする
- CDN(Cloudflare など)を経由している名前は、CDN 側が自動で証明書を発行していないかを確認する。CDN によっては複数の認証局を使い分けています
- DoH サーバー自体の証明書を、どの ACME クライアント・どのアカウントで取っているかを控える
CDN の中には、CAA を設定すると自社が使う認証局の行を自動で補うものもあります。動作は事業者や設定によって異なるため、各社の説明を確認してください(2026年10月現在)。
手順2: DoH サーバー向けの CAA を書く
認証局は、まず証明書の名前そのもの(doh.example.com)で CAA を探し、無ければ親(example.com)へとさかのぼります。最初に見つかった CAA の組が使われるので、DoH サーバーの名前にだけ、ドメイン全体より厳しい CAA を置くことができます。
; ドメイン全体: Let's Encrypt のみ許可、ワイルドカードは発行させない
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 issuewild ";"
example.com. 3600 IN CAA 0 iodef "mailto:security@example.com"
; DoH サーバー: 特定の ACME アカウントと検証方法に限定する(例)
doh.example.com. 3600 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789; validationmethods=dns-01"
doh.example.com. 3600 IN CAA 0 iodef "mailto:security@example.com"
accounturi と validationmethods は RFC 8657 で定められたパラメータです。Let's Encrypt はこの二つに対応していますが、対応状況は認証局ごとに異なります。他の認証局を使う場合は、その認証局の CAA に関する説明を確認してから使ってください。
accounturi:発行を許す ACME アカウントを一つに絞ります。同じ認証局でも、別のアカウントからの申請は拒否されます。validationmethods:ドメインの確認方法を限定します。値はdns-01、http-01、tls-alpn-01などです。DoH サーバーは 443 番で動くことが多く、HTTP の 80 番を閉じている構成もあるため、DNS で確認するdns-01を選ぶ例がよく見られます。
doh.example.com に CAA を置いた時点で、この名前には親の example.com の CAA は使われなくなります。そのため、iodef など親に書いた行で必要なものは、子の側にも書き直します。
アカウントの URI は、certbot であれば certbot show_account で表示できます。版によってはこのコマンドが無いため、その場合はアカウント情報のファイル(/etc/letsencrypt/accounts/ の配下にある regr.json の uri)を確認してください。
DNS の設定を変える前には、現在のゾーンの内容を書き出して保存しておきます。CAA の誤りは、次の証明書更新の日まで表に出ないことが多いからです。
手順3: DoH で CAA の見え方を確かめる
登録したら、公開リゾルバーに DoH で問い合わせて確認します。BIND 9.18 以降の dig は +https で DoH を使えます(ビルドによっては無効です)。
dig +https @1.1.1.1 doh.example.com CAA
dig +https @8.8.8.8 doh.example.com CAA
dig +https @1.1.1.1 example.com CAA
確認する点は次のとおりです。
status:がNOERRORで、書いたとおりの行が返っているか- ゾーンが DNSSEC で署名されているなら、
;; flags:にadが付いているか - 子の名前と親の名前で、意図した CAA がそれぞれ返っているか
SERVFAIL が返る場合は注意が必要です。CAA の問い合わせが失敗すると、多くの認証局は発行を見送ります。DNSSEC の署名切れや DS の食い違いがあると、この状態になります。+cd を付けると値が返るなら、署名の検証で落ちていると判断できます。
doh.example.com が CNAME になっている場合は、CNAME の先の名前で CAA が評価されることがあります。CNAME を使う構成では、問い合わせ結果に CNAME が含まれていないかも見てください。
JSON で確認したい場合は、各社の JSON 形式の API も使えます。これは RFC 8484 ではなく事業者独自の仕様なので、項目名や書式が変わる可能性があります。
curl -s 'https://dns.google/resolve?name=doh.example.com&type=CAA'
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=doh.example.com&type=CAA'
"Status": 0 が NOERROR、"AD": true が検証済みを表します。
手順4: CAA を定期的に点検する
CAA は一度書いたら終わりではなく、DNS サービスの移行やゾーンの書き換えで消えたり変わったりすることがあります。想定する内容をファイルに保存しておき、DoH で取得した結果と比べるスクリプトの例です。
#!/usr/bin/env bash
# caa-doh-check.sh : DoH で CAA を取得し、想定の内容と比べる(例)
set -u
NAME="doh.example.com"
EXPECTED="/etc/caa-check/doh.example.com.txt" # dig +short の形式で想定値を保存しておく
RESOLVERS="1.1.1.1 8.8.8.8"
want=$(sort "$EXPECTED")
rc=0
for r in $RESOLVERS; do
got=$(dig +https @"$r" "$NAME" CAA +short | sort)
if [ -z "$got" ]; then
echo "NG: ${r} で ${NAME} の CAA が取得できません(未設定・SERVFAIL・通信失敗のいずれか)"
rc=1
elif [ "$got" != "$want" ]; then
echo "NG: ${r} で CAA が想定と異なります"
printf '%s\n' "$got"
rc=1
else
echo "OK: ${r}"
fi
done
exit "$rc"
想定値のファイルは、正しい状態のときに dig +https @1.1.1.1 doh.example.com CAA +short > /etc/caa-check/doh.example.com.txt で作ると、書式の違いで誤検知しにくくなります。1日に1回程度 cron で動かし、失敗時に通知が届くようにしておくと十分に役立ちます。
手順5: 更新を試し、発行が止まらないことを確かめる
CAA を絞ったあとは、次回の自動更新を待たずに動作を試します。
sudo certbot renew --cert-name doh.example.com --dry-run
ここで一つ落とし穴があります。--dry-run は Let's Encrypt のステージング環境を使い、ステージングでは本番とは別のアカウントになります。本番のアカウントだけを accounturi で許可していると、テストが CAA で失敗することがあります。テストの間だけステージングのアカウント URI を含む issue 行を足すか、テストでの失敗理由が CAA かどうかをエラーメッセージで確認してから判断してください。
本番の更新が成功したら、certbot certificates で有効期限が延びたことを確かめます。認証局やアカウントを切り替える予定があるときは、新しい行を先に追加し、移行が終わってから古い行を消す順序にします。逆の順序だと、切り替えの間に更新が止まります。
まとめ
- 認証局は自前のリゾルバーで CAA を確認するため、自分側の DoH 設定は CAA の確認経路に影響しません。
- DoH サーバーを公開しているなら、その名前に
accounturiとvalidationmethodsで絞った CAA を置き、なりすまし用の証明書を取られにくくします。子の名前に CAA を置くと親の CAA は使われない点に注意します。 - DoH で複数の公開リゾルバーに問い合わせ、
status、adフラグ、値を確認します。SERVFAIL は発行停止につながります。 - 想定値との比較スクリプトで定期点検し、
--dry-runではステージング用のアカウントの扱いに気を付けます。