nabeo がピーしているブログ (仮)

どーも、nabeop です

infrataster-plugin-dns で DNS サーバの検査をする

DNS サーバの挙動をテストする

インフラの挙動をテストするためのフレームワークである infrataster という仕組みがあります。サーバ構築時のテストフレームワークServerspec が有名ですが、対象をインフラにしたものという乱暴な理解です。

で、インフラといえば DNS なんですが、僕は小心者なので DNS サーバの設定でしくじったりすると、被害が甚大なので、小さな修正でもドキドキしながらオペレーションをしています。また、設定直後はうまく動いているように見えて、実は他のエントリを書き換えてしまい後から障害になってしまう、ということも考えられます。まさに自分が自宅の内部ネットワークのメンテ時に凡ミスして一時的に名前解決ができなくなってツライ目にあってしまったので、infrataster の復習がてら、infrataster-plugin-dns を使ってみたので、そのメモを残しておこうと思います。

infrataster そのものは DNS サーバのテストはできませんが、infrataster-plugin-dns はその名前のとおり infrataster のプラグインとして作成されて、特定の DNS サーバを対象にしてクエリ応答のテストができます。

準備

infrataster は rspec の仕組みのうえに構築されているので、rspec --initrspec 的な準備をしておきます。

spec/spec_helper.rb には以下のように検査したい DNS サーバを定義します。

require 'infrataster/rspec'
require 'infrataster-plugin-dns'

Infrataster::Server.define(:exapmle_com, 'a.iana-servers.net')

ここでは例として example.com の権威サーバの a.iana-servers.net:example_com として定義しています。

テストケースは spec/example.com_spec.rb とかに書いていきます。

require  'spec_helper'

describe server(:exapmle_com) do
  # ここにテストケースを書いていく
end

頑張ってテストケースを書く

ここからは運用している DNS サーバに登録している内容をひたすら列挙していく作業です。

infrataster-plugin-dnsREADME.md にも書かれていますが、基本的に rspec 的なマッチャーは rspec-dns をベースにしているので、そちらを参照していきます。さらに、rspec-dns は内部的に Dnsruby を呼び出しているので、各 RR の応答を検査するときは Dnsruby::RR クラスのドキュメントを読みながら空気を察する必要があります。

というわけで、いくつか例をあげておきます。

NS レコード

example.com. の NS の場合は dig で確認すると以下のように応答がありました。

;; QUESTION SECTION:
;example.com.                   IN      NS

;; ANSWER SECTION:
example.com.            86400   IN      NS      a.iana-servers.net.
example.com.            86400   IN      NS      b.iana-servers.net.

この場合は Dnsruby::RR::NS を見ながら書くとこんな感じになります。

describe dns('example.com.') do
  it 'NS RR : a.iana-servers.net' do
    is_expected.to have_entry.with_type('NS').
                     and_ttl(86400).
                     and_domainname('a.iana-servers.net')
  end
  it 'NS RR : b.iana-servers.net' do
    is_expected.to have_entry.with_type('NS').
                     and_ttl(86400).
                     and_domainname('b.iana-servers.net')
  end
end

SOA レコード

example.comSOAdig で確認すると以下のようになっています。

;; QUESTION SECTION:
;example.com.                   IN      SOA

;; ANSWER SECTION:
example.com.            3600    IN      SOA     sns.dns.icann.org. noc.dns.icann.org. 2018013047 7200 3600 1209600 3600

で、NS の時と同じように Dnsruby::RR::SOAアトリビュートとRR応答の仕様を見比べながら書いていきます。

describe dns('example.com.') do
  it 'SOA RR' do
    is_expected.to have_entry.with_type('SOA').
                     and_ttl(3600).
                     and_mname('sns.dns.icann.org').
                     and_rname('noc.dns.icann.org').
                     and_refresh(7200).
                     and_retry(3600).
                     and_expire(1209600).
                     and_minimum(3600)
  end
end

その他の RR

基本的に Dnsruby::RR のドキュメントや infrataster-plugind-dnsrspec-dns の例を見ておけば、なんとなく雰囲気は察することができると思いますし、代表的な RR については例があるのでわかりやすいと思います。

最後に書きながらハマったところをメモがてら残しておきます。

DNSEC 周りの応答には Base64エンコードしたデータが含まれていますが、そのまま and_signature に渡すとテストに失敗します。Base64.decode64 で符号化を解いた状態でマッチャーに渡してあげましょう。コードの中身まで追っていませんが、Dnsruby の処理でそのようになっているような気がします。例えば、example.com の DNSKEY の1つは以下のように応答があります。

example.com.            3600    IN      DNSKEY  256 3 8 AwEAAblDjCPejMhknWXZqbwBEUPI6Lkwjvp0XlUNTBqW2glZrgf3MXjJ ZBXl8rhYoTkrov7jmbBaBOPTkqlQAbfOKFNoG+U+boGG6Zmy00l2XRP1 nckVMpJ2TxiDVcXJqs78MetC1Ztu4p6bj4VrJCYTmv3ZULSrWleMSWtv YXqY0S23

この場合は最後の公開鍵部分が Base64エンコードされているので、以下のようにするとテストできます。

describe dns('example.com.') do
  it 'DNSKEY RR' do
    is_expected.to have_entry.with_type('DNSKEY').
                     and_key(Base64.decode64('AwEAAblDjCPejMhknWXZqbwBEUPI6Lkwjvp0XlUNTBqW2glZrgf3MXjJZBXl8rhYoTkrov7jmbBaBOPTkqlQAbfOKFNoG+U+boGG6Zmy00l2XRP1nckVMpJ2TxiDVcXJqs78MetC1Ztu4p6bj4VrJCYTmv3ZULSrWleMSWtvYXqY0S23'))
  end
end

このようにあらかじめテストケースを網羅的に用意して、履歴を管理することで、思わぬ事故を防ぎ、家庭の平和(と父親の威厳)を守りたいものです。