ISC BINDでLAN向けとインターネット向けにDNSを構築する


 我が家の自宅ネットワークでは、LAN 内のサーバと DHCP で配布する IP アドレスに DNS で名前を振っている。一昔前は1サーバあたりにかかるコストが結構高かったために /etc/hosts でも十分管理できたが、仮想化技術の進展に伴い、自宅でも複数「台」のサーバを管理することが現実的な話となってきた。

 一昔前に流行った DDNS (Dynamic DNS) ではなく特に固定 IP で外向けに DNS をサービスする場合、まず間違いなく外向けと内向けでゾーンファイルに差が出てくる。そんなときには、ISC BIND 8 以降で実装された view 機能で出し分けが可能となる。

 まず、自前 DNS でホストするドメインのゾーンファイルを用意する。そのうち、内向けと外向けで内容に差があるものについては両方分用意する。次に acl (Access Control List) を用意するが、これは既に DNS が稼動していれば存在すると思う。これから DNS を構築する場合は、

  • 127.0.0.0/8
  • 内側に振っているセグメント

を適当な名前をつけて named.conf にリストアップする。例えば、IPv4 限定だと

acl "trusted" {
        127.0.0.0/8;
        192.168.1.0/24;
};

といった感じになる。

 次に、bind で読み込むゾーンファイルを named.conf に列挙する。この際、view で内向きと外向き用に全てのドメインを列挙していく。内向きと外向きで同じドメインの場合は、それぞれ同じゾーンファイルを読み込むように設定すればよい。

view "lan" {
        match-clients { trusted; };
        recursion yes;

        zone "localhost" IN {
                type master;
                file "pri/localhost.zone";
                allow-update { none; };
                notify no;
        };

        zone "127.in-addr.arpa" IN {
                type master;
                file "pri/127.zone";
                allow-update { none; };
                notify no;
        };

        zone com IN { type delegation-only; };
        zone net IN { type delegation-only; };

        zone "example1.com" IN {
                type master;
                file "pri/example1.com.lan.zone";
                allow-update { none; };
                allow-query { any; };
        };

        zone "example2.com" IN {
                type master;
                file "pri/example2.com.zone";
                allow-update { none; };
                allow-query { any; };
        };
};

view "global" {
        match-clients { any; };
        allow-query { none; };
        recursion no;

        zone com IN { type delegation-only; };
        zone net IN { type delegation-only; };


        zone "example1.com" IN {
                type master;
                file "pri/example1.com.global.zone";
                allow-update { none; };
                allow-query { any; };
                notify yes;
                also-notify { xxx.xxx.xxx.xxx; };
        };

        zone "example2.com" IN {
                type master;
                file "pri/example2.com.zone";
                allow-update { none; };
                allow-query { any; };
                notify yes;
                also-notify { xxx.xxx.xxx.xxx; };
        };
};

 このように、LAN 側は match-clients で trusted に分類した IP から問い合わせがあった場合にのみ lan という view のゾーンを返すようにする。example1.com が内向きと外向きで違うゾーンファイルを返す設定になっており、それぞれの view で違うゾーンファイルを読み出している。example2.com は内向き・外向き関係なく同じゾーンファイルを返したいので同じゾーンファイルを読み出す設定になっている。

 ここで気を付けないといけない点は2ヶ所ある。まず、内向き・外向きそれぞれでホストしたいドメインの設定を全て書く必要があるという点。LAN 側に書いたからグローバル側には書かなくていいというわけではなく、内向きと外向きで切り分けられているので両方に書く必要がある。正直な話、DNS をホストしているマシンがグローバル IP とローカル IP 双方を持っている場合、内向きのほうにのみドメインを設定しておけば最低限の名前解決はできる。ただし、内部的にはグローバル側からローカル側に問い合わせされるため、

  • 1 ステップ問い合わせ回数が増える。
  • 権威 DNS は常にローカル側になるため、インターネットからそのドメインを問い合わせると常に Non-authoritative answer となるし、セカンダリ DNS にゾーンファイルが転送されなくなる。

と、手を抜けるメリット以上のデメリットがあるために避けるべきだと思われる。

 もう1点は、DNS NOTIFY を使ってセカンダリ DNS に更新を通知する場合、DNS NOTIFY の設定をするのは外向きのホストのみということ。内側の設定に DNS NOTIFY の設定をつけてしまうと、LAN 側の情報が流れてしまう。そのため、本来意図しない IP がセカンダリ DNS から返されたり、セカンダリ DNS の設定に不備がある場合は LAN 側のサーバ構成を知るヒントになってしまう。

 以上で、LAN 側とインターネット側で違う結果を返す DNS を構築できる。小規模な自宅サーバだとあまり必要性はないが、ある程度の規模のある自宅ネットワークまたは社用ネットワークの場合はこういった DNS が役に立つものと思われる。