2026年6月7日
2026年6月7日
BIND から CoreDNS へ移行する手順|社内向けの転送・キャッシュ・内部名を Corefile に置き換える
社内LANやVPCの中で、BIND を「クライアントの問い合わせを受けて上流へ転送し、キャッシュし、社内だけの名前にも答える」DNS として使っている場合、CoreDNS への移行は forward・cache・acl・file(または hosts)の各プラグインへの置き換えで進められます。この記事では、BIND が担っている役割の棚卸し、CoreDNS では再現できない機能の見極め、named.conf と Corefile の対応、同じサーバーでの並行テスト、クライアントの切り替えと切り戻しまでを説明します。
対象は「キャッシュDNS・転送DNS」としての BIND です。外部に公開している権威ゾーンを CoreDNS で配信する移行は論点が異なり、ゾーン転送や DNSSEC の署名の扱いが中心になります。プラグインの種類や書式は CoreDNS の版によって変わるため、手元のバイナリで coredns -version と coredns -plugins を確認しながら読み進めてください。記載は執筆時点(2026年10月現在)の情報です。作業は検証環境から始め、named.conf と関連ファイル一式のバックアップを取ってから行います。
手順1:BIND が何をしているかを棚卸しする
named.conf は include で複数のファイルに分かれていることが多いため、まず全体を1つにまとめて表示します。
# include 先も含めた設定を、正規化した形で表示する
named-checkconf -p > /root/named-full.txt
# 実際にどんな名前が問い合わされているかを確認する(ログ量が増えるので短時間だけ)
rndc querylog on
# ... 業務時間帯にしばらく記録してから
rndc querylog off
named-full.txt から、次の項目を書き出します。この一覧が、Corefile に置き換える対象と、置き換えられない対象を決める材料になります。
listen-on:どのアドレスで待ち受けているかallow-query・allow-recursion:誰からの問い合わせを受け付けているかforwardersとforward first/forward only:上流をどこにしているか、上流が落ちたときに自力で解決するかzone ... type forward:特定のドメインだけ別のDNSへ転送する設定(条件付き転送)zone ... type primary:社内だけで使う名前を、BIND 自身がゾーンファイルで持っているかview、response-policy(RPZ)、dnssec-validation:見落としやすい機能
問い合わせログからは、社内の名前と外部の名前の比率や、逆引き(in-addr.arpa)が多く使われていないかを見ておきます。テストに使う名前の一覧も、このログから作ると漏れが減ります。
手順2:CoreDNS で再現できない機能を見極める
CoreDNS は転送とキャッシュを得意としますが、BIND のキャッシュDNSと同じ動きをするわけではありません。次の3点は移行を止める理由になり得るので、先に結論を出します。
フルの再帰解決:forwarders を設定せず、BIND がルートサーバーから自力で名前を辿っている場合、CoreDNS の標準プラグインには同じ機能がありません。上流に別のキャッシュDNS(Unbound、クラウド事業者が提供するリゾルバーなど)を置いて forward で渡す構成にします。libunbound を組み込む外部プラグインもありますが、CoreDNS を自分でビルドし直す必要があるため、運用の手間と合わせて判断します。
forward first の挙動:BIND の forward first は、上流が応答しないときに自力の再帰解決へ切り替えます。CoreDNS の forward にはこの切り替えがなく、動きは forward only に近くなります。上流を複数指定し、ヘルスチェックで落ちた上流を外す形で可用性を確保します。
DNSSEC の検証:BIND の dnssec-validation auto は、受け取った応答の署名を検証します。CoreDNS の標準プラグインには、キャッシュDNSとして署名を検証する機能は含まれていません。検証が必要な環境では、検証を行うリゾルバーを上流に置き、CoreDNS はその手前で転送とキャッシュを受け持つ形にします。
RPZ(応答ポリシーゾーン)で特定の名前を書き換えている場合も、そのままの形では移せません。hosts や template プラグインで近い動きを作れることはありますが、規模が大きいなら移行の範囲から外すことを検討します。
手順3:named.conf を Corefile に置き換える
次のような BIND の設定を例にします。
# named.conf(抜粋)
acl "internal" { 10.0.0.0/8; 192.168.0.0/16; 127.0.0.1; };
options {
listen-on { 10.0.0.10; 127.0.0.1; };
recursion yes;
allow-recursion { internal; };
forwarders { 192.0.2.53; 198.51.100.53; };
forward only;
};
zone "corp.example" {
type forward;
forward only;
forwarders { 10.0.1.53; };
};
zone "lab.internal" {
type primary;
file "/etc/bind/db.lab.internal";
};
これを Corefile にすると次のようになります。
# 各サーバーブロックで共通に使う設定
(common) {
bind 10.0.0.10 127.0.0.1
acl {
allow net 10.0.0.0/8 192.168.0.0/16 127.0.0.0/8
block
}
errors
log . {
class denial error
}
}
# 外部の名前:上流へ転送してキャッシュする
. {
import common
forward . 192.0.2.53 198.51.100.53 {
policy sequential
health_check 5s
}
cache 3600
loop
reload
}
# 条件付き転送:corp.example は社内の DNS へ
corp.example {
import common
forward . 10.0.1.53
cache 300
}
# 社内だけの名前:BIND のゾーンファイルをそのまま読む
lab.internal {
import common
file /etc/coredns/zones/db.lab.internal
}
置き換えのポイントは次のとおりです。
- **
listen-on→bind**:書かないと、すべてのアドレスで待ち受けます。社外に面したアドレスを持つサーバーでは、書き漏れがそのままオープンリゾルバーにつながるため注意します。 - **
allow-recursion→acl**:許可するネットワークをallowで並べ、最後のblockでそれ以外を拒否します。拒否された問い合わせには REFUSED が返ります。 - **
forwarders→forward**:policyは上流の選び方で、既定はランダムです。BIND と同じく「1台目を優先し、落ちたら次へ」にしたい場合はsequentialを指定します。 - **
type forwardのゾーン → 個別のサーバーブロック**:CoreDNS は問い合わせの名前にもっとも長く一致するサーバーブロックを選ぶため、corp.exampleのブロックに書いたforwardが優先されます。社内の逆引きを社内DNSへ渡している場合は、10.in-addr.arpaなどのブロックも同じ形で作ります。 - **
type primaryの社内ゾーン →file**:BIND のゾーンファイルは多くの場合そのまま読めます。数件の名前だけなら、hostsプラグインで hosts 形式のファイルから答える方法もあります。 - **
(common)とimport**:スニペットと呼ばれる書き方で、同じ設定を各ブロックに繰り返し書かずに済みます。
cache の数値は、キャッシュに保持する最大の TTL(秒)です。BIND の max-cache-ttl に近い意味ですが、既定値は異なるため、移行前と同じ考え方で決め直します。loop は、上流の設定がめぐりめぐって自分自身に戻ってくる転送ループを起動時に検出するプラグインです。上流に 127.0.0.53(systemd-resolved)を書いてしまうような誤りに気付けます。reload を入れておくと、Corefile の変更を定期的に検知して読み直します。
view で社内と社外に違う答えを返していた場合は、CoreDNS の view プラグインで送信元ごとにサーバーブロックを選び分けます。比較的新しいプラグインなので、手元の版に含まれているかを coredns -plugins で確認します。
手順4:同じサーバーで並行テストする
BIND を動かしたまま、検証用に別ポートで CoreDNS を起動します。上の Corefile をコピーし、各サーバーブロックの名前に :1053 を付けたもの(.:1053、corp.example:1053、lab.internal:1053)を検証用にします。
coredns -conf /etc/coredns/Corefile.test
次に、問い合わせログから作った「名前 型」の一覧で、BIND(53番)と CoreDNS(1053番)の答えを比べます。
#!/bin/bash
# names.txt には「www.example.com A」のように1行に1件ずつ書く
while read -r name type; do
a=$(dig +short @127.0.0.1 -p 53 "$name" "$type" | sort)
b=$(dig +short @127.0.0.1 -p 1053 "$name" "$type" | sort)
[ "$a" = "$b" ] || echo "DIFF: $name $type"
done < names.txt
ここでつまずきやすいのは、外部の名前の差分です。CDN やラウンドロビンを使っているサイトは問い合わせのたびに答えが変わるため、差分が出ても異常とは限りません。差分が出た名前は、手で dig を繰り返して判断します。一方、社内の名前、条件付き転送の先の名前、逆引き、存在しない名前(NXDOMAIN になるか)で差分が出たら、設定の写し漏れを疑います。
あわせて、許可していないネットワークから問い合わせて REFUSED が返ることも確認します。拒否の確認は見落とされがちですが、移行後に外部から使われてしまう事故を防ぐ要の確認です。
手順5:切り替えと切り戻し
もっとも単純で戻しやすいのは、同じサーバー・同じ IP アドレスで BIND と入れ替える方法です。クライアントの DNS 設定(DHCP で配っている DNS サーバーや resolv.conf)を変えずに済みます。
# 本番用の Corefile に戻してから入れ替える(サービス名は named または bind9)
systemctl disable --now named
systemctl enable --now coredns
# 53番で CoreDNS が待ち受けているかを確認
ss -lunp 'sport = :53'
dig @10.0.0.10 www.example.com A
CoreDNS は root 以外のユーザーで動かし、53番で待ち受けるための権限は systemd のユニットで AmbientCapabilities=CAP_NET_BIND_SERVICE のように与える構成が一般的です。切り替えた直後はキャッシュが空なので、しばらく応答が遅めになるのは想定どおりです。
問題が出たら、systemctl stop coredns のあと systemctl start named で元に戻せます。named.conf とゾーンファイルは、切り替え後もしばらく消さずに残しておきます。
別のサーバーに CoreDNS を立てて IP アドレスごと切り替える場合は、まず一部のクライアントだけ DNS の向き先を変えて様子を見ます。「優先 DNS を BIND、代替 DNS を CoreDNS」のように混ぜる方法は、どちらに問い合わせるかがクライアントの実装次第で、答えの食い違いの原因を追いにくくなるため避けたほうが無難です。
移行後に変わる運用
- キャッシュの削除:
rndc flushに相当するコマンドは無く、消したいときは再起動します。 - 設定の反映:
reloadプラグインが Corefile の変更を検知して読み直すため、検証環境で起動を確かめてから本番の Corefile を書き換えます。 - ログと統計:querylog の代わりに
logプラグイン(拒否やエラーだけに絞る)、rndc statsの代わりにprometheusプラグインのメトリクスを使います。
まとめ
- 最初に
named-checkconf -pと問い合わせログで、BIND が担っている転送・キャッシュ・社内ゾーン・アクセス制限を書き出します。 - フルの再帰解決、
forward firstのような自力解決への切り替え、DNSSEC の検証は CoreDNS の標準プラグインでは再現できないため、上流に別のリゾルバーを置く構成を検討します。 listen-onはbind、allow-recursionはacl、forwardersはforward、条件付き転送は個別のサーバーブロック、社内ゾーンはfileに置き換えます。- 別ポートで並行稼働させて答えを突き合わせ、同じ IP での入れ替えにすれば切り戻しも簡単です。