2026年5月24日
2026年5月24日
DNSサーバーを自前で構築する方法(BIND):プライマリ/セカンダリ2台構成とドメイン委任まで
BINDで自前のDNSサーバーを公開するなら、1台だけで済ませず、プライマリとセカンダリの2台を別のネットワークに置き、TSIG鍵で守ったゾーン転送で同期させるのが基本です。この記事では、2台のVPS(Ubuntu/Debian を想定)で権威DNSサーバーを構築し、レジストラでネームサーバーとグルーレコードを登録して委任を完了させ、更新と監視を回すところまでを手順で説明します。
構成と前提
この記事では、説明用のドメインとアドレスで次の構成を作ります。
| 役割 | ホスト名 | IPアドレス |
|---|---|---|
| プライマリ | ns1.example.com | 192.0.2.53 |
| セカンダリ | ns2.example.com | 198.51.100.53 |
ゾーンを編集するのはプライマリだけで、セカンダリはゾーン転送でコピーを受け取ります。2台を同じデータセンターや同じVPS事業者の同じ拠点に置くと、その拠点の障害で両方が止まります。可能なら事業者か拠点を分けてください。
前提として、各サーバーに固定のグローバルIPがあり、UDPとTCPの53番を外部から受け付けられること、OSとBINDのセキュリティ更新を継続して当てられることが必要です。WordPressサイトの名前解決だけが目的なら、マネージドDNSを使うほうが運用の負担は小さく済みます。自前運用は「ゾーンを自分で管理したい」「学習や社内要件のため」といった理由がある場合の選択肢です。
手順1:両方のサーバーにBINDを入れる
sudo apt update
sudo apt install bind9 bind9-utils bind9-dnsutils
# 53番を開ける(ufw の例。proto を省略すると TCP/UDP の両方が対象)
sudo ufw allow 53
TCPの53番を閉じたままにすると、大きな応答やゾーン転送が失敗します。クラウドのセキュリティグループなどOS外のファイアウォールも確認してください。
BINDはISCが複数の系列を並行してサポートしており、系列ごとにサポート期間が異なります。ディストリビューションが提供する版がどの系列か、セキュリティ修正が提供されているかを確認しておきます。各系列のサポート状況は変わるため、ISCの公式サイトで最新の情報を確認してください(2026年10月現在)。
手順2:ゾーン転送用のTSIG鍵を作る
ゾーン転送をIPアドレスだけで制限するより、共有鍵で署名するTSIGを併用するほうが安全です。プライマリで鍵を作り、同じファイルをセカンダリに安全な方法(SSH経由のコピーなど)で配ります。
# プライマリで実行
sudo tsig-keygen -a hmac-sha256 xfer-key | sudo tee /etc/bind/xfer.key > /dev/null
sudo chown root:bind /etc/bind/xfer.key
sudo chmod 640 /etc/bind/xfer.key
生成されたファイルには key "xfer-key" { algorithm hmac-sha256; secret "..."; }; の形で鍵が書かれています。この値は秘密情報なので、Gitリポジトリやチャットに貼らないでください。
手順3:プライマリを設定する
/etc/bind/named.conf.options で、公開用の権威サーバーとして再帰を止めます。
include "/etc/bind/xfer.key";
options {
directory "/var/cache/bind";
recursion no;
allow-transfer { none; };
version "not disclosed";
listen-on { any; };
listen-on-v6 { any; };
};
// セカンダリとの通信をTSIG鍵で署名する
server 198.51.100.53 {
keys { xfer-key; };
};
/etc/bind/named.conf.local にゾーンを定義し、転送は鍵を持つ相手だけに許可します。
zone "example.com" {
type primary;
file "/etc/bind/zones/db.example.com";
allow-transfer { key "xfer-key"; };
notify yes;
};
ゾーンファイル /etc/bind/zones/db.example.com には、ns1 と ns2 の両方をNSとして書き、それぞれのAレコードも入れます。
$TTL 3600
@ IN SOA ns1.example.com. hostmaster.example.com. (
2026101801 ; serial
3600 ; refresh
900 ; retry
1209600 ; expire
300 ) ; negative cache TTL
IN NS ns1.example.com.
IN NS ns2.example.com.
ns1 IN A 192.0.2.53
ns2 IN A 198.51.100.53
@ IN A 203.0.113.10
www IN CNAME example.com.
type primary が通らない古い系列では type master と書きます。設定したら構文を確認してから読み込みます。
sudo mkdir -p /etc/bind/zones # ゾーンファイルを置く前に作成
sudo named-checkconf
sudo named-checkzone example.com /etc/bind/zones/db.example.com
sudo rndc reload
手順4:セカンダリを設定する
セカンダリにも同じ xfer.key を置き、options は再帰なしにしたうえで、プライマリとの通信に鍵を使うよう指定します。
include "/etc/bind/xfer.key";
options {
directory "/var/cache/bind";
recursion no;
allow-transfer { none; };
version "not disclosed";
};
server 192.0.2.53 {
keys { xfer-key; };
};
zone "example.com" {
type secondary;
primaries { 192.0.2.53 key "xfer-key"; };
file "/var/cache/bind/db.example.com";
};
primaries と type secondary は新しい表記で、古い系列では masters と type slave を使います。セカンダリが受け取ったゾーンは、Ubuntu/Debian では AppArmor の制約上、書き込みが許可されている /var/cache/bind に保存するのが無難です。また、セカンダリのゾーンファイルは既定でテキストではない形式で保存される場合があり、中身を読むときは named-compilezone で変換します。
sudo named-checkconf
sudo rndc reload
# 転送を手動で要求して確認
sudo rndc retransfer example.com
sudo journalctl -u named --since "5 minutes ago"
ログに転送の完了が記録されていれば成功です。TSIG や REFUSED を含むエラーが出る場合は、鍵の名前と値が両方で一致しているか、プライマリの allow-transfer と server の指定が正しいかを見直します。
手順5:外部から動作を確かめる
委任する前に、手元のPCなど外部から両方のサーバーに直接問い合わせます。
# 両方が同じシリアルで権威応答を返すか(flags に aa が付く)
dig @192.0.2.53 example.com SOA +norec
dig @198.51.100.53 example.com SOA +norec
# 再帰問い合わせが拒否されるか(REFUSED になればよい)
dig @192.0.2.53 www.example.org A
# 鍵のない相手からのゾーン転送が拒否されるか(Transfer failed になればよい)
dig @192.0.2.53 example.com AXFR
再帰が通ってしまう場合はオープンリゾルバーとして悪用されるおそれがあるため、公開前に設定を直します。
手順6:レジストラで委任する
ネームサーバー名が自分のドメインの中(ns1.example.com など)にある場合、親ゾーン(.com や .jp)に「ns1 のIPアドレスはこれ」という情報が要ります。これがグルーレコードです。多くのレジストラでは「ネームサーバーのホスト登録」などの名称で、ホスト名とIPアドレスを登録する画面があります(名称はレジストラにより異なります)。
- ns1.example.com と ns2.example.com をそれぞれのIPでホスト登録する
- ドメインのネームサーバーを ns1・ns2 に変更する
- 親ゾーンに反映されたかを確認する
# 親ゾーンから委任までたどる
dig +trace example.com A
# .com の場合、親ゾーンのサーバーにNSとグルーを直接問い合わせる例
dig @a.gtld-servers.net example.com NS +norec
ネームサーバーの変更は、以前のNS情報が各地のリゾルバーにキャッシュされている間、新旧が混在します。旧DNSサービスのゾーンは、切り替え後もしばらく削除せずに残しておくと安全です。
運用:更新・監視・保守
レコードを変えるときプライマリのゾーンファイルを編集し、シリアルを増やして named-checkzone で確認し、sudo rndc reload example.com で読み込みます。NOTIFYによりセカンダリが更新を取りに来ます。シリアルを増やし忘れるとセカンダリは更新されません。編集前にゾーンファイルを日付付きでコピーしておくと、誤りに気づいたときにすぐ戻せます。
両者のシリアルを監視するプライマリとセカンダリのSOAシリアルが一致しているかを、監視ツールや定期実行のスクリプトで比べます。転送が止まったままでも、セカンダリはSOAのexpireまで古いデータで応答し続けるため、気づきにくい障害になります。
応答レート制限を検討するDNS応答を悪用した反射攻撃への対策として、options に rate-limit { responses-per-second 10; }; のような応答レート制限(RRL)を設定できます。値は環境に合わせて調整し、正規の問い合わせが落ちていないかログで確認します。
DNSSEC署名する場合、新しい系列では dnssec-policy で鍵の生成と更新を自動化できます。DSレコードをレジストラに登録するまでが一連の作業で、鍵の更新に失敗するとドメイン全体が名前解決できなくなるため、仕組みを理解してから導入してください。
更新を当て続けるBINDは定期的に脆弱性修正が公開されます。ISCのセキュリティ情報を確認し、ディストリビューションの更新を継続して適用します。
まとめ
- 自前のBINDはプライマリとセカンダリの2台を別ネットワークに置き、どちらも
recursion no;にします。 - ゾーン転送はTSIG鍵で署名し、
allow-transferで鍵を持つ相手だけに許可します。 - 委任前に外部から、権威応答・再帰拒否・転送拒否・シリアル一致を確認します。
- ネームサーバー名が自ドメイン内にある場合は、レジストラでグルー(ホスト登録)を設定してからNSを変更します。
- 運用ではシリアル更新、両者のシリアル監視、BINDの更新を継続します。