BIND9をchrootで建てる【前編】

目次

昔閉鎖したブログから復活させた記事です
ごちゃついていますが見たい方はどうぞご覧下さい。

背景

つい最近自宅のラズパイに内部向けDNSを建てたのですが、unboundというBINDとは別のDNSサーバーを建てたくなってしまいました。したがって、今度はGCPの無料インスタンスにBIND9によるDNSサーバーを構築します。 建てたDNSでは自分の持っているドメイン(以下 kusshie.com とします)を運用します。このブログのための名前解決はもちろんのこと、自宅の外部IPも登録します。 セキュリティがかなり不安だったので勉強も兼ねて chroot にチャレンジしてみました。他にも色々対策した方がいいと思いますがまずは基本的なところから始めます。

Bind9のインストール

面倒なことは嫌いなので、いつも通り楽に済ませます。コンパイルとかはしません。OS は ubuntu を使っておりますので apt を使います。

$ sudo apt install bind9

BINDのchroot化

以下のサイトに書いてあることを自分なりに理解しながら進めていきます。途中順番が入れ替わってたりしますが、こちらの都合です。

BIND9ServerHowto

こっちのサイトも面白いのですが、少し古いのと Cent OS 向けの記事となっています。

実用 BIND 9で作るDNSサーバ(2)

デフォルトのディレクトリで運用してもいいのですが、グローバルに公開するためセキュリティを確保します。bind専用のディレクトリを作って、そこをbindにとっての ‘ / ‘ (ルート)にしてしまうことで、万が一bindが乗っ取られてもbind専用ディレクトリ外にはアクセスできないようにして一定のセキュリティを担保します。これを chroot化 (change root) と言います。

ディレクトリ作成

$ sudo mkdir -p /chroot/named
$ cd /chroot/named
$ sudo mkdir -p dev etc/namedb/slave var/run

金魚を飼うときできる限り環境を整えてあげないと死んでしまうのと同じように、bindも彼にとって同じ環境となるように再現してあげなければいけません。したがって、新たに作った /chroot/named の中にいくつかディレクトリを作成しています。

                /
                |              
 +--------------+-------------+
 |              |             |
etc          chroot          var
                |
              named
                |
 +--------------+-------------+
 |              |             |
dev            etc           var
                |             |
              namedb         run
                |
              slave

コマンドを実行すると上記の図と同じディレクトリ構造が出来上がります。bindからはchroot外が見えないようにしてやろうというわけです。

権限・所有者の設定

続いて権限や所有者の設定を行います。

$ sudo chown root:root /chroot
$ sudo chmod 700 /chroot
$ sudo chown bind:bind /chroot/named
$ sudo chown bind:bind /chroot/named/etc/namedb/slave
$ sudo chown bind:bind /chroot/named/var/run
$ sudo chmod 700 /chroot/named

chroot自体はrootユーザーの管轄にして、直下のnamedをbindユーザー所有にします。bindユーザーはインストールしたときに勝手に作成されているはずなので新たに作る必要はないです。以下のコマンドユーザーリストが得られるので、本当にbindユーザーが存在しているか確認できます。

$ cat /etc/passwd

ちなみに chmod 700 は所有者にだけ全権を与える設定です。8進数がわかっていればすぐに意味はわかると思います。

設定ファイルの配置

ディレクトリは作成しましたが、肝心の設定ファイルがデフォルトのディレクトリに置きっぱなしです。cpコマンドでコピーしてもいいのですが、どうせ中身を消して書き直すので同名のファイルを作ることにします。

$ sudo touch /chroot/named/etc/named.conf

touch コマンドは空のファイルを作るときに重宝しますね。

デバイスファイルの作成

私はデバイスファイルというものが何なのかよく知りません。デバイスドライバのインターフェースであることと、bindで必要になることだけは理解したのでとりあえず作ります。

$ sudo mknod /chroot/named/dev/null c 1 3
$ sudo mknod /chroot/named/dev/random c 1 8
$ sudo mknod /chroot/named/dev/urandom c 1 9

mkhod がデバイスファイルを作成するコマンドらしく、これでデバイスドライバを操れるといわけですが、上記2行は特定の機器に対応するようなものではないようです。/dev/null はギリギリ使ったことがあります。ブラックホールのように飛んできたものを無にしてしまう結構便利なやつです。randomもそれ専用のデバイスがあるわけではなく仮想的なものであると理解しました。疑似乱数生成器とのことです。時間があればこのあたりOSの神秘的なことも勉強したいなと思う次第です。

※独自解釈な部分があって正しくないことを言っているかもしれません

起動オプションの編集

bind9にとってのルートディレクトリを変更したので、起動オプションをそのままにしてたら残念な結末が待っているようです。/etc/default/bind9 を編集します。初めは以下のようになっています。

OPTIONS="-u bind"

編集後

OPTIONS="-u bind -t /chroot/named -c /etc/named.conf"

-t オプションでbindが動作するルートディレクトリを変更していて、-c オプションは named.conf の場所を示しています。

「chroot したのに場所が古いじゃないか!」

と一瞬思いますが、-c オプションで指定する場所は -t オプションからの相対パスになっているので大丈夫です。安心安心 (^^)

rsyslogの設定

rsyslog だったのでコミュニティとドキュメントとはやり方を変えます。新たに /etc/rsyslog.d/bind-chroot.conf を作成して以下の内容を記入します。

$AddUnixListenSocket /chroot/named/dev/log

以下のサイトが参考になりました。

Debian 9 (Stretch) - DNS サーバ BIND9 の chroot 化!

これでようやくchroot化に関する設定が完了です。疲れました。

BINDの設定

BINDの実際の動作に関する設定をこれからしていきます。

named.confの編集

非常に迷ったのですが、ubuntu らしく、このファイルには include のみを書くことにします。/chroot/named/etc/named.conf です。

include "/etc/named.conf.options";
include "/etc/named.conf.internal-zones";
include "/etc/named.conf.external-zones";

named.conf.optionsの編集

option { } を書く専用のファイルです。Ubuntu はファイルがこんな風にもともとは分散しています。named.conf にまとめることもできたのですがあえて分けてみることにしました。

完成すると以下のようになります。(/chroot/named/etc/named.conf.options)

options {
    directory "/etc/namedb";
    pid-file "/var/run/named.pid";
    version "I won't tell you my version.";
    statistics-file "/var/run/named.stats";
    recursion no;
    allow-query { any; };
};

directory
応答に関するファイルをどこに置くかの設定です。スレーブゾーンは先ほど作った bindユーザーが所有している /chroot/named/etc/namedb/slave に置いてマスターゾーンと分けて管理することで、悲劇が起きた際にマスターゾーンへの被害を防げるみたいです。

directory "/etc/namedb";

ちなみに /chroot/named/etc/namedb とする必要はありません。

pid-file
bind が動き始めると自動で作成されるpidファイルの置き場所です。

pid-file "var/run/named.pid";

version
攻撃者にわざわざバージョンを教えてある義理なんてありませんから適当な文字列を返すように設定します。脆弱性のあるバージョンであることがばれたら速攻でカモにされます。こわいこわい。

version "I won't tell you my version.";

言い忘れてましたが、文末にはセミコロンがないとだめです。たまに入れるの忘れててエラーをはきます。

statistics-file
ちょっとよくわからなかったのですが以下のような記述を見つけました。

rndcを使ってステータス情報を要求した際に統計情報が追記されるファイルのパスを設定します。

強いBIND DNSサーバーを構築する

statistics-file "/var/run/named.stats";

recursion
再起問い合わせをするか否かの設定です。コンテンツサーバーとして稼働させますので no にします。要は自分の管理しているドメイン以外の情報をいちいち走り回って提供したりはしないということです。私の場合は kusshie.com 以外の問い合わせに対して知りませんよと返答する設定になります。

recursion no;

www.google.com のIPを教えろと言われても知らんと回答します。

allow-query
問い合わせ許可するクライアントを設定します。外部に公開しますので、誰でもOKという意味の any を指定します。

allow-query { any; };

any の後ろと } の後ろにそれぞれセミコロンが必要です。ちなみにネットワークアドレスとか特定のIPアドレスとかを指定すると対象を狭めることができます。

named.conf.default-zonesの編集

書くのが面倒なので元ファイルをコピーしてきて編集します。

$ sudo cp /etc/bind/named.conf.default-zones /chroot/named/etc/named.conf.default-zones

このファイルにはコメントで以下のように書かれています。

ローカルホストのフォワードゾーンとリバースゾーン、およびRFC 1912に基づくブロードキャストゾーンの権限を持つこと。 /etc/named.conf.default-zones

したがって、これらの記述だけ残しておきます。それ以外は消します。file のところパスが元のディレクトリを挿していると思うので、そこも新しい場所へ変更しなくてはなりません。

// be authoritative for the localhost forward and reverse zones, and for
// broadcast zones as per RFC 1912

zone "localhost" {
        type master;
        file "db.local";
};

zone "127.in-addr.arpa" {
        type master;
        file "db.127";
};

zone "0.in-addr.arpa" {
        type master;
        file "db.0";
};

zone "255.in-addr.arpa" {
        type master;
        file "db.255";
};

named.conf.internal-zonesの編集

内向きに定義するつもりはないので必要ないと言えばないのですが、external があるのに internal がないのも少々違和感ありというわけで作ります。

view "internal" {
    include "named.conf.default-zones";
}

デフォルトゾーンぐらいしか書くことがありません。

named.conf.external-zonesの編集

どんなドメインを定義するのかを書きます。私の場合は当ブログのドメインにもなっている、kusshie.com です。

view "external" {
    # 正引き
    zone "kusshie.com" {
        type master;
        file "kusshie.com.wan";
    };
    # 逆引き
    zone "56.142.197.104.in-addr.arpa" {
        type master;
        file "56.142.197.104.db";
        allow-update { none; };
    }
};

自分のところで管理しますから、type は master です。slave ではありません。ファイルの場所は /etc/namedb/ でいいと思います。意外に書くことが少ないですね。

ちなみに、ファイルの置き場所は /etc/namedb/ です。

kusshie.com.wanの編集

正引き情報を書きます。完成後のファイルが以下です。

$TTL 86400
@       IN      SOA     ns.kusshie.com. root.kusshie.com. (
                2020042601      ;Serial
                3600            ;Refresh
                1800            ;Retry
                604800          ;Expire
                86400           ;Minimum TTL
)
                IN      NS      ns.kusshie.com.
blog            IN      A       104.197.142.56
home            IN      A       XXX.XXX.XXX.XX自宅IP(動的)
strage          IN      A       XXX.XXX.XXX.XX自宅IP(動的)

自分のサーバーのファイルをさらしていることになるのですが、正直コマンドたたければ取れる情報ばかりなので隠す必要もないかなと思い公開してます。自宅IPは動的なためダイナミックDNSとしての設定が必要ですが、長くなるので今回は書きません。動的でも現状のIPアドレスを書く必要はあります。

$TTL

外部のDNSサーバーがここのドメイン情報をキャッシュして保持する時間を秒で設定するみたいです。86400秒ってどのくらい?と思いますが、計算すれば分かります。1日です。

SOAレコード

SOA は Start Of Authority の略だそうです。なんじゃそりゃ?

BINDではゾーンファイルの先頭、デフォルトTTLの指定の後に書くことになっています。また、SOAは委任に関するオーソリティ情報を記すものであり、各ゾーンの委任されたドメイン名に関連付けられます。

SOAレコードには何が記述されている?

オーソリティ情報が何かもよくわからないので疑問が無限ループしそうですが、色々設定を書くというわけです。一つ一つ確認したほうが分かるかもしれません。

ns.kusshie.com. の部分
これはゾーン情報を持ってるサーバーを表しています。プライマリとセカンダリがあった場合、元の情報はプライマリにあるため、プライマリの名前を書かなければいけません。

root.kusshie.com. の部分
ドメイン管理者のメールアドレスを書くのですが、そんなもの持っていないため適当に書いてます。問い合わせて人なんていないでしょう。(なんて雑なんだ)

Serial
何となく予想がついた方もいると思うのですが、日付になっています。しかし、日付を書くのが目的ではなくて、バージョンを書く場所となっています。 2020042601 は 2020年4月26日と読めます。 最後の01は細かく分けれるようにある番号ではないかと思います。このように絶対書かなければならないというわけではないようですが、どこのサイトをみても大抵この書き方です。

Refresh
セカンダリがゾーンの更新有無について確認するまでの時間。

Retry
上記Refreshに失敗した場合に再チャレンジするまでの時間。

Expire
ゾーンの情報更新に失敗した状態でセカンダリが情報を提供できる時間。

Minimum TTL
TTL…再び !!! キャッシュサーバーが存在しないドメインを問い合わせた際に結果を保持する時間らしいです。普通のTTLは問い合わせてヒットした結果をキャッシュする時間ですが、そんなドメインないよとなった場合にドメインがないという事実を記録しておく時間です。

NSレコード

ゾーンを管理するDNSサーバーを定義します。先ほどSOAでも書いたのと同じですね。

「www.kusshie.com だけ ns.example.com で管理してるの~」

という場合には、以下のように書きます。

        IN    NS    ns.kusshie.com.
www     IN    NS    ns.example.com.

NSレコードは一つとは限らないことは頭の片隅に置いておいたほうがいいかもしれません。内部向けにBINDを建てたときに上の技は使えると思います。他の用途は思いつきません。

Aレコード

一番DNS感のあるレコードです。特定のホスト名にIPアドレスを定義します。

blog.kusshie.com というURLがあった場合、blog の部分がホスト名になります。必ずこうなるわけではありませんが、多くの場合こうなります。

56.142.197.104.dbの編集

正直ファイル名は自由でいい気がしないでもないですが、分かりやすいようにIPアドレスを坂さまにして、最後に.dbを付けたファイル名にします。

$TTL 86400
@       IN      SOA     ns.kusshie.com.     root.kusshie.com. (
                2020042601      ;Serial
                3600            ;Refresh
                1800            ;Retry
                604800          ;Expire
                86400           ;Minimum TTL
)
        IN      NS      ns.kusshie.com.
        IN      PTR     blog.kusshie.com.

自宅のIPを逆引きしてもプロパイダのドメインが返ってくるだけなので、書いていません。最初は少し悩みました。

不足しているファイルを準備

named.conf.default-zones に書かれているゾーン情報に関するファイルが不足しているのでコピーしてきます。

$ sudo cp /etc/bind/db.local /chroot/named/etc/namedb/db.local
$ sudo cp /etc/bind/db.127 /chroot/named/etc/namedb/db.127
$ sudo cp /etc/bind/db.0 /chroot/named/etc/namedb/db.0
$ sudo cp /etc/bind/db.255 /chroot/named/etc/namedb/db.255

BINDの起動

rsyslog をまず再起動します。

$ sudo systemctl restart rsyslog

私は自動起動の設定をしていなかったので先にそっちをやります。

$ sudo systemctl enable bind9

最後に bind を起動します。

$ sudo systemctl start bind9

うまくいかない場合は systemd のUnit定義ファイルを書き換えたほうがいいかもしれません。私の場合は $OPTIONS がUnit定義ファイルに使われていたため問題ありませんでした。

以上で終了です。お疲れ様でした。

追記:問題なく動いているように見えましたが、rndc に関する設定が不足していました。これについては後編で書きます。




Archives

2022 (6)
2021 (3)
2020 (4)

Writer

筆者のイメージ画像
kusshie

情報系学部に所属していた社会人1年生です。大学ではネットワークを学んでいましたがまだまだです。 最近は友達に誘われてISPごっこに足を踏み入れました (AS63791)。