2026年6月14日
2026年6月14日
djbdns から CoreDNS へ移行する手順|dnscache のキャッシュとアクセス制限を forward・cache・acl で置き換える
djbdns のキャッシュDNS(dnscache)を CoreDNS に移すときは、dnscache の設定ディレクトリにある「待ち受け IP(env/IP)」「問い合わせを許すクライアント(root/ip)」「問い合わせ先(root/servers)」を、それぞれ CoreDNS の bind・acl・forward プラグインに置き換え、別の IP で並行稼働させて答えを比べてから切り替えます。この記事では、対応関係の洗い出しから Corefile の書き方、比較の方法、切り戻しの準備までを順に説明します。
先に大事な前提を1つ挙げます。CoreDNS は標準の構成では、ルートサーバーからたどって名前を解決する「フル再帰」を行いません。 forward プラグインで、上流の再帰リゾルバに問い合わせを転送して結果をキャッシュする形になります。dnscache がルートサーバーから自分で解決していた場合は、上流をどこにするかを最初に決める必要があります。
なお、権威DNS側(tinydns の data ファイル)を CoreDNS に移す手順は別の記事で扱っています。この記事はキャッシュ側に絞った内容です。
手順1:今の dnscache の設定を洗い出す
dnscache は設定ファイルを持たず、サービスディレクトリの中のファイルで動作が決まります。まず、次の場所を確認して書き出します(パスは /etc/dnscache の例です)。
cd /etc/dnscache
cat env/IP
ls root/ip
ls root/servers
cat root/servers/@
ls env
確認する項目と、CoreDNS での置き換え先の対応は次のとおりです。
| dnscache | 役割 | CoreDNS での置き換え |
|---|---|---|
| env/IP | 待ち受ける IP アドレス | bind プラグイン |
| root/ip/ のファイル | 問い合わせを許すクライアント | acl プラグイン |
| root/servers/@ | ルートサーバー一覧、または上流のキャッシュ | forward プラグイン(上流の再帰リゾルバ) |
| env/FORWARDONLY | servers/@ を上流キャッシュとして扱う | forward プラグイン |
| root/servers/ドメイン名 | 特定のドメインだけ別のサーバーに聞く | ドメインごとのサーバーブロック+forward |
| env/CACHESIZE | キャッシュの大きさ(バイト数) | cache プラグイン(件数で指定) |
ファイル名の読み方にも注意します。root/ip に 192.0.2 という空ファイルがあれば 192.0.2.0/24 からの問い合わせを許可し、10 なら 10.0.0.0/8 を許可します。CoreDNS では CIDR 表記に書き直します。
env/FORWARDONLY があれば、servers/@ に書かれているのは上流のキャッシュサーバーなので、そのまま forward 先に使えます。無ければ servers/@ はルートサーバー一覧で、dnscache が自分で再帰していたことになります。
手順2:上流の再帰リゾルバを決める
dnscache がフル再帰をしていた場合の選択肢は、主に次の2つです。
- 社内に再帰リゾルバを置く:Unbound などを同じサーバーか別のサーバーで動かし、CoreDNS はそこへ転送します。外部に問い合わせ内容を渡さずに済み、DNSSEC の検証も Unbound 側で行えます。
- プロバイダやパブリックDNSに転送する:構成は簡単になりますが、問い合わせの内容が外部の事業者に渡ります。利用規約とプライバシーの扱いを確認し、社内の方針と照らし合わせて判断してください。
dnscache は DNSSEC の検証をしないため、移行後に検証を始めたい場合は上流の再帰リゾルバで有効にするのが現実的です。CoreDNS の dnssec プラグインはゾーンに署名するためのもので、検証のためのものではない点も押さえておいてください。
手順3:Corefile を書く
洗い出した内容を Corefile に置き換えます。ここでは、旧 dnscache が 192.0.2.10 で動いていて、並行稼働のために CoreDNS を 192.0.2.11 で動かす例にします。上流の再帰リゾルバは 192.0.2.20 と 192.0.2.21、社内ゾーン internal.example は社内の tinydns(10.0.0.53)に聞いていたものとします。アドレスはすべて説明用なので、実際の値に置き換えてください。
.:53 {
bind 192.0.2.11
acl {
allow net 127.0.0.0/8 192.0.2.0/24 10.0.0.0/8
block
}
forward . 192.0.2.20 192.0.2.21
cache 3600
errors
log
}
internal.example:53 {
bind 192.0.2.11
acl {
allow net 127.0.0.0/8 192.0.2.0/24 10.0.0.0/8
block
}
forward . 10.0.0.53
cache 300
errors
}
それぞれの書き方の意味は次のとおりです。
- bind:待ち受ける IP を指定します。書かないとすべてのアドレスで待ち受けるため、同じサーバーで dnscache や tinydns が動いていると 53番ポートが衝突します。
- acl:上から順に評価され、
allow netに当てはまれば許可、最後のblockで残りをすべて拒否します。root/ip で許可していた範囲だけを書き、インターネット全体に開いたキャッシュDNS(オープンリゾルバ)にならないようにします。 - forward:
.はすべての名前を意味し、上流へ転送します。サーバーブロックはより長く一致するゾーンが優先されるので、internal.example の問い合わせは下のブロックで処理されます。これが dnscache の root/servers/ドメイン名 に相当します。 - cache:数値は保持する最大の TTL(秒)です。dnscache の CACHESIZE はバイト数で、CoreDNS の cache は件数で上限を決める仕組みなので、値をそのまま移すことはできません。まずは既定のまま動かし、メモリ使用量を見て調整します。
- errors と log:errors はエラーを、log は問い合わせ1件ごとを出力します。log は量が多いので、並行稼働の比較が終わったら外すか、出力先を考えます。
Corefile 内でのプラグインの並び順は動作の順番に影響しません。処理の順番は CoreDNS のビルド時に決まっています。
社内ゾーンの転送先が権威サーバーの場合、そのゾーンの中でさらに別のサーバーへ委任している部分があると、forward はその委任をたどりません。委任先がある場合は、委任先のゾーンにも個別のサーバーブロックを用意してください。
acl をはじめ、ここで使っているプラグインは公式に配布されている CoreDNS のバイナリに含まれています(2026年10月現在)。独自ビルドを使っている場合は coredns -plugins で組み込まれているプラグインを確認できます。
手順4:CoreDNS を起動して並行稼働させる
公式に配布されているバイナリを /usr/local/bin に置き、専用ユーザーで動かす systemd の unit を用意します。53番ポートを使うため、root ではなく権限を絞ったユーザーに必要な能力だけを与えます。
# /etc/systemd/system/coredns.service
[Unit]
Description=CoreDNS
After=network-online.target
Wants=network-online.target
[Service]
User=coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
AmbientCapabilities=CAP_NET_BIND_SERVICE
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo useradd -r -s /usr/sbin/nologin coredns
sudo systemctl daemon-reload
sudo systemctl enable --now coredns
journalctl -u coredns -n 50
起動したら、旧 dnscache と新しい CoreDNS に同じ問い合わせをして答えを比べます。よく使われる名前の一覧(社内ゾーンの名前も含める)を用意し、差分だけを表示します。
while read name type; do
old=$(dig +short @192.0.2.10 "$name" "$type" | sort)
new=$(dig +short @192.0.2.11 "$name" "$type" | sort)
[ "$old" = "$new" ] || echo "DIFF: $name $type"
done < names.txt
sort してから比べるのは、複数の答えが返る名前で順番が入れ替わるためです。CDN を使っているサイトなどは問い合わせごとに答えが変わるので、差分が出たものは個別に中身を見て判断します。
アクセス制限も確認します。許可していないネットワークから問い合わせると、dnscache は応答せずにタイムアウトしますが、CoreDNS の acl は block の場合 REFUSED を返します。挙動が違うので、監視やクライアント側の想定に影響が無いかを見ておきます。応答自体を返したくない場合は block の代わりに drop を使います。
手順5:切り替えと切り戻し
比較で問題が無ければ切り替えます。方法は2通りあります。
- クライアントの参照先を変える:DHCP の配布設定や各サーバーの resolv.conf を新しい IP に変えます。少しずつ移せる反面、変更箇所が多くなります。
- IP アドレスを引き継ぐ:dnscache を止め、CoreDNS の bind を旧 IP(192.0.2.10)に変えて再起動します。クライアント側の変更は要りません。
IP を引き継ぐ場合、dnscache は supervise に監視されているので、止めるだけでなく、OS の再起動時にも起動しないようにしておきます。
sudo svc -d /service/dnscache
sudo touch /etc/dnscache/down
down ファイルがあると、svscan は次回の起動時にもそのサービスを止めたままにします。切り戻すときは、CoreDNS を止めて down ファイルを消し、svc -u /service/dnscache で起動するだけです。そのため、/etc/dnscache は移行が落ち着くまで消さずに残しておきます。作業前にはこのディレクトリをバックアップし、切り替えは問い合わせの少ない時間帯に行ってください。
切り替え後しばらくは、journal のエラーと、クライアントからの名前解決の失敗の問い合わせが無いかを見守ります。
まとめ
- dnscache の env/IP・root/ip・root/servers を、CoreDNS の bind・acl・forward に対応させます。
- CoreDNS は標準ではフル再帰をしないため、上流の再帰リゾルバ(社内の Unbound など)を決めます。
- 特定ドメインの転送はドメインごとのサーバーブロックで、アクセス制限は acl で書き、オープンリゾルバにしないよう範囲を絞ります。
- 別の IP で並行稼働させて答えを比べ、拒否時の挙動の違い(無応答と REFUSED)も確認します。
- down ファイルで dnscache を止めておき、すぐ切り戻せる状態で切り替えます。