公開DNSサーバ構築についての詳細

#BINDにおけるセキュアな構築手順・設定について
BIND（DNS）についてはセキュリティホールがしょっちゅう見つかるみたいでchrootを用いた運用が勧められているとの事。ここではchrootを用いたBINDでhogehoge.co.jp、hogehoge.jp、hogehoge.comを公開する前提として例をすすめます。また、BINDの設定値についても詳しく調査してみました。

##install
`# yum -y install bind bind-chroot`

##chroot化
[参考リンク先](http://centossrv.com/bind.shtml)のshellを使用させてもらう
`# mkdir /usr/local/sh`
`# cd /usr/local/sh`
`vi bind-chroot-admin.sh`

```lang:bind-chroot-admin.sh
#!/bin/sh

# bind-chroot install check
rpm -q bind-chroot > /dev/null 2>&1
[ $? -ne 0 ] && echo bind-chroot not install && exit 1

# bind-chroot enabled
sed -i '/^ROOTDIR=/d' /etc/sysconfig/named
echo ROOTDIR=/var/named/chroot >> /etc/sysconfig/named

# file copy
filelist=`mktemp`
rpm -ql bind|grep ^/etc >> ${filelist}
rpm -ql bind|grep ^/var >> ${filelist}
for file in `cat ${filelist}`
do
  # directory make
  if [ -d ${file} ]; then
      DIRNAME=/var/named/chroot${file}
      [ ! -d ${DIRNAME} ] && mkdir -p ${DIRNAME}
  fi
  # file copy
  if [ -f ${file} ]; then
      DIRNAME=/var/named/chroot`dirname ${file}`
      [ ! -d ${DIRNAME} ] && mkdir -p ${DIRNAME}
      /bin/cp -a ${file} ${DIRNAME}
  fi
done
rm -f ${filelist}

chown named:named /var/named/chroot/var/named/data
chmod 770 /var/named/chroot/var/named/data
chown named:named /var/named/chroot/var/named/dynamic

exit
```

`# chmod 700 /usr/local/sh/bind-chroot-admin.sh`
`# ./bind-chroot-admin.sh`

##named.confの設定
named.confの設定について

###ACL
conf中に使用できる別名を定義する。
アドレスマッチリストのIP指定には下記の書き方が可能です。

```lang:IP指定方法
192.168.0.100 --- 192.168.0.100 のホストに一致する
192.168.0.0/16 --- 192.168.X.X のホストに一致する
192.168/16 --- 192.168.X.X のホストに一致する（上と同じ）
!192.168.1/24 --- 192.168.1.X のホストに一致しない
```

実際には下記の様に指定します。
`acl "別名" { 127/8; 192.168.10/24; };`

尚、下記の別名がデフォルトで定義されています。

|エイリアス名|備考|
|:----|:----|
|any|すべてのホストに一致する|
|none|すべてのホストに一致しない|
|localhost|サーバのすべてのインタフェースアドレスに一致する（IPv4 のみ）|
|localnets|サーバのインタフェースと同じネットワーク上のホストに一致する（IPv4 のみ）|

###options
全体にかかわる設定項目を定義します。例外を除き基本的にここでは全拒否し、viewや各zone個別で許可していく等の方針にすると間違ってセキュリティホール穴を作らないのでその方針でいきました。

|項目名|設定例|備考|
|:-------------------|:---|:---|
|version|"unknown"|BINDへのヴァージョン問い合わせに大して返送する文字列を指定します。バージョンを悟られないような文字列にします。|
|hostname|"ns.hoge.com"|BINDへのホスト名問い合わせに対して返送する文字列を設定します。省略時はOSで設定しているホスト名になります。DNSサーバのホスト名とOS設定でのホスト名が違う場合に使用するとよいでしょう。|
|directory|"/var/named"|named.conf中の相対パスの起点です。chrootしている場合はchroot後のパスで記述します。|
||||
|listen-on port|53 {any;}|BINDがクライアントからのクエリを受け取るネットワークインタフェースとポートを指定します。たとえばローカルホストからだけの要求を許可するには、127.0.0.1と記述します。|
|listen-on-v6| port 53 {any;} |BINDがIPv6クライアント要求をリッスンするポートを指定します。|
||||
|dump-file|"/var/named/data/cache_dump.db";|rndcコマンドでの出力結果を保存するパスを指定します。|
|statistics-file|"/var/named/data/named_stats.txt";|rndcコマンドによってnamedが名前解決を行った回数の統計データを入れるパスを指定します。|
|memstatistics-file|"/var/named/data/named_mem_stats.txt"|サーバ終了時にメモリ使用統計について出力するファイル。rndcによって使用されます。|
||||
|allow-query|{any;}|クエリ（問い合わせ）事態の許可設定。よほどの事がない限り全てのゾーンでクエリは許すのでoptionで許可します。|
|allow-transfer|{none;}|ゾーン転送要求を許可するスレーブサーバやalso-notifyで指定したサーバのIPアドレス指定します。optionでは全拒否しておきます。externalのviewや各ゾーンでそれぞれのセカンダリサーバを許可する方式にします。|
|allow-update|{none;}|外部書き込みアクセスを制御し、クライアントにDNSエントリへの書き込み権を付与することができます。クライアントにゾーンの書き換えをされては大変なのでデフォルトnoneになっていますが、あえて明記しnoneにしておきます。 |
|allow-query-cache|{none;}|キャッシュデータに対するDNSクエリーを許可するリモートホストを定義します。|
||||
|recursion|no|再帰的クエリーに対する措置を決定します。yesに設定されたDNSサーバは、自分がマスターでないゾーンについてのクエリーを受けると、そのゾーンのSOAである他のDNSサーバに問い合わせをかけ、調べて回答します。noに設定されたDNSサーバは、自分がSOAであるゾーンのみについて名前解決をおこないます。「キャッシュポイズンニング」を防ぐため、「プライマリDNSサーバ」や「セカンダリDNSサーバ」については、キャッシュ専用サーバを別途たてるなどして、自身が所有するゾーン情報のみを処理させるために極力recursionを禁止すべき。どうしても内部ネットワークからインターネットへのアクセスについてDNSサービスを提供しなければならないような場合、次のallow-recursionにより再帰的クエリーを内部のクライアントに許可する必要があります。|
|allow-recursion|{localhost; localnets;}|上記をyesにした場合などはこのように制限を必ずかけておくこと。再帰的クエリーを特定のホストやネットワークに限定して許可する場合に使用します。DNSサーバはこれらのホストの要求に対しては、再帰的クエリーを実行します。|
||||
|dnssec-enable|yes|DNSSECは権威サーバの署名を検証する事で、応答の偽装や改ざんを検知する事が可能になる技術です。DNSキャッシュポイズニング対策として有効です。|
|dnssec-validation|yes|dnssecのvalidationです。|
|dnssec-lookaside|auto|CentOS6に同梱されるRPM版のBINDであれば設定不要です。autoで自動設定されます。|
|bindkeys-file|/etc/named.iscdlv.key|トラストアンカーの場所|
|managed-keys-directory|"/var/named/dynamic"|DNSSECのキーを保存しておく場所です。|
||||
|notify|yes|このサーバでゾーン情報に変更が加えられた際に、他のネームサーバ（スレーブサーバ）にそれを通達し、ゾーン情報の更新を促す機能です。値にyesが設定された場合、NSレコードで登録されている他のネームサーバ（SOAのパラメータによりプライマリサーバとされているサーバは除く）と、次の「also-notify」で指定されたスレーブサーバに通達が送信されます。|
|also-notify|記述しない|前項のnotifyにyesが設定されている場合に有効の、ゾーン情報の更新通知の対象とするネームサーバのIPアドレスを指定します。allow-transfer同様、複数ある場合は列挙することができます。NSレコードにより当該ゾーンのネームサーバとして登録されているサーバは、ここに記載しなくても通知されます。「このゾーンのネームサーバ（NSレコードで指定されたサーバ）に加えて、これらのサーバにも（”ALSO”）通達する」という意味での「also」です。例えば、スレイブとして登録していないが、ホスト内部での参照のみを目的としたDNSサービスを持っている場合などはこの項目を使うと便利です。|

###logging
ネームサーバのログオプションを設定します。channelフェーズで出力先やレベルを指定し、categoryフェーズでどの種類のログをどのchannelに記録するかを指定します。

```lang:logging記述方法
logging {
   [ channel channel_name {
     ( file path_name
         [ versions ( number | unlimited ) ]
         [ size size spec ]
       | syslog syslog_facility
       | stderr
       | null );
     [ severity (critical | error | warning | notice |
                 info | debug [ level ] | dynamic ); ]
     [ print-category yes or no; ]
     [ print-severity yes or no; ]
     [ print-time yes or no; ]
   }; ]
   [ category category_name {
     channel_name ; [ channel_name ; ... ]
   }; ]
   ...
};

「number」は0～2147483647の整数またはunlimitedを指定します。unlimitedを指定した場合、99世代までローテーションします。「size」にはK／k、M／m、G／gを単位に添えることができます。
```

デフォルトで用意されているチャネルは下記

|チャネル|内容|
|:---|:---|
|default_syslog|severityがinfo以上のログが、syslogのdaemonファシリティに送られる。|
|default_debug|severityがdynamic以上のログが、ワーキングディレクトリ（optionsのdirectoryオプションで指定したディレクトリ）のnamed.run ファイルに記録される。|
|default_stderr|severityがinfo以上のログが、STDERR に記録される。|
|null|このチャネルに送られたすべてのログを記録しない。|

デフォルトで用意されているカテゴリは下記

|カテゴリ|内容|
|:---|:---|
|default|categoryで意図的に指定された以外のカテゴリがここで定義される|
|general|上記以外の多くのログはカテゴリが未分類であり、それらはgeneralに分類される|
|database|ゾーン情報やキャッシュ情報など、データベースに関連する記録|
|security|要求の承認／否認の記録|
|config|構成ファイルの構文解析と処理の記録|
|resolver|クライアントに代わって実行されるキャッシュサーバの動作に代表される、再帰検索のようなDNS解決の記録|
|xfer-in|サーバが受信したゾーン転送の記録|
|xfer-out|サーバが送信したゾーン転送の記録|
|notify|NOTIFY（通知）プロトコルの記録|
|client|クライアント要請の処理記録|
|unmatched|どのview にもマッチしなかったメッセージ|
|network|ネットワーク操作の記録|
|update|DDNSの記録|
|update-security|アップデートリクエストの承認と拒否のログカテゴリ。|
|queries|問い合わせクエリーの記録|
|query-errors|何かしらのエラーが発生したクエリに関するログカテゴリ。|
|dispatch|サーバモジュールへ入ってくるパケットを処理するCPU割り当て（ディスパッチ）の記録|
|dnssec|DNSSECやTSIG処理の記録|
|lame-servers|DNS解決の際にほかのサーバで見つけた設定ミス（lame）の記録|
|delegation-only|委譲ゾーンやスタブゾーンに対するクエリ結果がNXDOMAINになった場合のログカテゴリ。|
|edns-disabled|タイムアウト等でEDNSでなく普通のクエリになったときのログカテゴリ。また、rfc1034に従っていないDNSサーバに問い合わせた場合も該当する。これは通信相手のDNSサーバがDNSクエリを理解できない場合も含まれる。このログはパケットロスが発生した場合にも出力される。対向のDNSサーバがrfc1034に従わないDNSサーバと判断する前に、本当にrfc1034に従っていないのかどうかをチェックする。このテストによりFalse-Positiveなリポートを予防することができる。|

loggingが設定されていない時は下記のカテゴリがデフォルトで動作します。

```lang:デフォルトのカテゴリ
logging {
     category default { default_syslog; default_debug; };
     category unmatched { null; };
};
```

###view
view{};ステートメントは、match-clientsオプションを使用して、IPアドレスまたは特定のネットワークに合致したものにのみ、指定のオプションとゾーン情報を適用します
view{};セクションを使う場合、match-clientsで指定するIPアドレスやネットワークは先頭のview{};セクションから順に評価されます。VIEWを用いたnamed.confでは、まず初めに合致しているかが評価され、それに漏れたものが次のview{};セクションの評価対象になります。

```lang:view記述方法
view view_name
      [class] {
      match-clients { address_match_list };
      match-destinations { address_match_list };
      match-recursive-only yes_or_no ;
      [ view_option; ...]
      [ zone_statement; ...]
};
```
ここではinternal,externalという内部用、外部用とviewクラスを２つ用意して定義していきます。
（完全に内向きと外向きのサーバ単位でわけるが理想。しかし予算がないので共存させます。。。）

####internal
内向き用です。zoneになければforwardする方式で今回は設定します。

|項目名|設定例|備考|
|:---|:---|:---|
|match-clients|{localhost; localnets;}|このviewに適用させる範囲を書きます。どこから問い合わせに来たのかを定義します。|
|match-destinations|記載しない|このviewに適用させる範囲を書きます。destination（宛先）で定義します。|
|match-recursive-only|記載しない|yes を指定すると、クライアントが再帰的なクエリを要求したときだけマッチする。|
||||
|recursion|yes|説明はoption部に記載。内向きのみOKにしておく。|
|allow-query-cache|{localhost; localnets;}|キャッシュを使用しての問い合わせを許可する範囲を指定します。|
|allow-recursion|{localhost; localnets;}|上記をyesにした場合などはこのように制限を必ずかけておくこと。再帰的クエリーを特定のホストやネットワークに限定して許可する場合に使用します。DNSサーバはこれらのホストの要求に対しては、再帰的クエリーを実行します。|
|forwarders|{ DNS-Addr1; DNS-Addr2;}|forwardersを指定すると、自分がmaster以外のゾーンは常に自分をセカンダリと認識し、上位DNSへ問い合わせます。|
|forward|only|このオプションは、forwarders オプションが記述されているときのみ有効になる。 first を指定すると、最初に（キャッシュの中に答えが見つからなければ） forwarders で指定したネームサーバに問い合わせ、もし答えが得られなければ次に自分自身で答えを見つけようとする。 only を指定すると、単純に forwarders で指定したネームサーバに問い合わせる。 デフォルトは first。|

####external
internalにマッチしないものがここに来ます。

|項目名|設定例|備考|
|:---|:---|:---|
|match-clients|{any;}|anyと記載します。※internalでマッチした物はここには来ません。|
|allow-transfer|{yyy.yyy.yyy.yyy;}|セカンダリなどのzone転送してもよい場所を記載します。複数台ある場合は複数記載します。|

###以上までを踏まえた設定例
```lang:named.conf
//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//

options {
	version "unknown";
	directory "/var/named";
	hostname "ns.hogehoge.co.jp";

	listen-on port 53 {any;};

	dump-file "/var/named/data/cache_dump.db";
	statistics-file "/var/named/data/named_stats.txt";
	memstatistics-file "/var/named/data/named_mem_stats.txt";

	allow-query {any;};
	allow-transfer {none;};
	allow-update {none;};
	allow-query-cache {none;};
	
	recursion no;

	dnssec-enable yes;
	dnssec-validation yes;
	dnssec-lookaside auto;
	bindkeys-file "/etc/named.iscdlv.key";
	managed-keys-directory "/var/named/dynamic";
	
	notify yes;
};

logging {
	channel default-log {
		file "data/default.log" versions 10 size 10M;
		severity dynamic;
		print-time yes;
		print-severity yes;
		print-category yes;
	};
	//category lame-servers {null;};
	category default {"default-log";};
};

view "internal" {
	match-clients {localhost; localnets;};
	recursion yes;
	allow-query-cache {localhost; localnets;};
	allow-recursion {localhost; localnets;};
	forwarders {xxx.xxx.xxx.xxx;};
	forward only;

	zone "." IN {
		type hint;
		file "named.ca";
	};

	include "/etc/named.rfc1912.zones";
	include "/etc/named.rfc5735.zones";

	include "/etc/named.root.key";
	include "/etc/named.hogehoge.co.jp.zones";
};

view "external" {
	match-clients {any;};
	allow-transfer {yyy.yyy.yyy.yyy;};

	zone "." IN {
		type hint;
		file "named.ca";
	};

	include "/etc/named.root.key";
	include "/etc/named.hogehoge.co.jp.zones";
};
```

###named.hogehoge.co.jp.zonesファイル
冗長的になるのが嫌だったのでファイルにまとめました。

```lang:named.hogehoge.co.jp.zones
zone "hogehoge.co.jp" {type master; file "hogehoge.co.jp.db";};
zone "hogehoge.com" {type master; file "hogehoge.com.db";};
zone "hogehoge.jp" {type master; file "hogehoge.jp.db";};
```

###各ゾーンファイル
各ゾーンファイルです。
一つでまとめてもよいですが今回は各ドメインで別ファイルにて管理します。
なお、お作法として空白行で始まるレコードは、暗黙の了解で前の行と同じ名前を指定しているのと同じことになります。

|項目|説明|
|:---|:---|
|$ORIGIN|それにつづくSOAレコード、Aレコード、PTRレコード等がの最後に「.（ドット）」のない名前を含む場合に、最後に付加される文字 列を定義します。また\$ORIGINに設定した値そのものを名前として利用する場合、「@」で置き換えることができます。また、ひとつのゾーンファイルの中で複数回使用することができます。つまり、ひとつのゾーンファイルにすべてのゾーンを記述することも可能です。|
|$TTL|当該ゾーンの各レコードの寿命（Time To Live）の値を秒単位で指定します。前項の\$ORIGINでゾーンを指定した直後は、かならず\$TTLで、そのゾーンのデフォルトTTL値を指定する必 要があります。\$TTLが指定されると、それに続くすべてのゾーンレコードのTTLがこの値となります。また\$TTLは何回でも設定可能です。\$TTL の値は、このDNSサーバからゾーン情報を取得する他のサーバにより、取得した情報の有効期間として参照されます。マスターサーバ側でなんらかの変更を 行った際に、最大ここに指定した時間、他のサーバに更新が反映されない可能性があります。例では、3600秒=1時間です（かつてはもっと大きな値を設定 するのが一般的でしたが、サイトの成長の早い昨今では、この程度に短縮される傾向にあります）更に\$TTLは、各レコード毎に指定することも可能です。|
|||
|SOAレコード|SOAは「Start Of Authority」の略です。「Start」は「権限のあるゾーンの記述がここから始まる」というような意味です|
|@|@は、直近の\$ORIGINディレクティブで定義されたゾーン名をあらわしています。@以前に$ORIGINディレクティブがない場合には、named.confで指定したゾーンのゾーン名です。「@」の代わりに、ゾーン名をそのまま書くことも可能です。|
|MNAME|このゾーンのプライマリーDNSサーバを指定します。ホスト名をFQDNで設定します（末尾に「.」が必要です）。IPアドレスはだめです。ここに設定するホスト名は、AレコードでIPアドレスを定義したものでなければなりません（CNAMEで定義したホスト名はだめ）|
|RNAME|このゾーンの責任者のメールアドレスをFQDNで設定します。具体的には、通常のメールアドレスのローカルパートとメールドメイン部の区切り文字である「@」を、「.」におきかえた文字列です。MNAME同様、末尾に「.」をつけないと、ゾーン名が自動的に付加されてしまいます。メールアドレスのローカルパートが「.」を含むものである場合、そのドットの直前に「\」記号を付加してエスケープします。メールアカウントにはホストの管理者のメールアドレスの予約語である「hostmaster@<ホストのFQDN>」を使うのがセオリーですが、このアドレスには管理者が送信されたメールを読むことが確実であるアドレスを設定します。|
|SERIAL|ゾーン情報のシリアル番号です。符号なし32ビットの数値で、更新毎に連番で加算されることが前提となっています。|
|REFRESH|RETRY, EXPIREとともに、スレーブネームサーバによる更新チェックのタイミング等を決定する値のひとつです。REFRESH はスレーブサーバがゾーン情報の更新の有無（「SERIAL」の増加）をチェックする周期を秒単位で示す数値です。スレーブサーバに対して更新チェックの 目安として提示されます。チェックの結果、SERIALが増加していれば、スレーブサーバはゾーン転送によりゾーン情報を更新します。|
|RETRY|マスターサーバやネットワークの障害によりポーリングが失敗した際に、再試行の周期を秒単位であらわす数値です。原則としてREFRESHの値の整数分の１の値を設定します。|
|EXPIRE|マスターサーバがダウンした場合、スレーブサーバはこの数値で提示された期間が経過するまでは、最後にマスターサーバから受け取ったゾーン情報を有効なもの としてサービスを継続しますが、期間満了とともにそのゾーン情報を無効として廃棄します。その後のこのゾーンに関するDNSクエリーはエラーとなります。値を決定する目安は、マスターサーバの予想される最大ダウンタイムよりも長い期間ということになります。RFC1912は、2～4週間（1209600～2419200）を推奨しています。|
|MINIMUM|当初はDNSキャッシュサーバが一度検索したレコードをキャッシュに保持(通常のキャッシュ)しておく期間(単位は秒)として使用されていましたが、現在はドメイン名が存在しなかったという情報を保持(ネガティブキャッシュ)する期間(同じく単位は秒)として使用されています。|
|||
|NS|NSはName Serverの略で、ネームサーバを指定します。自分自身と、普通はISPが用意するセカンダリネームサーバを指定しておきます。ここでも、ドメイン名の最後にピリオドが必要です。|
|A|ホスト名はA（Address）レコードで記述します。ここでは、ホスト名の後ろにピリオドを付ける必要はありません。IPアドレスも普通に記述します。|
|PTR|逆引きのデータを記述します。|
|MX|メールサーバを指定するMX（Mail eXchanger）レコードです。ここでMXに続く数値はプリファレンス値といいます。この数値が小さい順に、順次メールサーバとしてアクセスするという指定になります。相対的な大きさだけが問題なので、符号なし16bits数値の範囲なら好きなように設定できます。つまり、予備のメールサーバを作っておけるわけです。|
|CNAME|CNAME（Canonical NAME）レコードがあります。これは、1つのIPアドレスに幾つかのホスト名を割り当てるときに使います。これもMXレコード同様に、正引きデータを記述したファイル内に記述します。|
|TXT|好きにコメントが書ける行となります。最近はSPFレコードを記載することが多いです。SPFとはメールの送信元アドレスの偽装を防止する技術です。詳細は省きます。|

下記は一例です。

```lang:hogehoge.co.jp.db
$ORIGIN hogehoge.co.jp.
$TTL    3600
@       IN      SOA     ns.hogehoge.co.jp. hostmaster.hogehoge.co.jp.  (
                                2016030201      ; Serial
                                600    ; Refresh
                                100     ; Retry
                                2419200 ; Expire (4week)
                                900 )  ; Minimum
        IN      NS      ns.hogehoge.co.jp.
        IN      NS      2nd.dnsv.jp.
        IN      MX 10   hogehoge.co.jp.
        IN      TXT     "v=spf1 +a:ns.hogehoge.co.jp ~all"
        IN      A        xxx.xxx.xxx.xxx

ns      IN      A        xxx.xxx.xxx.xxx
smtp    IN      CNAME    ns
pop     IN      CNAME    ns
www     IN      CNAME    ns
```

##テストコマンド

###問い合わせのテスト
問い合わせが出来るかテストします。再帰問い合わせ不許可の所から
管理ゾーン、管理外ゾーンに対して問い合わせし、挙動を確認しておきましょう。
`dig @マスターサーバ hogehoge.co.jp`
`dig @マスターサーバ 管理外ドメイン`
管理外ドメイン時は「status: REFUSED」となるはずです。

###ゾーン転送のテスト
下記のコマンドでゾーン転送が出来るかテストできます。
allow-transferが効いているか、許可、許可外からテストしておきましょう。
`$ dig @マスター・サーバ hogehoge.co.jp axfr`
許可外の場合は「Transfer failed」となるはずです。

###DNSSECのテスト
DNSSEC対応済のDNS「hogehoge.co.jp」に対してdigコマンドで動作確認を行います。
`$ dig +dnssec @マスター・サーバ hogehoge.co.jp`
flagsに「ad」があればDNSSECでの検証に成功しています。

##参考
https://www.isc.org/downloads/bind/doc/bind-9-8/
http://centossrv.com/bind.shtml
http://d.hatena.ne.jp/carme-264pp/20110121/1295599232
http://www.eis.co.jp/bind9_src_build_4/
http://www.nina.jp/server/redhat/bind/named.conf.html