BIND9でDNSSECを構成する:ゾーン自動署名と鍵管理の完全ガイド

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

現場の課題:DNSキャッシュポイズニングという見えない脅威

デイリーアクティブユーザー4万5,000人を抱えるECサイトの移行作業を担当していた際、サポートデスクに東南アジア圏のユーザーからSSL証明書不一致エラーの問い合わせが突如として殺到しました。オリジンサーバーのCPU負荷は平常通りで、アクセスログにも異常は見られず、ワイルドカードTLS証明書も正常に有効期限内でした。

攻撃者はクラウドアカウントやサーバーインスタンスを直接侵害したわけではありませんでした。代わりに、現地のTier 2トランジットISP 2社が運用する再帰的キャッシュリゾルバーをポイズニングし、当サイトのAレコードをフィッシング用クローンサイトのIPアドレスへと書き換えていたのです。

従来のDNSは暗号化されていない平文のUDP上で動作し、データ検証の仕組みを持ちません。再帰リゾルバーがIPアドレスを問い合わせる際、16ビットのトランザクションIDと送信先UDPポート番号が一致していれば、最初に届いたパケットを無条件に正当なものとして受理してしまいます。リゾルバーの問い合わせ最中に偽装した応答パケットを大量送信する攻撃者は、このレースコンディションに容易に勝利できます。一度不正な応答が注入されると、そのTTL(多くの場合86,400秒=24時間)が切れるまでキャッシュに残り続けます。この24時間の間、インフラ監視のアラートが一切鳴動しないまま、現地のユーザーはフィッシング詐欺に晒され続けることになります。

根本原因の分析:従来のDNSがデータの完全性を保証できない理由

DNSは1983年、通信速度とプロトコルオーバーヘッドの最小化を最優先に設計されました。セキュリティは考慮されていませんでした。その後、ネットワークエンジニアによってカミンスキー型ブラインドアタックを緩和するためのUDPソースポートランダム化など、統計的なパッチが導入されました。しかし、統計的な防御壁は暗号論的な保証にはならず、トラフィックの多いリゾルバーであれば、攻撃者がポートの組み合わせを総当たりで突破することは依然として可能です。

従来のDNSは、完全性に関する以下の3つの基本要件を満たせていません。

  • 送信元認証の欠如:リゾルバーは、受信したパケットが正規の権威ネームサーバーから送られたものか、偽装された送信元アドレスからのものかを判別できません。
  • 改ざん検知の欠如:中継ルーターが通信経路の途中でペイロードを改ざんしても、チェックサムエラーとして検知されません。
  • 非存在の認証不可:レコードが存在しないことを暗号学的に証明する手段がないため、NXDOMAIN応答がスプーフィングに対して無防備になります。

DNS Security Extensions(DNSSEC)は、公開鍵暗号を活用してこれらの脆弱性を解決します。権威サーバーはすべてのリソースレコードセット(RRset)に対して電子署名(RRSIG)を付与します。リゾルバーはICANNのルートゾーンから該当ドメインまでの「信頼の連鎖(Chain of Trust)」をたどり、各委任境界で公開鍵を順次検証します。

手法の比較:手動スクリプト vs 従来型インライン署名 vs 最新のdnssec-policy

BIND9でDNSSECを実装するには、主に3つの方法があります。適切な方式を選択しないと、署名の有効期限切れやロールオーバーの暗黙的な失敗を招き、世界規模でのSERVFAILによるサービス停止を引き起こすリスクがあります。

1. dnssec-keygenとdnssec-signzoneによる手動署名

BIND 9.9以前は、管理者がディスク上に鍵を生成し、cronジョブでdnssec-signzoneを実行していました。ゾーンを更新するたびに静的な.signedゾーンファイルを再生成し、デーモンをリロードする必要がありました。

  • メリット:動作が予測可能で、オフライン署名などの閉域環境でも問題なく機能する。
  • デメリット:運用のリスクが極めて高い。cronジョブの停止や署名有効期限(通常30日)の更新漏れが発生すると、DNSSECを検証する世界中のリゾルバーから該当ドメインへのアクセスが完全に途絶します。

2. 従来型インライン署名(auto-dnssec maintain)

BIND 9.9で導入されたこのアプローチは、動的ジャーナル(.jnl)を管理することで、未署名の生ゾーンファイルと署名済み出力を分離しました。ゾーンファイルが更新されると、BINDが自動的にレコードに再署名します。

  • メリット:外部のcronスクリプトが不要になり、RFC 2136による動的DNS更新(DDNS)がネイティブでサポートされる。
  • デメリット:鍵のロールオーバーが完全に手動のまま。正確なスケジュールに従い、CLIコマンドを叩いてKSKおよびZSKの生成、公開、アクティブ化、パージを実施する必要がありました。

3. 最新の自動化dnssec-policy(BIND 9.16以降)

BIND 9.16で導入され、9.18以降のLTSで標準となったdnssec-policyブロックは、BINDを自律的な暗号エンジンへと進化させました。鍵の生成、ロールオーバーステータスの管理、DSダイジェストハッシュの計算、RRsetへの再署名をすべて内部で自律的に行います。

  • メリット:鍵ロールオーバーの完全自動化、ECDSA P-256のネイティブサポート、署名の自動リフレッシュによる運用負荷の劇的な削減。
  • デメリット:BIND 9.16以降が必要であり、デーモンの作業ディレクトリに対する適切なファイル書き込み権限の設定が必須。

最適なアプローチ:BIND9 dnssec-policyによる自動DNSSEC署名

現代の本番環境においては、宣言型アプローチを採用したdnssec-policyフレームワークが最も堅牢な選択肢です。当社の本番環境では、約12,000 QPSを処理するプライマリ権威DNSクラスタ全体でこの構成を採用しており、運用のボトルネックなしに安定稼働しています。

ステップ1:BINDのバージョンと暗号エンジンの確認

サーバーでBIND 9.16 LTSまたはBIND 9.18以降のLTSが動作していることを確認します。

named -v
# 対象バージョン: BIND 9.16.x または BIND 9.18.x

Ubuntu 22.04/24.04の標準パッケージ(bind9)やRHEL 9/AlmaLinux 9のパッケージ(bind)には、デフォルトでOpenSSL暗号サポートがリンクされています。

ステップ2:本番用DNSSECポリシーの定義

設定ファイル(Debian/Ubuntuでは/etc/bind/named.conf.options、Enterprise Linuxでは/etc/named.conf)を開き、専用のポリシーブロックを追加します。ここではアルゴリズム13(ECDSAP256SHA256)を採用します。署名サイズがわずか64バイトと軽量であり、パケットサイズを抑えてUDPバッファサイズ制限(1,232バイト)超過によるIPフラグメンテーションを防ぐことができるためです。

dnssec-policy "production-policy" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsa256;
        zsk key-directory lifetime 60d algorithm ecdsa256;
    };

    # 署名のタイミング設定
    signatures-refresh 5d;
    signatures-validity 14d;
    signatures-validity-dnskey 14d;

    # 伝播遅延とTTLのマージン
    max-zone-ttl 86400;
    zone-propagation-delay 300;
    parent-ds-ttl 86400;
    parent-propagation-delay 3600;
};

この構成では、ゾーン署名鍵(ZSK)が60日ごとに自動ロールオーバーされます。鍵署名鍵(KSK)は手動で移行スケジュールを組むまで無期限で有効となります。BINDは14日間の署名有効期間が切れる5日前に、レコードを自動的に再生成して署名を更新します。

ステップ3:ゾーンへのポリシーの適用

ローカルゾーン定義ファイル(/etc/bind/named.conf.localなど)を開き、管理対象の権威ゾーンにポリシーを紐付けます。

zone "example.com" {
    type primary;
    file "/var/lib/bind/db.example.com";
    dnssec-policy "production-policy";
    key-directory "/var/lib/bind/keys";
    inline-signing yes;
};

鍵ディレクトリとそのストレージの所有権がサービス実行ユーザーに設定されていることを確認してください。BINDはジャーナルファイル、署名済みゾーンデータベース、秘密鍵を作成するため、書き込み権限が必要です。

mkdir -p /var/lib/bind/keys
chown -R bind:bind /var/lib/bind/
chmod 770 /var/lib/bind/ /var/lib/bind/keys

ステップ4:BINDのリロードと鍵生成の確認

リロードを実行する前に構文チェックを行います。

named-checkconf
named-checkzone example.com /var/lib/bind/db.example.com

両方のコマンドがエラーなしで完了したら、デーモンに変更を反映します。

rndc reconfig
rndc reload example.com

BINDは/var/lib/bind/keys配下にKSKおよびZSKの鍵ペアを自動生成し、ゾーンに署名します。鍵のステータスはrndcで確認できます。

rndc dnssec -status example.com

ステップ5:レジストラへのDS(Delegation Signer)レコードの登録

ローカルでの署名により、自サーバー内での検証準備は整いました。ただし、外部のリゾルバーが検証を行うには、上位ゾーン(.comや.netなど)にKSKを紐付ける必要があります。それまでは、問い合わせに対してSERVFAILが返されてしまいます。この信頼の連鎖を繋ぐため、ドメインレジストラにDelegation Signer(DS)レコードを登録します。

ローカルの権威サーバーに問い合わせて、生成されたCDSレコードを取得します。

dig @127.0.0.1 example.com CDS +short

このコマンドにより、スペース区切りの4つのフィールドが出力されます。

# 鍵タグ | アルゴリズム | ダイジェストタイプ | ダイジェスト値
2371 13 2 E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855

ドメインレジストラ(Cloudflare、お名前.com、Route 53 Domainsなど)の管理画面にログインします。example.comのDNSSEC設定を開き、ダイジェストタイプ2(SHA-256)、アルゴリズム13(ECDSA Curve P-256 with SHA-256)を選択し、ダイジェスト文字列を貼り付けます。通常、1〜2時間以内に上位レジストリへDSレコードが反映されます。

ステップ6:エンドツーエンドの信頼チェーンの検証

上位ゾーンの更新完了後、delvを使用して信頼の連鎖(Chain of Trust)を検証します。

delv @8.8.8.8 example.com +rtrace

出力の末尾に完全な検証成功を示すメッセージが表示されているか確認します。

;; fully validated
example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN RRSIG A 13 2 3600 ...

digを使用してAuthenticated Data(AD)フラグが立っていることを直接確認することも可能です。

dig @1.1.1.1 example.com +dnssec

ヘッダーのフラグ行を確認します。adフラグが存在していれば、上位リゾルバーによる暗号検証が正常に行われたことを意味します。

;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

現場での実践から得られた運用のベストプラクティス

  • ゾーンTTLの上限を86,400秒に設定する:レコードのTTLは3,600秒〜86,400秒(1日)の間に収めてください。長すぎるTTLを設定すると、緊急時のレコード切り替えが滞るだけでなく、鍵の不整合が発生した際の検証エラー障害が長期化します。
  • 外部監視による署名期限切れアラートを構築する:自動化されたデーモンであっても、プロセスクラッシュやジャーナルファイルのロックに遭遇する可能性があります。PrometheusのBlackbox ExporterやNagiosなどを使い、署名の有効期限が7日を切った段階でアラートを通知する外形監視を設定してください。
  • 破棄フェーズの鍵ファイルをディスクから即座に削除しない:ZSKの移行中、BINDは上位リゾルバーのキャッシュに残るレコードを検証できるようにするため、旧公開鍵を一定期間保持(リタイアフェーズ)します。鍵ファイルをディスクから手動で強制削除しないでください。
Share: