2026年6月18日
2026年6月18日
DANE の鍵ローテーション手順|次の鍵の TLSA を常に公開しておき、メールサーバーの鍵を計画的に入れ替える
DANE を使うメールサーバーの鍵ローテーションは、「次に使う鍵の TLSA レコードを、ふだんから現在の鍵と並べて公開しておく」運用にすると、入れ替えのたびに TTL を待つ必要がなくなり、失敗したときもすぐ元に戻せます。この記事では、その「current + next」を常設する運用の組み立て方、certbot で次の鍵の証明書を用意する方法、Postfix での切り替え、MX が複数あるときの進め方、漏えい時の対応、鍵と TLSA のずれを見つける監視までを説明します。
単発の作業(MX ホストの移行や DANE の取りやめなど)の順番は別の記事で扱っているため、ここでは「同じホストの鍵を、定期的に、止めずに回し続ける」ことに絞ります。また、ここでいう鍵はメールサーバーの TLS の鍵であり、DNSSEC の署名鍵(ZSK・KSK)とは別物です。
なぜ「次の鍵」を先に公開しておくのか
DANE に対応した送信側のメールサーバーは、受信側の MX ホスト名に対応する TLSA レコード(_25._tcp.<MXホスト名>)を DNSSEC 付きで引き、提示された証明書や公開鍵と照合します。一致しなければ、平文には落とさず配送を保留します。
ここで問題になるのがキャッシュです。新しい鍵に切り替えた直後でも、送信側のリゾルバーが古い TLSA だけをキャッシュしていれば照合に失敗します。そのため、鍵を替える前に新しい鍵の TLSA を公開し、TTL が切れるまで待つ必要があります。
この待ち時間を毎回とるのではなく、次の鍵の TLSA を前回の入れ替えのときから公開しておけば、入れ替えたい時点ですでに世界中のキャッシュに次の鍵が入っています。RFC 7671 が示す「current + next」の考え方を、ふだんの状態として保つ運用です。
; ふだんの状態(値はダミー)
_25._tcp.mail.example.com. 3600 IN TLSA 3 1 1 aaaa...(現在使っている鍵)
_25._tcp.mail.example.com. 3600 IN TLSA 3 1 1 bbbb...(次に使う鍵)
使う TLSA の形式は、サーバー自身の公開鍵に一致させる 3 1 1(DANE-EE、公開鍵、SHA-256)が扱いやすいです。証明書の更新で鍵を変えなければ TLSA も変わらず、鍵を替えるときだけ TLSA を操作すれば済みます。
ローテーションの1サイクル
ふだんの状態から、次の順番で1回分の入れ替えを行います。
- メールサーバーの設定を、次の鍵(bbbb)の証明書に切り替えて再読み込みする
- 外部から照合が通ることを確認する
- 数日様子を見て問題がなければ、旧鍵(aaaa)の TLSA を削除する
- さらに次の鍵(cccc)を作り、その TLSA を追加する
手順1の時点で bbbb の TLSA はすでに長く公開されているため、待ち時間なしで切り替えられます。手順2で問題が見つかった場合は、設定を aaaa に戻して再読み込みするだけで元に戻ります。旧鍵の TLSA を急いで消さないことが、この運用の安全装置です。
手順3と4は同時に行っても構いません。終わった時点で、再び「現在の鍵+次の鍵」の2行の状態に戻ります。
次の鍵の証明書を用意する(certbot の例)
この運用の要は、次の鍵で発行した証明書を、使い始める前から手元に用意しておくことです。Let's Encrypt と certbot を使う場合は、証明書の系列(lineage)を2つ持つ方法が分かりやすいです。
# 次の鍵の系列を別名で取得する。この時点ではメールサーバーには設定しない
sudo certbot certonly --cert-name mail-b --reuse-key \
--key-type ecdsa -d mail.example.com --standalone
--reuse-key は、自動更新のときに同じ秘密鍵を使い続ける指定です。すでに使っている系列(ここでは mail-a)も、更新のたびに鍵が変わらない設定になっているかを確かめます。設定は /etc/letsencrypt/renewal/mail-a.conf の [renewalparams] に reuse_key = True として保存されるのが一般的で、ここが無い場合は更新のたびに新しい鍵が作られます。どちらの系列も通常の自動更新の対象になるため、使っていない系列でも証明書の期限は切れません。認証方式(--standalone や --webroot など)やオプションの書き方は環境と certbot の版で異なるので、使っている版の文書で確認してください(2026年10月現在)。
次の鍵の TLSA に使うハッシュは、取得した証明書から計算します。
sudo openssl x509 -in /etc/letsencrypt/live/mail-b/cert.pem -noout -pubkey \
| openssl pkey -pubin -outform DER | sha256sum
出力された64桁の16進数を、3 1 1 の値として TLSA に追加します。
自前の CA や、ACME 以外の方法で証明書を取る場合は、先に秘密鍵と CSR を作っておき、その CSR で発行してもらう形になります。どの方法でも、秘密鍵はメールサーバーの実行ユーザーと root 以外が読めない権限にし、バックアップの対象に含めます。
Postfix で次の鍵に切り替える
Postfix 3.4 以降では、smtpd_tls_chain_files で鍵と証明書をまとめて指定できます。切り替えはこの1行を書き換えて再読み込みするだけです。
# /etc/postfix/main.cf(切り替え前)
smtpd_tls_chain_files =
/etc/letsencrypt/live/mail-a/privkey.pem,
/etc/letsencrypt/live/mail-a/fullchain.pem
# 切り替え後
smtpd_tls_chain_files =
/etc/letsencrypt/live/mail-b/privkey.pem,
/etc/letsencrypt/live/mail-b/fullchain.pem
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.bak.$(date +%F)
sudo postfix check && sudo postfix reload
古い Postfix で smtpd_tls_cert_file と smtpd_tls_key_file を使っている場合は、その2つを書き換えます。IMAP や SMTP サブミッション(587番)で同じ証明書を使っている場合、そちらは DANE の照合対象ではありませんが、設定をそろえておくと管理が楽になります。
切り替え後の確認
切り替えたら、送信側の立場で照合を確かめます。openssl s_client には公開中の2つの TLSA を両方渡し、どちらに一致したかを見ます。
openssl s_client -starttls smtp -connect mail.example.com:25 \
-dane_tlsa_domain mail.example.com \
-dane_tlsa_rrdata "3 1 1 aaaa..." \
-dane_tlsa_rrdata "3 1 1 bbbb..." </dev/null 2>/dev/null \
| grep -E "Verification|DANE"
検証結果が成功となり、bbbb の TLSA に一致したと表示されれば切り替えは成功です(表示の文言は OpenSSL の版で多少異なります)。Postfix 付属の posttls-finger を使い、実際に DNS から MX と TLSA を引かせて確かめる方法もあります。引数には MX を引く対象のドメイン名を渡します。
posttls-finger -c -l dane example.com
この確認は、DNSSEC を検証するリゾルバーを使える環境で行います。DANE に対応した外部のメール検証サービスで、外部からの見え方を確かめておくのも有効です。
MX が複数あるときの進め方
TLSA は MX ホストごとに置きます。mx1 と mx2 がある場合、それぞれの _25._tcp.mx1.example.com と _25._tcp.mx2.example.com に「現在+次」の2行を持たせ、入れ替えは1台ずつ行います。1台目の切り替えと確認が終わってから2台目に進めば、問題が起きても片方のサーバーで受信を続けられます。
複数台で同じ鍵を共有する構成も可能ですが、1台の鍵が漏れると全台の入れ替えが必要になります。台数が少ないうちから、ホストごとに別の鍵を持つほうが影響を小さくできます。
鍵が漏れたときの対応
秘密鍵が漏れた疑いがある場合は、次の鍵へすぐ切り替え、漏れた鍵の TLSA を削除します。ふだんから次の鍵が公開されているため、この切り替え自体は待ち時間なしで行えます。
ただし、削除した TLSA は、TTL が切れるまで一部のリゾルバーのキャッシュに残ります。その間、漏れた鍵を持つ第三者が経路上でなりすました場合に照合が通ってしまう可能性があります。TLSA の TTL を極端に長くしないことが、こうした事態への備えになります。削除と同時に新しい「次の鍵」も作り直し、2行の状態に戻してください。
ずれを見つける監視
運用でもっとも起きやすい事故は、「証明書の自動更新で鍵が知らないうちに変わり、TLSA と合わなくなる」ことです。次のスクリプトは、実際に提示されている公開鍵のハッシュが、公開中の TLSA のどれかに一致するかを確かめる例です。一致しない場合や、TLSA が1行しかない場合に終了コード1を返すので、cron や監視ツールから呼び出して通知につなげます。
#!/bin/sh
# 使い方: ./tlsa-check.sh mail.example.com
host="$1"
live=$(openssl s_client -starttls smtp -connect "$host:25" -servername "$host" </dev/null 2>/dev/null \
| openssl x509 -noout -pubkey \
| openssl pkey -pubin -outform DER | sha256sum | cut -d' ' -f1)
tlsa=$(dig +short "_25._tcp.$host" TLSA \
| awk '$1==3 && $2==1 && $3==1 {h=""; for(i=4;i<=NF;i++) h=h $i; print tolower(h)}')
if [ -z "$live" ] || ! echo "$tlsa" | grep -qx "$live"; then
echo "NG: $host の公開鍵が TLSA に一致しません"; exit 1
fi
if [ "$(echo "$tlsa" | grep -c .)" -lt 2 ]; then
echo "WARN: $host の TLSA が1行だけです(次の鍵が未公開)"; exit 1
fi
echo "OK: $host"
dig +short は長い16進数を空白で区切って表示するため、awk で連結してから比べています。このスクリプトは DNS の値を読むだけで、DNSSEC の検証結果は見ていません。DNSSEC 側の監視は別に用意してください。
まとめ
- DANE の鍵ローテーションは、TLSA に「現在の鍵+次の鍵」の2行を常に置く運用にすると、待ち時間なしで切り替えられ、すぐ元に戻せる
3 1 1を使い、証明書の更新では鍵を変えない設定にして、鍵を替えるときだけ TLSA を操作する- certbot では証明書の系列を2つ持ち、次の鍵の証明書を先に取得してハッシュを公開しておく
- 切り替えは1台ずつ行い、
openssl s_clientやposttls-fingerで照合を確かめてから旧鍵の TLSA を消す - 提示中の公開鍵と TLSA の一致、TLSA が2行あることを定期的に監視する