やってみようDNSSEC (自分で頑張りたい方へ):IIJ DNSプラットフォームサービスのスゴいところ(第5回)

2020年04月23日 木曜日


【この記事を書いた人】
やまぐち

アプリケーションサービス部所属。そのへんのおっさん。

「やってみようDNSSEC (自分で頑張りたい方へ):IIJ DNSプラットフォームサービスのスゴいところ(第5回)」のイメージ

はじめに

IIJ DNSプラットフォームサービスのスゴいところを紹介する最終回です。そのはずです。でも、サービスの話はまったくしません。サービスを使ってくれなくてもいいから真似できるところがあれば真似してね、という趣旨にのっとり、最後は自前で楽ちんにDNSSECを運用するためのHowtoです。

バックナンバーはいちおうこちらにありますけど、そういうわけでほとんど関連しないので読まなくてもいいです。

自前でDNSSEC

DNSSECなんてやりたくないと思う人が多いかもしれません。作業手順が煩雑で、作業に使うコマンドに引数をたくさん付与する必要があり、しかもその引数はしょっちゅう変わります。非常に難易度の高い運用であり、しかもミスするとそのドメインの名前解決ができなくなる、事実上インターネットからそのドメインが消失するに等しい事故になります。だからDNSSECに手を出さないんですか?

正しいです。手作業でやるならDNSSECに手を出してはいけません。人間は間違える生き物です。作業手順をきちんとマニュアル化しても失敗を完全になくすことはできません。「正しい手順を心がける」という精神論は愚策です。最悪です。もし完全手作業でDNSSECを運用してこれまでトラブルを起こしていないところがあったとしても、それは運用が優れているからではありません。運がよかっただけです。

しかし、最悪なのは「手作業でDNSSECを運用すること」であって、「DNSSECを運用すること」ではありません。人間は間違える生き物です。間違いの元凶である人間が介入するのをやめればいいのです。手作業でやるという発想を捨ててください。自動化してください。100%自動化できるわけではありません。手作業の部分は残ります。しかし、自動化できるところはしてください。高性能なサーバはいったい何のために存在すると思ってるんですか。人間がめんどくさい作業をしなくて済むように存在してるんですよ。自動化してください。ルートサーバがDNSSECに対応したのが2010年。当時は自動化するにはツールを自作する必要がありました。それからもう10年も経ち、今では使いやすい自動化ツールが出回ってます。何度でも繰り返します。自動化してください。

以下順を踏んで説明しますが、いくら楽とはいえ細かいところまで書くととんでもなく長くなるのでてきとーに端折ります。DNSSECの運用経験のない人でも簡単にできることを目指して書きますが、DNSSECの知識そのものについては解説しません。他で調べてください。

ステップ0: 準備

レジストラがDNSSEC対応しているかどうか確認する

DNSSEC対応するには、署名鍵のハッシュ(DSレコード)を上位ゾーンに載せてもらう必要があります。これは通常レジストラ(jpドメインなら指定事業者)経由でおこなうことになりますが、レジストリへのDS取り次ぎに対応していないレジストラというのが存在します。そのため、まずはご利用のレジストラがDS取り次ぎに対応しているかどうか確認してください。対応していない場合、対応している他のレジストラにドメインを移管するか、DNSSECをあきらめるか、の二択になります。

hidden master構成を組む

第1回の記事で説明したhidden master構成を作ってください。

フロントエンド(セカンダリサーバ)、バックエンド(プライマリサーバ)とも、お好きなものを使ってください。使い慣れたBINDでもいいし、これを機会にNSDにしてもかまいませんし、どこかの権威DNSサービスを利用してもかまいません。

図1: hidden master構成

SOAレコードのシリアル番号を変更する

ゾーンファイルを人力で保守している場合、シリアル番号はYYYYMMDDnnの形式で管理していたケースが多いと思います。

DNSSECを自動化すると、署名を付与するためにゾーンがたびたび書き換えられ、その際にSOAのシリアル番号が更新されます。自動化に使うツールにもよりますが、シリアル番号にunixtime (1970/01/01からの通算秒)を使うものや、単純に前回のシリアル番号に+1するだけのものが多いようです。

しかし、2020年の時点では、unixtimeとYYYYMMDDnnを10進数として比較すると後者の方が値が大きく、そのためシリアル番号形式をいきなりunixtimeに変更するとセカンダリサーバにゾーン転送されなくなってしまいます。そのため、DNSSEC署名をはじめる前にシリアル番号をunixtimeよりも小さな値に巻き戻しておく必要があります。

また、単純にシリアル番号を+1する形式ではゾーン転送されなくなることはありませんが、元がYYYYMMDDnn形式で管理していた場合、何度もゾーンが更新されていくうちに13月39日っぽいシリアル番号になってしまってなんだかカッコ悪いです。こちらは単純に見た目だけの問題で動作に影響はなく、放置してもかまいませんが、気になるようならシリアル番号を変更しておいた方がいいでしょう。この記事で説明する構成はインクリメント方式になります。

シリアル番号を増やすのはただ値を変更して反映させるだけですが、減らす場合は特別な手順が必要になります。ここではその手順は説明しません。RFC1982などをご覧ください。

ステップ1: 3段構成への変更

Knotのインストール

サーバをもう1台用意します。ネットワーク構成的には、フロントエンドとバックエンドの中間になります。フロントエンドとバックエンドからアクセスできれば、それ以外のホストからアクセスできる必要はありません。リソースはほとんど消費しないので、バックエンドのホスト内に同居させてもかまいません。

図2: 3段構成

この中段ホストにはKnotをインストールします。BINDやNSDではなく、Knotです。インストール手順はOSによっても異なるので省略します。RedHat系はパッケージが用意されておらず、野良ビルドするのも依存パッケージが多くてけっこうたいへんなので、公式Dockerイメージを利用してもいいかもしれません(この記事の動作検証はDocker for Macでおこなっています)。

Knotの設定

まずはDNSSECを考えず、単純にバックエンドでゾーンを書き換えたらKnotを中継してそのままフロントエンドに転送されるようにします。Knotの設定ファイルは以下のようになります。この例は最低限の設定しかしていませんので、必要に応じてドキュメントを確認してください。Knotの設定はYAMLファイル以外にもLMDB形式のデータベースを使うことも可能ですが、この記事ではYAMLで説明します。

server:
  # IPアドレスとポート番号
  # ip.add.re.ss@port の形式で記述
  listen: 0.0.0.0

# knot「が」アクセスするホストの定義
remote:
  - id: backend
    address: 172.22.53.10
  - id: frontend
    address: [ 172.22.53.20, 172.22.53.21 ]

# knot「に」アクセスするホストの制限
acl:
  - id: notify_backend
    address: 172.22.53.10
    action: notify
  - id: xfr_frontend
    address: [ 172.22.53.20, 172.22.53.21 ]
    action: transfer

# デフォルトで3段構成の中段となるようにする
template:
  - id: default
    master: backend
    notify: frontend
    acl: [ notify_backend, xfr_frontend ]

# 必要なゾーンを列挙
zone:
  - domain: example.com
  - domain: example.jp
  - domain: example.org

ステップ2: DNSSEC署名開始

ここがキモです。

この構成では中段のKnotがDNSSEC管理を担当します。第3回で説明したセカンダリ署名です。

署名すると、ゾーンにはDNSKEYやRRSIG、NSEC(3)といったDNSSECに必要なレコードが追加されます。これらがゾーンファイルにただ「追加」されるだけならわかりやすいのですが、署名にともなって元のゾーンファイルにあったコメントが削除されたり、レコードの記述順が変わったりするなど、可読性が大きく落ちます。可読性が落ちるということは間違いやすくなるということです。また、人間がゾーンファイルを編集するタイミングと署名有効期間を更新するためにシステム側で自動再署名するタイミングが運悪く重なると、予期せぬ結果になることがあります。そのため、「今は人間が編集するからシステムはさわるな(freeze)」「編集終わったからシステム側で書き換えていいよ(thaw)」という指示をいちいち出す必要があります。しかしfreezeだけしてthawを忘れると、署名有効期間が更新されず、そのうち検証失敗することになります。

トラブルの原因は排除しておきましょう。人間とシステムが同一のファイルを共有するから競合するのです。分離しましょう。分離するための手っ取り早い方法が、セカンダリ署名です。人間はDNSSECなんか一切気にせずゾーンファイルを編集し、バックエンドに編集したゾーンファイルを読み込ませます。バックエンドはそれを未署名のまま署名サーバにゾーン転送し、セカンダリ署名します。こうすることで可読性を保ったままゾーンファイルを管理することができ、freeze/thawの指示からも解放されます。

図3: セカンダリ署名

Knotの設定ファイルを以下のように書き換えます。いろいろ追加されていますが、dnssec-signing: onの行が重要で、それ以外はぶっちゃけなくても動きます。

server:
  listen: 0.0.0.0

remote:
  - id: backend
    address: 172.22.53.10
  - id: frontend
    address: [ 172.22.53.20, 172.22.53.21 ]
  # 追加: キャッシュDNSのIPアドレス(submissionの設定をしない場合は不要)
  - id: resolver
    address: 192.0.2.10

acl:
  - id: notify_backend
    address: 172.22.53.10
    action: notify
  - id: xfr_frontend
    address: [ 172.22.53.20, 172.22.53.21 ]
    action: transfer

# 追加: 設定しなくても可(設定しておくと人力作業がひとつ減る)
submission:
  - id: ds_submission
    parent: resolver

# 追加: DNSSECの署名ポリシー
policy:
  - id: nsec3
    nsec3: on
    # submissionの設定をしない場合は不要
    ksk-submission: ds_submission
    # デフォルトでは以下の設定をしないかぎりKSKロールオーバーしない
    # ksk-lifetime: 365d

template:
  - id: default
    master: backend
    notify: frontend
    acl: [ notify_backend, xfr_frontend ]
    # 追加: DNSSEC署名
    dnssec-signing: on
    dnssec-policy: nsec3

# comとjpはDNSSEC署名する、orgはしない
zone:
  - domain: example.com
  - domain: example.jp
  - domain: example.org
    dnssec-signing: off

設定を書き換えてKnotをリロードすると、DNSSEC鍵2種(KSK、ZSK)が自動で生成され、バックエンドから転送されてきたゾーンに生成した鍵による署名が自動的に付与されます。そして、署名されたゾーンがフロントエンドに転送されます。人間が鍵を生成するコマンドを叩いたり、署名を付与するコマンドを叩く必要はありません。自動でおこなわれます。

ステップ3: DS登録

Knotで署名を開始してしばらくすると、

Feb 18 09:01:55 signer knot[pid] notice: [example.com.] DNSSEC, KSK submission, waiting for confirmation

というログが出力されます。このログが出たら上位ネームサーバにDSレコードを登録してください。このログが出る前に登録すると、DNSKEYやRRSIGなどのDNSSEC関連レコードがネガティブキャッシュにヒットしてDNSSECの検証に失敗することがあります(ふつーDS登録前に問い合わせはされないのでキャッシュにヒットすることもありませんが念のため)。

登録すべきDSレコードは以下のコマンドで確認できます。

# keymgr example.com ds
example.com. DS 27048 13 2 6ad7deb64b1f1bfe4ae87320a54042ef27dffcce6b1db4a72118678a204b96ef
example.com. DS 27048 13 4 7cb75cbb2b1287d9e5772362e454da8ae682a9b000717863b3820adbcf04f00c3ce20fd7e9e3387e0acebf4693675150

2行出力されますが、第5フィールドが “2” のレコード(ハッシュアルゴリズムSHA-256)だけ登録すれば十分です。“4” になっている方はSHA-384で、こちらの方が強度は強いのですが、キャッシュDNSサーバ側で対応していないことがあります。“4”になっている方を登録してもかまいせんが、そのときは“4”だけでなく“2”の方も両方登録してください。また、Knotのバージョンが古いと“1”の行が出力されるかもしれませんが無視してください。これはSHA-1ハッシュで、強度が弱いためRFC8624で使ってはいけないことになりました。

図4: IIJドメイン管理サービスでDSレコード登録

しばらく待って、申請したDSレコードが実際にDNSでひけることを確認してください。

% dig -t DS example.com

DSレコードが登録されると、キャッシュサーバなどのvalidatorはDNSSEC検証を開始します。

なお、KnotはDSレコードの登録準備が整うと、CDSレコードをゾーンに載せます。そして、IIJドメイン管理サービスはCDSレコードに対応しており、CDSレコードを見つけると自動でDSレコードとして登録します。つまり、DSレコードの登録も自動でおこなえます。

DSレコードが登録されたことを人間が確認しても、Knotはそのことを知りません。Knotに教えてあげてください。

# knotc zone-ksk-submitted example.com

これにより、「よっしゃDS登録完了したで」とKnotが認識します。

なお、submissionの設定をしておくと、DS登録が完了したかどうかを人間が調べてKnotに教えなくても(knotc zone-ksk-submittedしなくても)、Knot自身が調べてくれるようになります。人間の作業がひとつ減るので、余裕があったら設定しておくといいでしょう。

ステップ4: バックアップ

DNSSEC鍵を紛失すると、詰みます。新しい鍵を作って引き継ぐには、古い鍵で新しい鍵に署名してやる必要があります。古い鍵にアクセスできないとそれができず、いったんDNSSECを止めるしか方法がありません。そうならないよう、かならず物理的に異なる筐体にバックアップしてください。ZSKのロールオーバーは定期的かつ自動でおこなわれますので、バックアップも定期的におこなう必要があります。

コンパイル時の設定にもよりますが、/var/lib/knot/とか/var/db/knot/とかに置かれているはずです(あるいは設定ファイルで指定してください)。このディレクトリの下をごっそりバックアップしておいてください。

ステップ5: 悠々自適

DNSSECの導入作業は以上で完了です。
一般に、DNSSECの運用は以下の作業が必要です。

  • 鍵の管理
    • 鍵の更新(ロールオーバー)
    • DSレコード登録
  • 署名の管理
    • ゾーン編集時の署名
    • 署名有効期間更新のための署名

これらの作業が煩雑だと言われてきたのですが、ここで紹介する構成ではめんどうな作業はありません。

まず、鍵の管理。ZSKは完全自動でロールオーバーされます。KSKはロールオーバー途中で発生するDSレコードの登録だけ手作業が必要ですが、デフォルトではKSKロールオーバーしないので、DSレコード登録も初回のDNSSEC導入時だけ必要で、それ以降は不要です。つまり、人力で鍵を作成したりロールオーバーしたりといった作業は一切ありません。

ゾーンの編集作業はこれまでとまったく変わりません。バックエンドでゾーンファイルを修正し、SOAレコードのシリアル番号を増やし、gitなり何なりでバージョン管理システムに新ゾーンをcommitしたらゾーンを反映するだけです。未署名ゾーンがKnotに転送され、Knotが署名し、署名済みゾーンがフロントエンドに転送されます。また、署名後一定期間が経過して署名有効期間が切れそうになると、Knotが自動で再署名して有効期間を更新します。つまり、署名は完全に自動でおこなわれるので、人間がおこなう作業は一切ありません。

要は、いったんDNSSECの導入が完了してしまえば、鍵の管理も署名の管理も完全自動で、人間がおこなう作業は何もないということです。人間の仕事は増えません。作業がないので作業ミスもありません。のんびりこれまでと変わらぬ日常を過ごしましょう。

ステップX: DNSSECをやめる

何か理由があってDNSSEC署名をやめるには以下の手順でおこないます。

  1. DSレコードのTTLを確認しておく
  2. レジストラにDSレコードの削除申請をする
  3. DSレコードが実際に消えるのを待つ
  4. 消えてからさらにDSレコードのTTLが経過するのを待つ
  5. DNSSEC署名をやめる(Knotでdnssec-signing: offにする)

この記事で紹介している方法にかぎらず、どんな方法でDNSSECを運用していても手順は共通です。

おしまい

Knotを利用した3段構成によるDNSSEC運用システムを解説しました。IW2018での筆者による発表資料も参考にしてください。導入まではちょっとした手間がかかりますが、それ以降は人間がおこなう作業はなく、一切DNSSECのことを考えずに運用できます。DNSSECは複雑で煩雑な作業が必要で人間の手に負えないという状態はとっくに解消されています。毎年いくつも見つかるBINDの脆弱性対応の方がよっぽど煩雑な作業だと思うんですがいかがでしょうか。これでもめんどうだと思うのならば、IIJ DNSプラットフォームサービスをご利用ください。ちなみに今回取り上げたKnotほどではありませんが、BINDでもかなりの部分を自動化できます。dnssec-keygenとかdnssec-signzoneとかを手で実行するのはやめましょう。

ということで今回はサービスの話はまったくしませんでしたが、IIJ DNSプラットフォームサービスのスゴいところを説明するシリーズはこれで最後になります。どちらかというと機能の紹介が主で、その機能を実現するための技術についてはほとんど触れていません。この記事を書いているのが開発でも運用でもなくサポートのおっさんで、詳しい内部構成についてはあんまり把握してないからしかたないね。そのあたりも(自分以外の誰かが)紹介する機会がもしかしたらどこかであるかも。ないかも。まあ期待せずに待っていてくださいませ。長々とお付き合いありがとうございました。

バックナンバー

やまぐち

2020年04月23日 木曜日

アプリケーションサービス部所属。そのへんのおっさん。

Related
関連記事