BIND9によるダイナミックDNSサーバーの運用

目次

  1. BIND9によるダイナミックDNSサーバーの運用
    1. 検証環境
    2. 検証作業内容
    3. DNSコンテンツサーバーの基本設定
      1. /etc/rc.conf
      2. /usr/local/etc/namedb/named.conf
  2. example.jp ゾーンの定義
    1. /usr/local/etc/namedb/TSIGキー名.key
      1. TSIG
    2. /usr/local/etc/namedb/named.conf
    3. /usr/local/etc/namedb/dynamic/example.jp
  3. 動作検証
    1. 動作検証環境
    2. 動作検証環境構築および確認
    3. 動作検証の実施
  4. よくある質問とその答え
    1. Q.「response to SOA query was unsuccessful」という見慣れないエラーが発生しました。なぜです?設定は完璧なはずです!
    2. Q.実環境でテストしてしまいました!どうやって削除すればいいですか?ゾーンファイル編集すれば良いですか?
    3. Q.allow-update の指定が意味不明です。まずは解説を!
    4. Q.1IP=1TSIGキー設定したい場合はどうすればいいのでしょうか?
    5. Q.あれ?これ acl 定義すれば簡単になるんじゃあ?
    6. Q.ゾーンに対して広範に任意のレコードの変更ができてしまうようですが、特定のレコードだけに制限する方法はありますか?
    7. Q.もしかしてダイナミックDNSって設定は簡単なのですか?
  5. 更新したいレコードの制限
    1. update-policy による更新レコードの制限
      1. update-policy の指定方法
    2. ゾーン切り分けによる更新レコードの制限
      1. /usr/local/etc/namedb/named.conf
      2. /usr/local/etc/namedb/master/example.jp
      3. /usr/local/etc/namedb/dynamic/test.example.jp
  6. 【付録】動的更新の安定性について
    1. 一レコード追加の担保方法(上書き)
    2. レコードの上書きをしないようにする方法
    3. 安全な削除方法
  7. 参考文献
  8. 注釈

検証環境

検証作業内容

DNSコンテンツサーバーの基本設定

/etc/rc.conf

以下の設定を追加する。

named_enable="YES"
named_chrootdir="/var/named"
altlog_proglist="named"

※altlog_proglist が既に設定済みの場合、「 named」を追加する。

/usr/local/etc/namedb/named.conf

   1 --- /usr/local/etc/namedb/named.conf.sample     2018-09-13 22:12:15.847935000 +0900
   2 +++ /usr/local/etc/namedb/named.conf    2018-09-13 22:46:26.048986000 +0900
   3 @@ -16,15 +16,20 @@
   4         dump-file       "/var/dump/named_dump.db";
   5         statistics-file "/var/stats/named.stats";
   6 
   7 +       recursion       no;
   8 +       allow-query       { any; };
   9 +       allow-recursion   { none; };
  10 +       allow-query-cache { none; };
  11 +
  12  // If named is being used only as a local resolver, this is a safe default.
  13  // For named to be accessible to the network, comment this option, specify
  14  // the proper IP address, or delete this option.
  15 -       listen-on       { 127.0.0.1; };
  16 +       listen-on       { any; };
  17 
  18  // If you have IPv6 enabled on this system, uncomment this option for
  19  // use as a local resolver.  To give access to the network, specify
  20  // an IPv6 address, or the keyword "any".
  21 -//     listen-on-v6    { ::1; };
  22 +       listen-on-v6    { any; };
  23 
  24  // These zones are already covered by the empty zones listed below.
  25  // If you remove the related empty zones below, comment these lines out.

example.jp ゾーンの定義

/usr/local/etc/namedb/TSIGキー名.key

下記コマンドを実行してTSIGキーを発行する。

tsig-keygen -a hmac-sha256 TSIGキー名. > /usr/local/etc/namedb/TSIGキー名.key
chown bind:wheel /usr/local/etc/namedb/TSIGキー名.key
chmod 0400       /usr/local/etc/namedb/TSIGキー名.key

TSIG

TSIG(Transaction SIGnature)は、共有秘密鍵と一方向ハッシュ関数を使用してトランザクションレベルの認証をDNSプロトコルにもたらす仕組みである。

TSIGはTSIGキー名と秘密鍵(secret)からなり、以下のようなフォーマットになる(BIND9では)。

key "ns-www." {
    algorithm hmac-sha256;
    secret "PfzeGvXiOqtPOwQJY/iNFrvlD3/eKAHRZ0TbyK5GYII=";
};

肝心のTSIGキー名についてだが、

/usr/local/etc/namedb/named.conf

   1 --- /usr/local/etc/namedb/named.conf.orig       2018-09-13 22:46:26.048986000 +0900
   2 +++ /usr/local/etc/namedb/named.conf    2018-09-26 14:33:08.706240000 +0900
   3 @@ -383,3 +383,11 @@
   4         };
   5  };
   6  */
   7 +
   8 +include "/usr/local/etc/namedb/TSIGキー名.key";
   9 +
  10 +zone "example.jp" {
  11 +    type master;
  12 +    file "/usr/local/etc/namedb/dynamic/example.jp";
  13 +    allow-update { !{ !IPv4アドレス; !IPv6アドレス; any; }; key "TSIGキー名."; };
  14 +};
  15 

/usr/local/etc/namedb/dynamic/example.jp

$TTL    300

@       IN SOA ns.example.jp. domain.example.jp. (
                1          ; serial
                7200       ; refresh (2 hours)
                900        ; retry (15 minutes)
                2419200    ; expire (4 weeks)
                86400      ; minimum (1 day)
                )
        IN NS   ns
ns      IN A    IPv4アドレス
        IN AAAA IPv6アドレス

chown bind:wheel /usr/local/etc/namedb/dynamic/example.jp
chmod 0644       /usr/local/etc/namedb/dynamic/example.jp

動作検証

動作検証環境

動作検証環境構築および確認

動作検証の実施

以下のコマンドを実行することにより検証を行う(インタラクティブモードで検証)。

# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス
# /usr/local/bin/nsupdate -v -k TSIGキー名.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> update add test.example.jp. 5 TXT "hello, world."
> send
> quit
# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス
"hello, world."
# 

※検証時における「local」指定について

よくある質問とその答え

Q.「response to SOA query was unsuccessful」という見慣れないエラーが発生しました。なぜです?設定は完璧なはずです!

# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス
# /usr/local/bin/nsupdate -v -k TSIGキー名.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> update add test.example.jp. 5 TXT "hello, hello, world!"
> send
response to SOA query was unsuccessful
# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス
# 

A.甘いな。激甘だな。完璧などとおこがましい。 ログファイル1を見れば、 named 起動時に下記のようなメッセージを確認できるはず。

named[21358]: zone example.jp/IN: loading from master file /usr/local/etc/named/dynamic/example.jp failed: file not found
named[21358]: zone example.jp/IN: not loaded due to errors.

このケースの場合「file not found」つまりファイル名の指定に間違いがある(×etc/named/、○etc/namedb/)。精進すべし。

正しく設定した場合はログファイルには以下の通り出ている。

named[21470]: zone example.jp/IN: loaded serial 1

Q.実環境でテストしてしまいました!どうやって削除すればいいですか?ゾーンファイル編集すれば良いですか?

A.ダイナミックDNSで追加したならダイナミックDNSで削除しないと。 (サービス停止して)オフラインであるならゾーンファイル編集でもかまいません。 ゾーンファイルは独特な(機械処理故の限界な)フィードバックが行われていますので編集の際、驚かないでください。

# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス
"hello, world."
# /usr/local/bin/nsupdate -v -k tsig.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> update delete test.example.jp. TXT
> send
> quit
# dig +short ANY test.example.jp @ダイナミックDNSサーバーのIPアドレス

Q.allow-update の指定が意味不明です。まずは解説を!

A.BIND9のACL(Access Controll Lists)の癖となります。詳しくは参考文献をお読みください。 というのは不親切なので、解説すると、

  1. ACLの処理はリストの左から右へ評価されます。これはAND結合であるということを意味しません。
  2. よってブール論理で解釈するのではなく、ファーストマッチ・ファーストアウトで解釈する必要があります。
  3. 接続元IPアドレスに対して、以下の3つが判断されます。
    • match and accept(一致かつ受諾)
    • match and reject(一致かつ拒絶)
    • no match(一致しない、なので次のリストに委ねる)
  4. これは「!」(reject)と「 」(accept、「!」が指定されてない)のニュアンスが直感的に違うことを意味します(今まで一致・不一致に見えませんでしたか?)。
  5. ネスト({・・・})されていた場合、その内容について下記の2つの評価を行い(ここでは一致・不一致の直感とマッチする)、その結果に対して上記3つの判断が実施されます。
    • match(一致)
    • no match(不一致)
  6. 当然ネストの中に更にネストできるので、それは上記2評価(match または no match)の繰り返しとなる。

上記ルールを踏まえ、問題のルールを解析すると・・・。

  • 大枠は2つのリストに分かれる(「!」の意味に注意)。
    1. !{ !IPv4アドレス; !IPv6アドレス; any; } → match and reject
    2. key "TSIGキー名." → accept or reject
  • 最初のネストを分解すると、3つのリストに分かれる。
    1. !IPv4アドレス → 一致した場合「no match」という評価で完了
    2. !IPv6アドレス → 一致した場合「no match」という評価で完了
    3. any → 一致(常に一致する)した場合「match」という評価で完了

上記をまとめて解釈すると、

  • 最初のネスト内では以下の評価を行う(「!」の評価に注意)。
    1. 「IPv4アドレス『ではない』」の評価をする。
      • 指定された「IPv4アドレス」であれば「no match」として評価完了。
      • そうでないなら「match」として次の評価。
    2. 「IPv6アドレス『ではない』」の評価をする。
      • 指定された「IPv6アドレス」であれば「no match」として評価完了。
      • そうでないなら「match」として次の評価。
    3. 「any」(任意の全てのアドレス)に・・・とここまで来ると常に match と解釈され、評価が終了する。
    4. また any の時点で既に、「match」評価されてるので、この場合の「any」の指定のあり/無しに意味はない
      • ※人間の直感には反するから入れておいた方がいい。
  • このネストでの結果を全体「!」=match and reject で評価することから、「no match」(つまり次のリストの評価)の時は、次のリスト key が評価される。

Q.1IP=1TSIGキー設定したい場合はどうすればいいのでしょうか?

A.君のような勘のいいガ(ry。

裸の key "TSIGキー名." では一致しなかったときに拒絶される(accept or reject)ことから、ネストで保護してやれば(accept or no match)行けます(下記例参照)。

allow-update {
    { !{ !IPv4アドレス; any; }; key "TSIGキー名1."; };
    { !{ !IPv6アドレス; any; }; key "TSIGキー名2."; };
};

Q.あれ?これ acl 定義すれば簡単になるんじゃあ?

A.君のような勘のいいガ(ry。

acl "ACL名1" {
    !{ !IPv4アドレス; any; }; key "TSIGキー名1.";
};

acl "ACL名2" {
    !{ !IPv6アドレス; any; }; key "TSIGキー名2.";
};

zone "example.jp" {
    type master;
    file "/usr/local/etc/namedb/dynamic/example.jp";
    allow-update { "ACL名1"; "ACL名2"; };
};

Q.ゾーンに対して広範に任意のレコードの変更ができてしまうようですが、特定のレコードだけに制限する方法はありますか?

A.ない。正確には allow-update では解決しえない。 update-policy で設定するが、これにはトレードオフがあり、結果2つのソリューションがある(後述)。

Q.もしかしてダイナミックDNSって設定は簡単なのですか?

A.Yes。簡単だ。allow-update か update-policy の2つのうちのどれかがゾーン設定にあればよい。厳密には、以下の条件となる。

ACL設定もTSIG設定も allow-update のための設定でしかない。究極 any と設定することで動く(実際に運用しちゃダメ)。

更新したいレコードの制限

update-policy による更新レコードの制限

allow-update の代わりに下記のように記述する。

    update-policy { grant "TSIGキー名." NAME test.example.jp. TXT; };

上記の例では「test.example.jp.」に完全一致かつTXTレコードの場合のみアクセス(追加・変更・削除)が許可される。

複数指定したい場合は下記の通りとなる。

    update-policy {
        grant "TSIGキー名1." NAME test TXT;
        grant "TSIGキー名2." NAME test TXT;
    };

update-policy の指定方法

先に見て通り「;」で区切って、複数の設定を行うことができる。 また複数指定した場合、上から順に評価される(条件が確定するとそこで評価を終了する)。 その指定方法については以下のパターンとなっている(update-policy local; という特殊事例は除く)。

update-policy {
    モード "アイデンティティ" ルールタイプ レコード名 タイプ;
};
モード
  • grant と deny の2つある。通常は grant を使用することになる。
アイデンティティ
  • TSIGキー名を指定する(厳密には SIG(0) が絡むが・・・)。
ルールタイプ
  • NAME, SUBDOMAIN, ZONESTAB, WILDCARD, SELF, SELFWILD, MS-SELF, MS-SUBDOMAIN, KRB5-SELF, KRB5-SUBDOMAIN, TCP-SELF, 6TO4-SELF, EXTERNAL の13種類存在する(大文字小文字は区別しない)。
レコード名
  • 限定したいレコード名を指定する。
タイプ
  • 限定したいリソースタイプを指定する。これは0個以上(空白区切り)の指定が可能である(0個はほぼ全てのリソースタイプを許す※後述)。

ルールタイプとレコード名、タイプには相関(指定可能なオプション)に相違があるので、代表的なものをピックアップする。

ルールタイプ:NAME

update-policy {
    モード "アイデンティティ" NAME FQDN. [[タイプ] ...];
};

ルールタイプ:SUBDOMAIN

色々試してよくわからない振る舞いしたので使わないでください。 たぶん自分の解釈に問題があります!

update-policy {
    モード "アイデンティティ" subdomain ワード [[タイプ] ...];
};

ゾーン切り分けによる更新レコードの制限

※ゾーンを分ける以外は、今までの話と同じであることから、上記の流れを参照すること。 ここでは再度説明しない。

/usr/local/etc/namedb/named.conf

   1 --- /usr/local/etc/namedb/named.conf.orig       2018-09-13 22:46:26.048986000 +0900
   2 +++ /usr/local/etc/namedb/named.conf    2018-10-08 00:53:08.437564000 +0900
   3 @@ -383,3 +383,16 @@
   4         };
   5  };
   6  */
   7 +
   8 +include "/usr/local/etc/namedb/TSIGキー名.key";
   9 +
  10 +zone "example.jp" {
  11 +    type master;
  12 +    file "/usr/local/etc/namedb/master/example.jp";
  13 +};
  14 +
  15 +zone "test.example.jp" {
  16 +    type master;
  17 +    file "/usr/local/etc/namedb/dynamic/test.example.jp";
  18 +    allow-update { !{ !IPv4アドレス; !IPv6アドレス; any; }; key "TSIGキー名."; };
  19 +};
  20 

/usr/local/etc/namedb/master/example.jp

$TTL    300

@       IN SOA ns.example.jp. domain.example.jp. (
                20181001   ; serial
                7200       ; refresh (2 hours)
                900        ; retry (15 minutes)
                2419200    ; expire (4 weeks)
                86400      ; minimum (1 day)
                )
        IN NS   ns
ns      IN A    IPv4アドレス
        IN AAAA IPv6アドレス
test    IN NS   ns

/usr/local/etc/namedb/dynamic/test.example.jp

$TTL    300

@       IN SOA ns.example.jp. domain.example.jp. (
                1          ; serial
                7200       ; refresh (2 hours)
                900        ; retry (15 minutes)
                2419200    ; expire (4 weeks)
                86400      ; minimum (1 day)
                )
        IN NS   ns.example.jp.

※本ファイル作成後のオーナー・パーミッションに注意すること。

chown bind:wheel /usr/local/etc/namedb/dynamic/test.example.jp
chmod 0644       /usr/local/etc/namedb/dynamic/test.example.jp

【付録】動的更新の安定性について

ダイナミックDNSはDNSプロトコルに乗って更新を行うため、いくつかの不安定さがある。

よって更新(追加・変更・削除)において一定のパターンがある。それを以下に示す。

一レコード追加の担保方法(上書き)

# /usr/local/bin/nsupdate -v -k tsig.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> prereq yxrrset test.example.jp. TXT
> update delete test.example.jp. TXT
> send
> prereq nxrrset test.example.jp. TXT
> update add test.example.jp. 5 TXT "THIS IS A TEST MESSAGE."
> send
> quit

レコードの上書きをしないようにする方法

# /usr/local/bin/nsupdate -v -k tsig.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> prereq nxrrset test.example.jp. TXT
> update add test.example.jp. 5 TXT "THIS IS A TEST MESSAGE."
> send
> quit

安全な削除方法

# /usr/local/bin/nsupdate -v -k tsig.key
> local allow-updateで許可したダイナミックDNSクライアントのIPアドレス
> server ダイナミックDNSサーバーのIPアドレス
> prereq nxrrset test.example.jp. TXT
> update delete test.example.jp. TXT
> send
> quit

参考文献

注釈

  1. named のデフォルトファシリティは daemon であること、/etc/syslog.conf を変更してない場合、デフォルトのログファイルは /var/log/messages である。 (1)

bind9/ダイナミックDNSサーバー (最終更新日時 2018-11-16 23:02:42 更新者 NorikatsuShigemura)