2026年6月16日
2026年6月16日
ジオDNS の設定方法|自前の BIND で view と GeoIP2 を使い、地域ごとに応答を分ける手順と確認
自分で運用している BIND の権威 DNS サーバーでも、view と GeoIP2 の ACL を組み合わせればジオDNS を設定できます。問い合わせの送信元アドレスを国に当てはめ、国ごとに別のゾーンファイルを返す仕組みです。この記事では、GeoIP2 データベースの準備、BIND の設定とゾーンファイルの分け方、どの view が答えたかの確かめ方、セカンダリサーバーとデータベース更新の注意点までを説明します。マネージド DNS(Route 53 など)で設定する方法とは手順が大きく異なるため、自前の BIND を使っている人向けの内容です。
仕組みと、先に知っておく制約
BIND の view は、問い合わせの送信元に応じて別の内容のゾーンを返す機能です。送信元の判定に geoip country JP のような ACL を使うと、国単位で view を選べます。
ここで大事な制約があります。権威サーバーに届く問い合わせの送信元は、利用者の端末ではなく、利用者が使っているリゾルバー(プロバイダーや公開 DNS)です。マネージド DNS の多くは EDNS Client Subnet(ECS)で利用者側のネットワークの情報も使いますが、オープンソース版の BIND の権威サーバーは ECS を使った応答の切り替えに対応していません(2026年10月現在)。そのため、次のような判定のずれが起こります。
- 日本の利用者が海外拠点の公開リゾルバーを経由すると、その国として判定される
- 企業や VPN のリゾルバーが別の国にあると、その国として判定される
国単位の大まかな振り分けには十分使えますが、精度が必要な場合や ECS を使いたい場合は、PowerDNS の GeoIP バックエンドやマネージド DNS を検討してください。また、ジオDNS はアクセス制限の手段にはなりません。IP アドレスを直接指定すれば回避できるため、制限が目的ならサーバー側や WAF で行います。
手順1: BIND と GeoIP2 データベースを準備する
現在の BIND 9 系は、MaxMind の GeoIP2 形式(拡張子 .mmdb)のデータベースを、libmaxminddb を通して読みます。まず、手元の BIND がこのライブラリ付きでビルドされているかを確認します。
named -V | grep -i maxmind
何も表示されない場合、その BIND では geoip の ACL が使えません。配布パッケージの別ビルドを使うか、ビルドの設定を見直します。
データベースは、MaxMind の無料版(GeoLite2)か有償版(GeoIP2)を使います。GeoLite2 の入手には MaxMind のアカウント登録とライセンスキーが必要で、利用条件も定められています。条件は変わることがあるため、MaxMind の案内を確認してください。ダウンロードと更新には MaxMind の geoipupdate を使うのが一般的です。
# /etc/GeoIP.conf(例: 値はダミー)
AccountID your-account-id
LicenseKey your-license-key
EditionIDs GeoLite2-Country
sudo geoipupdate
ls -l /usr/share/GeoIP/
# 特定のアドレスが何の国と判定されるかを確認する
mmdblookup --file /usr/share/GeoIP/GeoLite2-Country.mmdb --ip 203.0.113.1 country iso_code
データベースを置くディレクトリは、配布パッケージによって異なることがあります。BIND は決まったファイル名(GeoLite2-Country.mmdb など)を、指定したディレクトリから探します。
手順2: BIND の設定に ACL と view を書く
作業の前に、/etc/bind/ の設定とゾーンファイルをまとめてバックアップしてください。view の導入は設定全体の構造が変わるため、可能ならステージング用のサーバーで先に試します。
まず options に、データベースのディレクトリを指定します(Debian 系の配置を例にしています)。
// /etc/bind/named.conf.options(抜粋・例)
options {
directory "/var/cache/bind";
geoip-directory "/usr/share/GeoIP";
recursion no;
allow-transfer { none; };
};
次に、国ごとの ACL と view を定義します。view は上から順に評価され、最初に当てはまったものが使われます。どれにも当てはまらない問い合わせを受けるため、最後に any の view を置きます。
// /etc/bind/named.conf.local(例)
acl "geo_jp" { geoip country JP; };
acl "geo_eu" {
geoip country DE;
geoip country FR;
geoip country NL;
geoip country GB;
};
view "japan" {
match-clients { geo_jp; };
zone "example.com" {
type primary;
file "/etc/bind/zones/db.example.com.japan";
};
};
view "europe" {
match-clients { geo_eu; };
zone "example.com" {
type primary;
file "/etc/bind/zones/db.example.com.europe";
};
};
view "default" {
match-clients { any; };
zone "example.com" {
type primary;
file "/etc/bind/zones/db.example.com.default";
};
};
ここでつまずきやすいのが、view を一つでも使うと、すべてのゾーンを view の中に入れる必要があることです。Debian 系の初期設定では、named.conf が named.conf.default-zones を view の外で読み込んでいるため、そのままでは設定エラーになります。権威専用のサーバーであれば、この読み込みを default の view の中へ移すか、不要なら外します。ほかに管理しているゾーンがある場合も、各 view に入れるか、in-view で別の view のゾーンを共有させます。
type primary は新しい書き方で、古い版では type master と書きます。手元の版の書き方に合わせてください。
手順3: ゾーンファイルを共通部分と差分に分ける
view ごとにゾーンファイルを丸ごと持つと、レコードを1行変えるたびに3つのファイルを直すことになり、食い違いの原因になります。共通の部分を一つのファイルにまとめ、$INCLUDE で読み込む形にすると管理が楽です。
; /etc/bind/zones/db.example.com.common(全 view 共通)
$TTL 300
@ 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
mail IN A 192.0.2.25
@ IN MX 10 mail.example.com.
; /etc/bind/zones/db.example.com.japan
$INCLUDE /etc/bind/zones/db.example.com.common
www 60 IN A 192.0.2.10
; /etc/bind/zones/db.example.com.europe
$INCLUDE /etc/bind/zones/db.example.com.common
www 60 IN A 198.51.100.20
; /etc/bind/zones/db.example.com.default
$INCLUDE /etc/bind/zones/db.example.com.common
www 60 IN A 192.0.2.10
SOA を共通ファイルに置いているので、シリアル番号の更新は1か所で済みます。地域で変えるレコードの TTL は短め(60〜300秒程度)にしておくと、振り分け先を変えたときの反映が早くなります。
手順4: 設定を検査して反映する
反映の前に、設定とゾーンの両方を検査します。-z を付けると、設定に書かれたゾーンの読み込みまで試します。
sudo named-checkconf -z
sudo named-checkzone example.com /etc/bind/zones/db.example.com.japan
sudo rndc reload
エラーが出た場合は反映せず、表示された行を直します。反映後にサービスが起動しないときは、バックアップした設定に戻して原因を調べます。
手順5: どの view が答えたかを確かめる
自分の手元から問い合わせると、自分の地域の view しか見えません。確認には次の方法を組み合わせます。
問い合わせログで view 名を見る。BIND の問い合わせログには、応答した view の名前が記録されます。
sudo rndc querylog on
# ログに「view japan: query: www.example.com IN A」のような行が出る
sudo journalctl -u named -f # 配布パッケージによってはユニット名が bind9
sudo rndc querylog off
問い合わせログは量が多いので、確認が終わったら止めます。
試験用の ACL で自分の IP を割り当てる。一時的に、自分の固定 IP アドレスを europe の view の match-clients に足して問い合わせると、その view の応答を手元で確かめられます。確認後は元に戻してください。
他の地域から問い合わせる。海外のサーバーや、RIPE Atlas のような計測サービスから dig @ns1.example.com www.example.com A +short を実行し、地域ごとの応答を見ます。公開リゾルバー経由の結果は、そのリゾルバーの拠点の場所で判定される点に注意してください。
判定が想定と違うときは、mmdblookup で問い合わせ元のアドレスがどの国になっているかを確認すると、データベースの判定なのか設定の誤りなのかを切り分けられます。
運用で気をつけること
- データベースの更新:IP アドレスの割り当ては変わるため、
geoipupdateを定期的に実行し、更新後に BIND に読み直させます。読み直しの扱いは版によって異なることがあるので、更新後にrndc reloadを実行する手順にしておくと安全です。 - セカンダリサーバー:view を使うと、セカンダリにも同じ view の構成が必要です。view ごとにゾーンを転送するには、TSIG の鍵で view を選ばせる構成が一般的で、設定はプライマリより複雑になります。プライマリだけ view にすると、セカンダリが返す応答では地域が分かれません。
- 振り分け先の準備:WordPress を地域ごとの拠点で動かす場合、データベースの書き込み先、アップロードファイルの同期、各拠点の証明書、サイトアドレスの統一を先に決めておきます。DNS の設定よりも、こちらのほうが大きな作業になります。
まとめ
- BIND の view と GeoIP2 の ACL で、問い合わせの送信元の国ごとに別のゾーンを返せます。判定はリゾルバーのアドレスで行われ、オープンソース版の BIND では ECS は使われません。
named -Vで libmaxminddb の対応を確認し、GeoLite2 などのデータベースをgeoipupdateで入手します。- view を使うとすべてのゾーンを view に入れる必要があります。ゾーンファイルは共通部分を
$INCLUDEでまとめ、差分だけを view ごとに持ちます。 - 反映前に
named-checkconf -zで検査し、問い合わせログの view 名、試験用 ACL、他地域からの問い合わせで動作を確かめます。