KITA Eng.

北海道でサーバー技術者として歩み出したひとが綴るblog。

BIND9 ゾーンファイルの編集の罠

BIND9にて、ゾーン情報の編集を以下の流れでやった場合に、変更後の情報が反映されない事象が発生しました。

  1. 現在のゾーンファイル(zone.example.com)を別名にコピー(zone.example.com_copy)して、内容を編集(シリアル値はもちろんあげておく)
  2. 1.の作業から少ししてから、現在のゾーンファイル(zone.example.com)を編集して、BINDに再読み込みさせる
  3. 2.の作業の後に、現在のゾーンファイル(zone.example.com)を別名(zone.example.com_back)にして、zone.example.com_copyをzone.example.comにして、BINDに再読み込みさせる

この場合、2. においては、意図したとおりゾーンファイルの情報が再読み込みされるにも関わらず、3. の場合には、 再読み込みがされません(もちろんシリアル値は、大きくなっている)。

コマンドで上記を追うと、以下のようになる。

# cat zone.example.com
;$ORIGIN .
;$TTL 600           ; 10 minutes
$TTL 60             ; 1 minutes
@           IN SOA  ns.example.com. abuse.example.com. (
                2019090900 ; serial
                3600       ; refresh (1 hour)
                900        ; retry (15 minutes)
                604800     ; expire (1 week)
                86400      ; minimum (1 day)
                )
            NS  ns.example.com.
ns          A   172.16.3.1
test1           A   172.16.3.1


# dig +norec +short @172.16.3.1 test1.example.com
172.16.3.1
# dig +norec +short @172.16.3.1 test2.example.com   # 登録がないのでレスポンスなし
# dig +norec +short @172.16.3.1 test3.example.com   # 登録がないのでレスポンスなし


# cp -iva zone.example.com zone.example.com_copy
'zone.example.com' -> 'zone.example.com_copy'

# vi zone.example.com_copy 
# cat zone.example.com_copy 
;$ORIGIN .
;$TTL 600           ; 10 minutes
$TTL 60             ; 1 minutes
@           IN SOA  ns.example.com. abuse.example.com. (
                2019090910 ; serial    ; <-- 大きめに変更
                3600       ; refresh (1 hour)
                900        ; retry (15 minutes)
                604800     ; expire (1 week)
                86400      ; minimum (1 day)
                )
            NS  ns.example.com.
ns          A   172.16.3.1
test1           A   172.16.3.1
test3           A   172.16.3.1      ; <-- 追加した


# ls -la
total 16
drwxr-xr-x 2 root root 4096 Sep  9 18:01 .
drwxrwxr-x 3 root bind 4096 Sep  9 17:54 ..
-rw-r--r-- 1 bind bind  334 Sep  9 17:56 zone.example.com
-rw-r--r-- 1 bind bind  355 Sep  9 18:01 zone.example.com_copy

# vi zone.example.com
# cat zone.example.com
;$ORIGIN .
;$TTL 600           ; 10 minutes
$TTL 60             ; 1 minutes
@           IN SOA  ns.example.com. abuse.example.com. (
                2019090901 ; serial
                3600       ; refresh (1 hour)
                900        ; retry (15 minutes)
                604800     ; expire (1 week)
                86400      ; minimum (1 day)
                )
            NS  ns.example.com.
ns          A   172.16.3.1
test1           A   172.16.3.1
test2           A   172.16.3.1      ; <-- 追加した


# vi zone.example.com
# ls -la
total 16
drwxr-xr-x 2 root root 4096 Sep  9 18:02 .
drwxrwxr-x 3 root bind 4096 Sep  9 17:54 ..
-rw-r--r-- 1 bind bind  355 Sep  9 18:02 zone.example.com   # <-- こっちのタイムスタンプの方が新しい
-rw-r--r-- 1 bind bind  355 Sep  9 18:01 zone.example.com_copy


# rndc reload example.com    # 【リロード(1)】
zone reload queued

# dig +norec +short @172.16.3.1 test1.example.com
172.16.3.1
# dig +norec +short @172.16.3.1 test2.example.com   # <-- 追加したので出るようになった(意図通り)
172.16.3.1
# dig +norec +short @172.16.3.1 test3.example.com


# mv -iv zone.example.com zone.example.com_back
'zone.example.com' -> 'zone.example.com_back'
# mv -iv zone.example.com_copy zone.example.com
'zone.example.com_copy' -> 'zone.example.com'

# ls -la
total 16
drwxr-xr-x 2 root root 4096 Sep  9 18:04 .
drwxrwxr-x 3 root bind 4096 Sep  9 17:54 ..
-rw-r--r-- 1 bind bind  355 Sep  9 18:01 zone.example.com   # <-- mvで入れ替えたのでタイムスタンプが古いママ
-rw-r--r-- 1 bind bind  355 Sep  9 18:02 zone.example.com_back

 
# rndc reload example.com   # 【リロード(2)】
zone reload up-to-date
# dig +norec +short @172.16.3.1 test1.example.com
172.16.3.1
# dig +norec +short @172.16.3.1 test2.example.com   # <-- でないはずなのに出る
172.16.3.1
# dig +norec +short @172.16.3.1 test3.example.com   # <-- でるはずなのに出ない


# touch zone.example.com    # <-- タイムスタンプを新しくする
# ls -la
total 16
drwxr-xr-x 2 root root 4096 Sep  9 18:04 .
drwxrwxr-x 3 root bind 4096 Sep  9 17:54 ..
-rw-r--r-- 1 bind bind  355 Sep  9 18:05 zone.example.com
-rw-r--r-- 1 bind bind  355 Sep  9 18:02 zone.example.com_back
# rndc reload example.com   # 【リロード(3)】
zone reload queued


# dig +norec +short @172.16.3.1 test1.example.com
172.16.3.1
# dig +norec +short @172.16.3.1 test2.example.com   # <-- でなくなった(意図通り)
# dig +norec +short @172.16.3.1 test3.example.com   # <-- でるようになった(意図通り)
172.16.3.1

syslogには、以下のようにログが出力される。
【リロード(2)】のときには、「zone example.com/IN: loaded serial 2019090901」のように読み込んだファイルのシリアル値のログが出ていない。

Sep  9 18:03:50 localhost named[25597]: received control channel command 'reload example.com'
Sep  9 18:03:50 localhost named[25597]: zone example.com/IN: loaded serial 2019090901
Sep  9 18:04:52 localhost named[25597]: received control channel command 'reload example.com'
Sep  9 18:05:13 localhost named[25597]: received control channel command 'reload example.com'
Sep  9 18:05:13 localhost named[25597]: zone example.com/IN: loaded serial 2019090910

これは、ゾーンファイルの再読込について、ゾーンファイルの中身(シリアル値)が確認される前に、ゾーンファイルのタイムスタンプをBINDが確認しているためで、現在読み込まれているファイルのタイムスタンプよりも新しくなっていないと、再読込されない仕様なようです。

Tuning your BIND configuration effectively for zone transfers (particularly with many frequently-updated zones) - Performance

上のページの3段落目に、

the decision on whether or not the zone has changed is the serial number that is maintained in the zone's SOA record (although when a master reloads, it does also check the timestamp on the zone data file first, before checking the zone's SOA).

と書かれています。

普通に更新したら、ファイルのタイムスタンプは新しくなるだろうけど、上のように事前に用意しておいたファイルと置き換えるときには注意が必要なようです。