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、他地域からの問い合わせで動作を確かめます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲