Kozupon.com    
 
 BINDのセキュリティ!


DNSサーバをBINDで構築されている人は世の中ほとんどであろう!
最近、BINDモジュールのセキュリティホールを突いて攻撃をかけてくるクラッキングも増えている。そのため、BIND8・9用のパッチモジュール等が公開され、パッチを施している人が多いと思う。また、BINDの設定方法を細かく説明する親切なサイトは多数有るため、ここでは説明しないがBINDの設定ミスが原因でセキュリティホールになる事実もある。さらにBINDのゾーンデータベースの内容が参照され、それがセキュリティホールになる場合もある。
このような背景の中、BIND8・9には、セキュリティに効果がある拡張パラメータがあるので、その機能と設定方法をここでは説明する。


1. ゾーンデータベース変更の通知
プライマリサーバとセカンダリサーバが存在するゾーンの場合、ゾーンデータベースが更新されたか否かの確認は、SOAリソースレコードで設定されたリフレッシュ間隔に合わせて行われる。しかし、この場合更新されたゾーンデータベースがセカンダリサーバに転送されるまでの一定時間が必要で、その間プライマリサーバとセカンダリサーバのゾーンデータベースの内容は、異なったものとなってしまう。特に階層化されたセカンダリサーバが存在する巨大なゾーンの場合は、そのタイムラグは無視できないほど大きくなってしまう。このような問題を解決する手段がゾーンデータベース変更通知機能である。
ゾーンデータベースの変更通知機能をプライマリサーバで有効にすると、ゾーンデータベースの変更が有った場合、変更があったことがセカンダリサーバに即座に通知される。通知を受け取ったセカンダリサーバは、SOAリソースレコードで設定されたリフレッシュ間隔に関わらず、即座にプライマリサーバのゾーンデータベースのシリアルナンバーがセカンダリサーバのシリアルナンバーより大きい場合、ゾーンデータベース転送が行われる。
ゾーンデータベースの変更通知があった場合でも、ゾーンデータベースのシリアルナンバーの確認が行われ通常の手順と同様な転送が行われることによって、ゾーンデータベースの変更通知を導入することによるセキュリティー上の弱点が発生する事を防いでいる。また、ゾーンデータベースの更新の通知に利用されるnotifyプロトコルは軽量で、利用された場合にもネットワークに負荷をかけないような配慮もされている。
以下に、ゾーンデータベースの変更通知を行うように設定した、プライマリサーバのゾーンデータベースの内容を示す。

named.confの内容

省略

zone "kozupon.com"{
    type master;
    file "kozupon.com.zone";
    notify yes;

};

省略

サブステートメントnotifyに引数yesが設定されている場合、ゾーンデータベースの変更が行われるとその情報が、ゾーンデータベースファイルにNSリソースレコードで記述されている全てのネームサーバに送られるようになる。


2. ゾーンデータベースの動的更新
BIND付属のプログラムnsupdateを利用することで、ゾーンデータベースを動的に更新することが出来る。nsupdateを利用する場合、ゾーンデータベースを変更した場合には通常必要なnamedの再起動が必要になる。
BIND側の設定は、デフォルトでは全ての動的更新を受け付けない設定になっている。このため、動的更新を行う場合には動的更新を受け付けるホスト設定の行う必要がある。動的更新に関する設定はゾーン単位で行う。以下にホスト自身から動的更新を受け付けるように設定する例を示す。


named.confの内容

zone "kozupon.com" {

省略

    allow-update {
           localhost;
    };

省略

};

以上のように設定すると、ホスト自身から動的更新を受け付けるようになる。動的更新はセキュリティ上の弱点となる可能性もあるため、最低限のホストからのみ受け付けるように設定するようにする。

nsupdateは、/etc/resolv.confファイルのnameserverディレクティブの設定にしたがって、クエリーを発行するDNSサーバを選択している。なので、ここで設定されているDNSサーバが、クエリーを受け付ける設定にされている必要がある。
nsupdateは、動的更新の内容をプロンプトに対して1行づつ入力していくスタイルと、更新内容を指定のファイルに記述しておいてこのファイルから入力するスタイルの2種類が選択可能である。設定できる内容は同一で、ここではプロンプトを利用した設定について説明する。
nsupdateを起動するとプロンプトが出力される。nsupdateの書式を以下に示す。

<section>: { <operation> } <RR>

<section>には、"prereq"と"update"の2つが利用可能である。prereqはupdateを行う前の前処理指定を行う。
<operation>には、"add"と"delete"の2つが利用可能である。addはゾーンデータベースに対する追加、deleteはゾーンデータベースからの削除を行う。
<RR>には、ゾーンデータベースファイルに記述するのと同じリソースレコードを指定する。ただし、ここではTTLの指定が必須となっている。

ゾーンkozupon.comに対して、新しいホストnew.kozupon.comを加える場合の例を以下に示す。ここでは、new.kozupon.comのIPアドレスを192.168.0.40としている。

# nsupdate
> update add new.kozupon.com 600 IN A 192.168.0.40
>  ←スペース入力
> Ctrl + D

コマンドを入力した段階では、namedに対してクエリーは送れない。入力した内容を有効にするには、必ずスペースを一文字入力してEnterを入力する。書式に誤りがある場合には、クエリーは発行されずにnamedは終了する。
また、クエリーが発行された場合でも動的更新がうまくいかなかった場合、BINDの設定で動的更新を受け付けるホストの設定に失敗していることが考えられる。

ゾーンデータベースからエントリの削除を行う場合には、deleteを利用する。削除の場合にはリソースレコードをすべて指定する必要はないが、エントリのクラスと型を指定する必要がある。ゾーンkozupon.comからホストnew.kozupon.comを削除する場合の例を以下に示す。

# nsupdate
> update delete new.kozupon.com IN A
> ←スペース入力
> Ctrl + D

クエリーが発行されて動的更新が受理された場合の出力は、あらかじめ削除する対象のホストの存在を確認するため、追加に比べて出力が長くなる。


3. リゾルバ
リゾルバ(name resolver)は、DNSにおけるクライアントプログラムである。その名が示す通り、名前を解決するために動作する。名前解決が必要なプログラムは。gethostbyaddr()やgetaddrbyname()などの関数を利用してIPアドレスやホスト名を取得するが、このとき利用されるプログラムがリゾルバだ。
リゾルバは、/etc/host.confファイルの内容にしたがって動作する。/etc/host.confファイルの例を以下に示す。/etc/host.confふぁいるは、通常はこの例の通り設定する。

/etc/host.confファイル

# cat /etc/host.conf
order hosts,bind      (1)
multi on           (2)

(1)で、リゾルバが名前解決する順番を指定している。hostsは/etc/hostsファイルを利用した名前解決を表し、bindはDNSを利用した名前解決を表す。先に定義された方法から優先的に利用される。この設定では、まず/etc/hostsファイルを参照し、問い合わせがの内容が見つからなければDNSを利用する。
(2)では、同一のホストに複数のIPアドレスが割り当てられている場合の動作方法を指定する。"on"では割り当てられている全てのアドレスを問い合わせ元に送信し、"off"の場合は一番最初のアドレスのみ送信する。

/etc/resolv.confファイルは、リゾルバの設定ファイルである。/etc/resolv.confの例を以下に示す。

/etc/resolv.confファイル

# cat /etc/resolv.conf
search kozupon.com
nameserver 192.168.0.1

/etc/resolv.confファイルに利用可能なディレクティブについて以下に示す。

search <domain name>・・・・・・

searchには、検索を指示されたホスト名に付加する複数のドメイン名を指定できる。FQDNを使用せず、ホスト名で検索を行うことが出来るようになるが、ドメイン名を複数記述すればそれだけ検索速度は低下する。
通常は、リゾルバが動作するホストが所属するドメイン名を記述する。

domain <domain name>

domainには、リゾルバが動作するホストの所属するドメインを記述する。但し、複数のドメイン指定は出来ない。

nameserver <address/netmask>

nameserverはにはDNSサーバのIPアドレスを指定する。DNSサーバは複数設定する事が可能なのだが、記述した順に利用されることになる。

sortlist <address/netmask>

sortlistには、リゾルバが優先的に使用するネットワークアドレスとサブネットマスクを指定する。

options [ndots:[num] | debug]

optionsには、リゾルバのオプションを設定する。<options>に設定できるパラメータは"ndots"と"debug"の2つである。
ndotsでは、検索リストを利用したドメイン負荷の対象にしないホスト名に含まれるドット数を指定する。デフォルトでは1で、検索する名前にドットが一つも含まれない場合には、検索リストが利用されることになる。
debugを利用すると、デバッグ出力が有効となる。デバッグ出力は、リゾルバを利用するプログラムの標準出力に対して行われる。


4. BINDのログ
他のソフトウェア同様、BINDにおいてもログの管理がセキュリティの基本になる。BINDのログはsyslogを利用して出力するほか、BIND独自にログを出力することも可能である。BINDの出力ログの設定書式を以下に示す。この設定は、/etc/named.confファイルに対して行う。

logging {
    category <category> { <chanel> };

};

カテゴリーステートメント<category>に設定できる値と、それぞれの内容について以下に示す。

カテゴリ 内容
default すべてのカテゴリである。チャネルを指定されないカテゴリに属するログメッセージは、すべてdefaultカテゴリにとして処理される。
config 設定ファイルの高レベルな処理を行ったときのログメッセージ。
parser 設定ファイルの低レベルな処理を行ったときのログメッセージ。
lame-servers "Lame server on・・・"のようなログメッセージ。
statistic 統計情報を表すログメッセージ。
update 動的更新を行ったときのログメッセージ。
ncache ネガティブキャッシュが発生したときのログメッセージ。
xfer-in ゾーン転送でデータを受信したときのログメッセージ。
xfer-out ゾーンデータを送信したときのログメッセージ。
db データベース操作を行ったときのログメッセージ。
eventlib イベントシステムからのでバッグ情報を表すログメッセージ。指定がない場合にはdefault-debugチャネルのみ利用される。
packet 送受信したすべてのパケットを記録したログメッセージ。指定がない場合にはdefault-debugチャネルのみ利用される。
notify notfiyプロトコルの記録を表すログメッセージ。
cname "・・・point to a CNAME"のようなログメッセージ。
security 許可/不許可になったリクエストを記録したログメッセージ
os オペレティングシステムの問題を記録したログメッセージ。
insist 内部的欠陥を記録したログメッセージ。
maintenance 定期的なメンテナンスを記録したログメッセージ。
load ゾーンデータベースを読み込んだときのログメッセージ。
response-checks "Malformed response・・・"など、応答が変である場合のログメッセージ。

カテゴリサブステートメントに設定するチャネル<channel>では、ログメッセージの出力先を指定する。利用するチャネルの書式を以下に示す。

channel <name> {
    <output-definition>
    severity { critical | error | warning | notice | info | debug [level] | dynamic ];
    print-severity [ yes|no ];
    print-category [ yes|no ];
    print-time     [ yes|no ];
};

チャネルは必ずloggingステートメント内部に記述する。
<output-definition>には、ファイル、syslog、破棄の3種類を選択することが出きる。それぞれの書式は以下に示す。

file <filename> versions [ <num>|unlimited ] size <size>;   ファイル
syslog <facility>                         syslog
null;                                 破棄

ファイルを指定したときに設定する3つの引数は、それぞれの出力先のファイル名、ローテートして保存する古いログファイル個数、ログファイルのローテーションを行うファイルの上限サイズを設定している。つまり、ログは<filename>で設定したファイル名のファイルに出力され、<size>で設定したファイルサイズになるとローテーションが行われ、古いログファイルを<num>個だけ保存する。unlimitedを設定した場合、古いファイルは削除せずに無限に保存し続ける。
syslogを指定したときに設定する<facility>は、/etc/syslog.confファイルで設定するfacilityと同じ意味である。破棄を指定した場合、他のステートメントは無視されることになる。

BINDでloggingステートメントを設定しない場合、デフォルトの設定が行われたものとして動作する。デフォルトの設定を以下に示す。

■デフォルトのlogging設定

logging {
    category default { default_syslog; default_debug; };
    category panic { default_syslog; default_stderr; };
    category packet { default_debug; };
    category eventlib { default_debug; };
};

また、ここで利用されているチャネルもデフォルトで用意されているものである。デフォルトで用意されているチャネルの内容を以下に示す。

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

channel default_syslog {
    syslog daemon;
    severity info;
};
channel default_debug {
    file "name.run";
    severity dynamic;
};
channel default_stderr {
    file "<stderr>";
    severity info;
};
channel null {
    null;
};

default_syslogチャネルは、daemonファシリティを利用してinfoレベルのsyslogへのログ出力を行うことを設定している。
default_debugチャネルは、ファイル/var/named/named.runへdynamicレベルでログの出力を行うことを設定している。
default_stderrチャネルは、namedの標準エラー出力を利用してinfoレベルのログ出力を行うことを設定している。
nullチャネルは、エラーメッセージを破棄するように設定している。

■channelサブステートメントの記載例(chrootの場合)
logging {
     category lame-servers { null; };

     channel log_default {                          #log出力をlog_defaultとして定義
           file "/var/log/named.log" versions 7 size 1m;
  
 #1ファイル1MBの容量で7世代迄logrotateする
           severity info; print-time yes;              
#loglevelはinfo以上
           print-category yes;                    
#categoryをlogへ書く
                   };
     channel log_security {                         
#log出力をlog_securityとして定義
           file "/var/log/security.log" versions 7 size 1m;
           severity info; print-time yes;
           print-category yes;
                    };
     category default {"log_default"; };                 
#default logをlog_defaultラベルの通り出力
     category security { "log_security"; };               
#security logをlog_securityラベルの通り出力
};

default_syslogでもlog_default何れでも良いが、channelでlogを必ず取るような設定にして置いた方が良いと考える。例えば、以下のようなrootサーバのアドレスが変更になってる旨のメッセージなども出力してくれるからだ。

17-Nov-2007 07:40:42.975 general: checkhints: view globalnet: L.ROOT-SERVERS.NET/A (199.7.83.42) mis
sing from hints
17-Nov-2007 07:40:42.976 general: checkhints: view globalnet: L.ROOT-SERVERS.NET/A (198.32.64.12) ex
tra record in hints
17-Nov-2007 07:48:39.195 general: checkhints: view localnet: L.ROOT-SERVERS.NET/A (199.7.83.42) miss
ing from hints
17-Nov-2007 07:48:39.196 general: checkhints: view localnet: L.ROOT-SERVERS.NET/A (198.32.64.12) ext
ra record in hints


5. セキュリティ設定
5.1 クエリーの制限
BIND8、BIND9ともに、設定によって追加することが出きるセキュリティ設定がいくつかある。セキュリティ設定のうち主なものをこれから解説する。

"allow-query"、"allow-transfer"、"allow-update"、"allow-recursion"が参照するIPアドレスはアドレスリスト(address list)と呼ばれている。アドレスリストを指定する方法と意味を以下に示す。

パターン 意味
any すべてのホスト
none なし
localhost 自ホスト
localnet 自ホストが所属しているネットワーク
a.b.c.d[/m] IPアドレスとネットマスクによる指定

これらの書式の先頭に感嘆符"!"をつけると、パターンの反転となる。つまり"!any"は"none"と同じことになる。

BINDでは、アドレスリストに加えて、アクセスコントロールリスト(Access Control List;ACLs)を利用することも出来る。アクセスコントロールリストを利用して、頻繁に利用するIPアドレスやネットワークに対して名前を付けることによって、IPアドレスやネットワークに関する設定をより簡単にすることが出来る。また、IPアドレスやネットワークに関する書き違えや、変更時に一括して変更できることによる設定の間違いを減らすことが出来る。アクセスコントロールリストの書式を以下に示す。

acl <acl name> { a.b.c.d/m1; [e.f.g.h/m2]; ....};

指定したIPアドレスやネットワークを、<acl name>という名前に設定することが出来る。IPアドレスやネットワークは続けて記述することが出来、複数のIPアドレスやネットワークを一つの<acl name>で指定することが出来る。アクセスコントロールリストを利用して、クエリーを受け付けるホストを制限した例を以下に示す。

★ クエリー制限の例

// #/etc/named.conf
acl private-net { 192.168.0.0/24; };
options {

中略

    allow-query { private-net; };

中略

};

アクセスコントロールとアドレスリストのパターンを、混在して利用することも可能である。

ゾーン転送を行うことによって、DNSサーバに保存されているゾーンデータベースの内容をすべて入手することが出来る。ゾーン転送は本来セカンダリサーバに対して行われる処理であるが、手順に従えばどのホストからも行うことが可能である。しかしイントラネット環境などゾーンデータベースの内容が外部から参照できることは、セキュリティ上の弱点となる可能性がある。このような事態を防ぐために、ゾーン転送を行うことが出来るホストを制限することが可能である。
ゾーン転送の制限に関する設定は、/etc/named.confファイルで行う。以下にゾーン転送の制限を行う例を示す。

// #/etc/named.conf
options {

中略

    allow-transfer {
        192.168.0.2;
    };

中略

};

以上のように記述することによって、ゾーン転送を行うホストを192.168.0.2のみに制限することが出来る。ゾーン転送に関する制限が設定されない場合、すべてのホストからのゾーン要求を受け付けることになるため、出来るだけ設定することが望ましい。また、"allow-transfer"をoptionsステートメント内に記述することによって、すべてのゾーンに関してゾーン転送を制限することが出来る。また、"allow-transfer"をzoneステートメント内に記述する場合、デフォルトとして利用されるoptionsステートメントに記述された内容に上書きする形で、そのゾーンに関してのゾーン転送の制限が行われる。

通常の状態では、DNSサーバはすべてのホストからのDNS参照に対して応答する。通常の状態でもネットワーク資源の浪費となる可能性もあるが、場合によってはDNSサーバに対するDoS(Denital of Service)攻撃を受ける危険性がある。さらに、DNSキャッシュポイズニング攻撃の標的にされる可能性もある(詳細は、5.5項参照)。クエリーを受け付けるホストを限定したい場合、以下のような書式を利用する。

// #/etc/named.conf
options {

中略

    allow-recursion {
        192.168.0.0/24;
    };

中略

};

allow-recursionは、再帰(DNSサーバは各レベルを検索し、サーバからサーバへ移動して得られた情報を全てキャシュする)を完全に無効にするのではなく、LAN側だけに制限する。再帰を全面的に禁止したい場合は、セキュリティ的にも named.conf の options に、
recursion no;
と書く方がベストだ。ただし、そうすると localhost を含む全てのLAN内のクライアントからの再帰を行わなくなるので注意が必要だ。

// #/etc/named.conf
options {

中略

    allow-query {
        192.168.0.0/24;
    };

中略

};

クエリーを受け付けるホストを192.168.0.0/24で表されるIPアドレスを持ったホストに限定することが出来る。このような設定をすることによって。クエリーを受け付けるホストを組織内のホストだけに限定するなどの設定を行うことが出来る。
また、"allow-query"をoptionsステートメント内に記述することによって、すべてのゾーンに関してクエリーを受け付けるホストを制限することが出来る。allow-recursionとの違いは、ネームサーバが各ゾーンに対して権限を持つサーバではない場合は、見知らぬホストからのクエリーにはいっさい応答しない。つまり、allow-queryの方が厳しい制限となる。また、"allow-query"をzoneステートメント内に記述する場合、デフォルトとして利用されるoptionsステートメントに記述された内容に上書きする形で、そのゾーンに関してのクエリーを受け付けるホストの制限が行われる。

逆に頻繁にDos攻撃を仕掛けてくるホストなど、クエリーを無視するホストが明らかな場合には、"blackhole"サブステートメントを利用することが出来る。"blackhole"サブステートメントでは、"allow-query"サブステートメントとは逆に、クエリーを無視するホストやネットワークを設定することが出来る。"blackhole"の利用は以下のような記述で行う。

// #/etc/named.conf
options {

中略

    blackhole {
       172.16.0.0/24;

中略

    };

};

以上のように記述することによって、IPアドレス172.16.0.0/24を持ったホストすべてからのクエリーを無視する設定となる。なお、"blackhole"はzoneステートメント内に記述することは出来ず、設定された"blackhole"はすべてのゾーンに対して適用される。
前に説明したように、ゾーンデータベースに登録するリソースレコードの1つにHINFO(Host Infomation)がある。ここには通常、ホストのオペレーティングシステム(OS)やハードウェアについて記述することになるが、場合によっては悪意のあるユーザに対してホストの弱点を教えることになってしまう。
HINFOに関する情報は通常のDNS参照によって入手することが出来るため、前述のゾーン転送の制限では制限できない。このため、状況によってはゾーンデータベースにはHINFOに関する情報を記述しない方がよいことになる。ゾーンデータベースに記述された情報を補強するためのデータをゾーンデータベースに追加する場合には、コメント文などで追加した方が安全となる。
同様の理由から、WKS(Well-Known Service)に関しても、上記の処理が必要になる場合がある。

5.2 バージョン情報を隠蔽する
セキュリティの一つの方法としてBINDのバージョン隠すことが出来る。digコマンドを使って、

# dig CHAOS version.bind TXT

と入力するとBINDのバージョンを調べることが出来る。そこで、以下のようにoptionsステートメントに書くことによってサーバのBINDバージョンを隠すことが出来る。

// #/etc/named.conf
options {

中略

version "This BIND is Non version";

中略

};

5.3 セキュアなnamed.conf設定例
■ BIND9のnamed.confの設定例を以下に示す。

acl localnet {                          ← aclでアクセスコントロールリストを作る
  192.168.255.0/24;
  127.0.0.1;
};

logging {                              ← loggingでlogにはかれるlame serverの煩わしい文字列をはじく!
  category lame-servers { null; };
};

options {
  directory "/etc/namedb";
  auth-nxdomain yes;
  allow-query { 
                       ← クエリーの利用するホストを定義
    localnet;
  };

  allow-transfer {
                      ← ゾーン転送の制限を定義
    localnet;
    203.141.128.33;
  };

  lame-ttl 1800;                        ← lame-server logの保存期間
};
zone "." {
  type hint;
  file "named.ca";
};
zone "kozupon.com"{
  type master;
  file "kozupon.com.zone";

  allow-query { any; };                    ← クエリーによる名前解決を許可
};
zone "0.0.127.in-addr.arpa"{
  type master;
  file "named.local";
};
zone "180.144.141.203.in-addr.arpa"{
  type master;
  file "named.rev";

  allow-query { any; };                    ← クエリーによる名前解決を許可
};

これは、named.confの外向き設定例

5.4 その他
BIND9の場合、"viewステートメント"がある。これは、分離されたゾーンの管理が出来る。例えば、自前サーバのようにグローバルとローカルの名前解決が必要な場合には(自鯖の方は必須かと思う。これが有るのと無いのでは大夫違う)、非常に便利である。例えば、

view "localnet" {                       ← 内向きゾーン定義
    ゾーンファイル定義
};

view"globalnet" {                          ← 外向きゾーン定義
    ゾーンファイル定義
};

■ ネットを調べても、BINDの設定は正確な書き方が指摘されてない。したがって、自己流だが間違いは無いと思う。これは、2006年9月現在、俺自身のviewステートメントを使ったnamed.confの例を以下に示す(セキュリティ設定を考慮したものである)
viewステートメントでLAN側、WAN側に分けて、それぞれのviewステートメント内におのおのセキュリティ設定を施す。
この設定のコンセプトは、optionsでは全てのパラメータを無しとか許可していない。そして、viewステートメント内ではローカル・グローバル別にパラメータ有りだとか無しだとか受け付けるだとかの設定をしている。

acl localnet {                           ← aclでアクセスコントロールリストを作る
192.168.255.0/24;
127.0.0.1;
};
logging {                            
 ← 各種logフォーマットの定義
 category lame-servers { null; };            
← loggingでlogにはかれるlame serverの煩わしい文字列をはじく!
 channel log_security {                   
← log_securityの設定
 file "/var/log/security.log" versions 7 size 1m;
 severity info;
 print-time yes;
 print-category yes;
};
category security { "log_security"; };
};

# デフォルトオプションでは、基本的には再帰、クエリー、ゾーン転送をなしの設定にしておいて、各viewステートメント内で個別にセキュリティ設定を再定義して上書きする形を取る。
options {
 directory "/etc/bind";
 auth-nxdomain yes;                    
← BIND8互換
 allow-query { none; };                   
← 基本的にはクエリーなし
 allow-transfer { none; };                 
← 基本的にゾーン転送なし
 recursion no;                        
← 再帰なし
 lame-ttl 1800;                        
← lame-server logの保存期間
 version "";                          
← バージョン表示をしない
};

# Local Network Zone Setting(以下、ローカルネットワークの設定は、ローカルネット内で有れば何でも有りで良いと思う)
view "localnet" {
 match-clients { localnet; };               
← このローカルネットのホストからは、viewに定義したゾーン情報が参照できる
 recursion yes;                       
← 再帰有り
 allow-transfer { localnet; };               
← ゾーン転送ローカルネットのホストのみ有り
 allow-query { localnet; };                
← クエリーローカルネットのホストのみ有り

 zone "." {
  type hint;
  file "named.ca";
 };
 zone "kozupon.com"{
  type master;
  file "in-kozupon.com.zone";
 };
 zone "lighttpd.jp"{
  type master;
  file "in-lighttpd.jp.zone";
 };
 zone "0.0.127.in-addr.arpa"{
  type master;
  file "named.local";
 };
 zone "255.168.192.in-addr.arpa"{
  type master;
  file "in-named.rev";
 };
};

# Global Network Setting(以下、グローバルネットワークの設定は、ゾーン転送を制限してクエリーは全てのホストから受け付ける事にすれば良いと思う。それと、セキュリティの為に再帰は無しに設定すること)
# この設定は、従来より多く発生している再帰的な問合せを使った DDoS 攻撃の対策になると思う。
view "globalnet" {
 match-clients { any; };                   
← グローバル側のホストからは、viewに定義したゾーン情報が参照できる
 allow-query { any; };                    
← クエリーを、グローバルの全てのホストから受け付ける
 recursion no;                         
← 再帰なし

 zone "kozupon.com"{
  type master;
  file "out-kozupon.com.zone";
  allow-transfer {                       
← ゾーン転送を制限する(許可するゾーン転送は、基本的にローカルネットワークとセカンダリサーバ)
   localnet;
   203.141.128.33;
   203.141.128.34;
  };
 };

 zone "lighttpd.jp"{
  type master;
  file "out-lighttpd.jp.zone";
  allow-transfer {                       
← ゾーン転送を制限する
   localnet;
   203.141.128.33;
   203.141.128.34;
  };
 };

 zone "180.144.141.203.in-addr.arpa"{
  type master;
  file "out-named.rev";
  allow-transfer {                       
← ゾーン転送を制限する
   localnet;
   203.141.128.33;
   203.141.128.34;
  };
 };
 
# Secondary Setting(セカンダリの設定)
 zone "xxxxxxxxxxxxx.jp" {
  type slave;
  file "secondary/xxxxxxxxxxxxx.jp.zone.bak";
  masters {
   61.206.115.83;
  };
 };

};

key "rndc-key" {
 algorithm hmac-md5;
 secret "**********************************************";
};

controls {
 inet 127.0.0.1 port 953
 allow { 127.0.0.1; } keys { "rndc-key"; };
};

以上、named.confの内向き、外向き設定例

注1) named.confは、括弧が多く間違いやすくなるので設定後、
# named-checkconf /etc/named.conf
でシンタックスエラーのチェックをお勧めする。

■ 不正なゾーン転送
Sep 28 18:06:10.648 security: client 210.167.122.58#32781: zone transfer 'kozupon.com/IN' denied
Sep 28 18:19:20.668 security: client 210.167.122.58#32788: zone transfer 'kozupon.com/IN' denied
Sep 28 18:48:08.661 security: client 210.167.122.58#32808: zone transfer 'kozupon.com/IN' denied

このログは、許可されてないホストからのゾーン転送を拒否したlogである。
しかし、このケースはクラッキングの行為と見ることも出来るので注意が必要だ。このlogの場合頻繁にゾーン転送を行っているので、もしかすると悪意があるアクセスと取られても仕方がないだろう。

■ 不正なアップデート
Feb 16 14:32:27.069 security: client 222.149.74.23#32775: update 'kozupon.com/IN' denied

このログは、許可されてないホストからのゾーンの更新を拒否したlogだ。

このような2つのケースの対策として、blackholeステートメントをoptionsで定義して一切の問い合わせ要求を受け付けないと言う作戦がある。そこで、俺自身はこのblackholeステートメントを使う。たとえば、

options {
 省略
 blackhole{ 210.167.122.58;
 
222.149.74.23; }
 
省略
};

こんな感じ。

5.5 DNSキャッシュポイズニング攻撃
キャッシュポイズニング攻撃とは、悪人によりDNSキャッシュサーバのキャッシュデータを嘘のDNS情報に書き換えてしまう攻撃法である。かなりの精度でキャッシュポイズニングが行える方法が見つかっているようだ。
対策としては、キャッシュポイズニング攻撃は、嘘のレスポンスパケットを送り込むことにより、嘘のサーバに誘導させるので、再帰クエリーを制限することが、かなり効果的な対策のようである(5.1項参照)。


6.セキュリティの極めつけはchrootでBINDを動かす
通常BINDをroot権限で動かすことは非常に危険とされている。例えば、BINDのモジュールの脆弱性で実行権限が奪われた場合、root権限を奪われたら致命的だからだ。 通常、namedユーザとかbindユーザを作って実行権限を委ねている。しかし、もっと安全なのはchroot環境(例えば、/var/named/chrootとか)を作って、その中を全てnamed権限で操作できるようにすれば、たとえnamedユーザの権限が奪われても /var/named/chroot のディレクトリのみの被害ですみ、中を荒らされたなら一度/var/named/chroot以下を消去して再構築することが可能となる。例えば、CentOSでBINDをchrootで動かすにはyumでインストールする場合は以下のように、bindの指定にプラスしてbind−chrootをインストールする。Debianでchroot環境を構築するには、 ここ を参照すると良いだろう。

[root@sub2 ~]# yum bind bind-chroot

chrootでBINDを動かしているサーバは、

[root@sub2 ~]# ps -ef | grep named
named 9561 1 0 06:29 ? 00:00:04 /usr/sbin/named -u named -t /var/named/chroot

こんな感じにプロセスが表示される。


7.Built's in Empty Zoneの邪魔なワーニング除去方法(BIND9限定)!

7.1 BINDのログに発生するワーニング
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 0.IN-ADDR.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 127.IN-ADDR.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 254.169.IN-ADDR.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 2.0.192.IN-ADDR.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 255.255.255.255.IN-ADDR.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: D.F.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 8.E.F.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: 9.E.F.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: A.E.F.IP6.ARPA
Jul 29 17:18:10 sub2 named[346]: automatic empty zone: view localnet: B.E.F.IP6.ARPA

やら、

Jul 29 18:01:09 sub2 named[346]: too many timeouts resolving 'ns1.kohg.co.jp/A' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:09 sub2 named[346]: too many timeouts resolving 'ns1.kohg.co.jp/AAAA' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:09 sub2 named[346]: too many timeouts resolving 'ns2.kohg.co.jp/A' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:09 sub2 named[346]: too many timeouts resolving 'ns2.kohg.co.jp/AAAA' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:10 sub2 named[346]: too many timeouts resolving 'ns1.kohg.co.jp/A' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:10 sub2 named[346]: too many timeouts resolving 'ns1.kohg.co.jp/AAAA' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:10 sub2 named[346]: too many timeouts resolving 'ns2.kohg.co.jp/A' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:10 sub2 named[346]: too many timeouts resolving 'ns2.kohg.co.jp/AAAA' (in 'kohg.co.jp'?): disabling EDNS
Jul 29 18:01:11 sub2 named[346]: too many timeouts resolving '238.232/29.56.120.219.in-addr.arpa/PTR' (in '232/29.56.120.219.in-addr.arpa'?): disabling EDNS

のようなログがでる。

7.2 原因
BIND-9.4.1以降に追加された、Built's in Empty Zoneと言う、どうもIPv6用拡張仕様らしい。基本的には、問い合わせした際のタイムアウトエラー状態を表してるようだ。

7.3 対策
named.confのloggingディレクティブ内に、

logging {
category edns-disabled { null; };
};

を設定すれば、disabling EDNSやautomatic empty zone:ワーニングは無くなる。


8.BIND9キャッシュポイズニング(ファーミング)の脆弱性の個人的考察

8.1 概要的には?
BIND9には、キャッシュポイズニング攻撃が容易になる脆弱性がある。影響を受けるシステムベンダの提供する情報を参照して欲しい。

8.2 まずは、キャッシュポイズニング(ファーミング)攻撃とはなんぞや?
DNSサーバの問い合わせ結果の正当性は、「トランザクションID」&「UDPソースポート番号」で決まる。
したがって、問い合わせ要求の結果として、正しいと思われるトランザクションIDとUDPソースポート番号が返されるとDNSサーバは、その結果をDNSキャッシュに書き込む。この性質を利用してるのが、この攻撃である。以下、図1のメカニズムで説明する。

@ ターゲットドメインに対して、存在しないサブドメインをわざと作成して、強制的に名前解決の問い合わせを行う。
A 問い合わせを受けたDNSサーバは、自分のキャッシュには存在しないドメインで有る為、外部に再帰問い合わせを行う。
B 悪人は、再帰問い合わせの結果より早く、あくまでも正当であるようなトランザクションIDとUDPソースポート番号と偽装したドメイン情報をセットした応答を用意してDNSサーバへ送信する。何も知らないDNSサーバは返信結果を自己のキャッシュに書き込む。
C ローカルクライアントは、何も知らずにキャッシュから問い合わせ結果を貰う。
D 偽装された悪のサイトへ誘導される。


  図1

8.3 で対策方法は?
ア.パッチを適用する
使っている製品ベンダから提供されているパッチを適用する。サーバ製品だけでなく、ネームサーバに query を投げる側のノードに対してもパッチが提供されていないか確認する。

イ.アクセスを制限する
再帰問い合わせを受け付ける IPアドレスを制限することで、ある程度キャッシュポイズニング攻撃を受ける可能性を減らすことができる。
後のに記述方法の詳細が有るので参考にしてほしい。しかし、実は制限するだけでは無く、必要無ければ拒否する方が望ましい。

ウ.フィルタリングにより偽造パケットを拒否する
パケットフィルタリングにより、例えば組織内の IP アドレスを送信元アドレスとする偽造パケットがインターネット側からやってくるのを拒否することが可能。
関連する RFC として、RFC 2827、RFC 3704、RFC 3013を参照のこと。

エ.再帰問い合わせを拒否する
グローバル側から DNS queryを受け付けるネームサーバにおいては、再帰問い合わせを受け付けないように設定する。

の対策を施したnamed.confの例)

acl mitynet {
192.168.255.0/24;
127.0.0.1;
};
logging {
category lame-servers { null; };
category edns-disabled { null; };
};
options {
directory "/etc/bind";
auth-nxdomain yes;

allow-query { none; };     # デフォルトは、クエリー禁止
allow-transfer { none; };   # ゾーン転送禁止
recursion no;           # 再帰問い合わせ禁止

blackhole { 210.167.122.58; };
lame-ttl 1800;
version "";
};

# Local Network Zone Setting

view "localnet" {
match-clients { mitynet; };

recursion yes;          # ローカルからは、再帰問い合わせを有効にする
allow-transfer { mitynet; };
allow-recursion { mitynet; }; # ただし、再帰問い合わせを制限する
allow-query { mitynet; };    # さらに、クエリーも制限する

zone "." {
type hint;
file "named.ca";
};
zone "kozupon.com"{
type master;
file "in-kozupon.com.zone";
};
zone "lighttpd.jp"{
type master;
file "in-lighttpd.jp.zone";
};
zone "0.0.127.in-addr.arpa"{
type master;
file "named.local";
};
zone "255.168.192.in-addr.arpa"{
type master;
file "in-named.rev";
};
};

# Global Network Setting

view "globalnet" {
match-clients { any; };

allow-query { any; };      # 全てのクエリーを許可する
recursion no;           # ただし、再帰問い合わせは禁止

zone "kozupon.com"{
type master;
file "out-kozupon.com.zone";
allow-transfer {
xxx.xxx.xxx.xxx;
xxx.xxx.xxx.39;
xxx.xxx.xxx.40;
};
notify yes;
};

zone "lighttpd.jp"{
type master;
file "out-lighttpd.jp.zone";
allow-transfer {
xxx.xxx.xxx.xxx;
xxx.xxx.xxx.39;
xxx.xxx.xxx.40;
};
notify yes;
};

zone "142.157.141.203.in-addr.arpa"{
type master;
file "out-named.rev";
allow-transfer {
xxx.xxx.xxx.xxx;
xxx.xxx.xxx.39;
xxx.xxx.xxx.40;
};
notify yes;
};

};

key "rndc-key" {
algorithm hmac-md5;
secret "********************************************";
};

controls {
inet 127.0.0.1 port 953
allow { 127.0.0.1; } keys { "rndc-key"; };
};

設定し終えたら、 ここ でDNSサーバのテストを行うと良い。
上手い設定のDNSサーバには、「GREAT」の御褒美が付くだろう。
また、BIND9から allow-query-cache サブステートメントが追加されているようだ。
これは、allow-query-cacheが設定されていなくて、allow-queryが設定されていたとしたら、allow-query-cacheは、allow-queryの値が優先される。もし、allow-queryが設定されていなければ、allow-query-cacheは、ローカルのみから許可となるようだ。前述のことからいえる問題は、allow-queryを設定していない場合にローカル以外からのキャッシュ経由の問い合わせを拒否してしまうことだ。
だから、この場合はallow-queryを適切に設定する必要が有るということになりそうだ。

options {
allow-query { mitynet; };
};

allow-queryの設定があれば、allow-query-cacheはその内容を継承するが、設定がなければ
allow-query-cache{ localhost, localnet; };と同等となる。

allow-query:
デフォルト値→{ any; }

allow-query-cache:
デフォルト値→{ localnets; localhost; };

ただし、設定されていなければ、"allow-recursion"の設定が優先される。
"allow-recursion"が設定されていなければ、"allow-query"の設定が優先される。
"allow-query"も設定されていなければ、デフォルト値が優先される。

allow-recursion:
デフォルト値→{ localnets; localhost; };

ただし、設定されていなければ、"allow-query-cache"の設定が優先される。
"allow-query-cache"が設定されていなければ、"allow-query"の設定が優先される。
"allow-query"も設定されていなければ、デフォルト値が優先される。

つまり、特に何も記述しなければ、下記がデフォルト値となる。

allow-query { any; }
allow-query-cache { localnets; localhost; };
allow-recursion { localnets; localhost; };


オ.source port randomization を実装する
DNS 製品ベンダは IETF で検討されているdraft-ietf-dnsext-forgery-resilienceを参照のうえ、対策を検討のこと

以上


 
 
 



Copyright 2008 Kozupon.com.