連載
» 2003年09月09日 00時00分 UPDATE

実用 BIND 9で作るDNSサーバ(9):BIND 9のセキュリティ対策 (1/2)

DNSは広く公開するサービスであるため、その運用には細心の注意が要求される。BINDを攻撃者から守るには何をすればよいか。今回はBINDで行うべきセキュリティ対策を紹介する。(編集局)

[鶴長鎮一,@IT]

 もともとがパブリックなサービスであるDNSは、ゾーン情報を広く知らしめるための手段であり、そこへアクセスする対象も不特定多数です。一方キャッシュサービスは利用対象者を限定することができます。セキュアなサービスを提供するには、まずBINDの働きを整理し、それに応じた方策と万が一の場合の善後策を準備する必要があります。

DNS全般の対策

キャッシュサーバとゾーンサーバの分離

 キャッシュサーバとゾーンサーバとでは、セキュリティに対するアプローチもサービスを利用する対象も違います。サーバを物理的に、できればキャッシュサーバはNAT配下などのクローズドな環境で利用することが望ましいといえます(図1)。

図1 キャッシュサーバとゾーンサーバの分離 図1 キャッシュサーバとゾーンサーバの分離

 ゾーンサーバでキャッシュサービスを提供しないのであれば、named.confを次のように設定し、一切の再帰問い合わせを無効にします。

options {
    (省略)
        recursion no;
    (省略)
};
ゾーンサーバのnamed.conf

ユーザー権限の奪取対策

●専用ユーザーとchrootの利用

 rootをはじめとするユーザー権限の奪取は、ソースコードに潜むバッファオーバーフローを突くことで行われます。ユーザーには、ソースコードそのものの脆弱性を是正することは簡単にはできません。もしバッファオーバーフロー脆弱性が報告されたら、問題のBINDのアップデートを行う以外にすべはありません。

 そこで、ユーザー権限が奪取されてしまった場合を前提とした策として、最小権限のユーザーでnamedプロセスを起動することが考えられます。本連載では第1回以降、一貫してnamedプロセスの起動はnamedユーザー、namedグループを使用しており、この方法はすでに導入済みのはずです。

 今回はもう一歩踏み込んで、chrootを導入します。chrootはファイルシステムのルートディレクトリの位置を変更する機能で、特定のプロセスに対して任意のディレクトリをルートディレクトリに見せかけることができます。あるディレクトリへchrootすると、そのプロセスとその子プロセスは、chrootしたディレクトリより上の階層のファイルにアクセスできなくなります()。もしユーザー権限が奪われてしまっても、jail(牢獄)に幽閉してしまおうというわけです(編注)。

注:chrootはFTPサービスでは常とう手段となっており、多くの場合ログイン直後のホームディレクトリを「/」にします。

編注:本記事で「jail」と呼んでいるのは、chroot環境のことである。FreeBSDのjailコマンドとは無関係であることに注意。

 BINDはchrootを考慮した実装がすでに行われており、named起動時のオプションの1つとして指定可能です。後は、chrootでルートディレクトリに見せかけるディレクトリを準備するだけです。では、/var/namedをjailとする場合を例に手順を見ていきましょう()。

注:ここでは、BIND 9がインストールされていることを前提にしています。第1回第2回を参考に、インストールしておきましょう。

 まず、jailに以下のファイルを用意します。

  /var/named($jail)
    dev
  null
random
etc
  localtime
named.conf
var
  log(注)
named
  named.ca
local.zone
そのほかのゾーンファイル……
run
named
  named.pid(起動時に作成される)
 
  注:すぐに必要になることはありませんが、この後紹介する「BIND 9のログで利用状況を把握する」で使用します。

 ゾーンファイルの保存先として使用していた/var/namedは、整理しやすいようにリネームして新たな/var/namedを用意します。

# mv /var/named /var/named_org
# mkdir /var/named

 $jail下に移動し、必要なディレクトリを用意します。mkdir実行の際は「-p」オプションを指定し、一気に親ディレクトリとサブディレクトリを作成します(参考:親ディレクトリとサブディレクトリを同時に作成するには)。

# cd /var/named
# mkdir -p dev etc var/run/named var/log var/named

 $jail/etc下に、named.confファイルとlocaltimeファイルを用意します。localtimeファイルは、namedプロセスが日付時間関数を使用する場合にローカルなタイムゾーンを取得し、正しい時刻を得るのに必要です。$jail/etc/named.confを用意する際は、オリジナルの/etc/named.confをリネームしておきます。

# cd /var/named/etc/
# cp /etc/named.conf .
# cp /etc/localtime .
# mv /etc/named.conf /etc/named.conf.org

 次に、ゾーンファイルを用意します。ここでは、以前/var/namedで使用していたものをそのまま使用します。

# cd /var/named/var/named/
# cp /var/named_org/*.* /var/named/var/named/.

 ここで、ファイルのパーミッション/オーナー/グループを整えます。$jail下はnamedユーザー/namedグループで、/var/namedへのアクセスを限定します。

# cd /var/
# chown -R named.named named
# chmod 700 /var/named

 最後に、mknodコマンドを使用して$jail/dev下にデバイスファイルを作成します。

# cd /var/named/dev/
# mknod null c 1 3
# mknod random c 1 8
# chmod 666 *

 namedの起動の際は、「-t」オプションで$jailを指定します。

#/usr/local/sbin/named -u named -t /var/named
注:namedのパスはインストール状況に合わせて変更してください。

 /etc/init.d下の起動スクリプトを使用している場合も、スクリプトを編集してnamedに「-t /var/named」の指定を行います。Red Hat Linuxは、起動スクリプトを編集しなくても、/etc/sysconfig/namedファイルに下記の行を追加するだけで構いません。

ROOTDIR="/var/named"
/etc/sysconfig/named

 jail利用時は、$jail/etc/named.conf(/var/named/etc/named.conf)がデフォルトで使用されます。しかし、起動スクリプトの多くは、named起動の条件として/etc/named.confの有無を確認するため、以下のような修正を加えます()。

注:Red Hat Linuxは「${ROOTDIR}/etc/named.conf」で定義されているため、変更の必要はありません。

[ -f /etc/named.conf ] || exit 0
/etc/init.d/named(修正前)

[ -f /var/named/etc/named.conf ] || exit 0
/etc/init.d/named(修正後)

 named.confファイルの修正は特に必要ありません。-tオプションを指定することで、設定ファイル中の各ルート(/)ディレクトリは$jailに変換されます。

options {
        directory "/var/named"; ←実際のファイルパスは/var/named/var/named
        pid-file "/var/run/named/named.pid"; ←実際のファイルパスは/var/named/var/run/named/named.pid
(省略)
/var/named/etc/named.conf例

 本稿では、named.confに「include」を用いた外部ファイルの読み込みは紹介しませんが、includeを使用する場合もファイルパスは$jailをルートディレクトリに見立てるため、外部ファイルは$jailに合わせてコピーや移動しておきます。

(省略)
include "/etc/rndc.key";
(省略)
/var/named/etc/named.conf例
注:
このような場合は、rndc.keyファイルを/var/named/etc/に置く必要があります。

 設定したら、/var/log/messagesに出力されるメッセージやdigコマンドなどを利用して、正常に動作しているかどうかを確認します。treeコマンドが使えるなら、

$ tree /var/named

を実行し、先に紹介したファイル一覧がすべて用意されているか確認しましょう。

BINDのバージョンを非表示にする

 使用しているBINDのバージョンは、外部からでも簡単に引き出すことができます。

$ dig @対象のDNSサーバ version.bind chaos txt
(省略)
;; QUESTION SECTION:
;version.bind.                  CH      TXT

;; ANSWER SECTION:
version.bind.           0       CH      TXT     "9.2.2"
(省略)

 バージョンが分かることで、バージョン特有の脆弱性を攻撃される可能性があるため、意図的にバージョン名を隠ぺいまたは置き換えます。

options {
    (省略)
        version "XXX DNS Server 1.0X";
    (省略)
};
named.conf

DoS/DDoS攻撃対策

 DoS(Denial of Service)やDDoS(Distributed Denial of Service)攻撃は、サーバそのものに及ぶ被害より、DNSとして正常なサービスを提供できなくなることに留意する必要があります。DNSサーバやネットワーク中継装置に対して、ICMP PingやAckなどの不特定で大量のパケットを送り付けるため、未然に防ぐことは困難です。

 そこで、DoS/DDoS攻撃を早期に発見し、攻撃元および攻撃対象を特定して中継装置でのフィルタリングやサーバ自身でのiptablesなどを用いたパケット制限(Linuxで作るファイアウォール[パケットフィルタリング設定編])を実施します。

●blackholeの設定

 DoSのように攻撃元が絞れる場合は、BIND 9でも対応可能です。named.confのoptionsステートメント中のblackholeで攻撃元のIPを指定し、一切の問い合わせ要求を無視するようにします。

options {
    (省略)
        blackhole {
            192.168.10.100; ←ホスト指定
            192.168.10.0/24; ←ネットワーク指定
            (省略)
        };
    (省略)
};
named.conf

●rndcを使った利用状況の把握

 肝心なのは、現在攻撃を受けているのか、被害が出ている場合はどこから攻撃を受けているかを一刻も早く察知することです。そのためにも、普段からDNSに対する問い合わせ要求数をチェックしたりBIND 9のロギング機能を有効にするなど、日ごろの運用監視が重要になります。

 運用監視については回を改めて紹介しますが、ここではrndcを使った統計情報の取得方法と、BIND 9のロギング機能を有効にする方法について説明します。

 rndcの設定および使用方法については、第5回を参照してください。次にrndcの引数にstatsを指定し、/var/named/下(jailを使用している場合は$jail/var/named/下)に統計情報を出力させます。

# /usr/local/sbin/rndc stats

+++ Statistics Dump +++ (1062085004)    #出力開始時間はUNIX System Time(1970年1月1日から経過した秒数)で表示される
success 264     #問い合わせに成功したクエリーの数
referral 0      #問い合わせに対し参照となったクエリーの数
nxrrset 0       #問い合わせに対するレコード型が存在しなかったクエリーの数
nxdomain 5      #問い合わせに対するドメイン名やホスト名が存在しなかったクエリーの数
recursion 6     #再起問い合わせを行ったクエリーの数
failure 0       #エラーとなったクエリーの数
--- Statistics Dump --- (1062085004)
/var/named/named.stats例

 新しいデータは、ファイルの末尾に追加されます。crontabなどを利用して、必ず等間隔で統計情報を出力するようにし、異常な増加が見られないか確認するようにしましょう。

 rndcの統計情報は貴重な情報ですが、数字のままで確認するのはあまり効率が良くありません。そこで、数値を基にMRTGなどのグラフ化ユーティリティで視覚的にとらえられるデータを作成するなどの工夫が必要です。これについては、今後BIND 9の運用管理の紹介で取り上げます。

●BIND 9のログで利用状況を把握する

 次に、BIND 9のロギングを有効にする方法を見ていきます。これまでに紹介してきた設定では、BIND 9が出力するログはsyslogdを通して/var/log/messagesに記録されます。ところが/var/log/messagesには、BIND以外にも/etc/syslog.confの設定によってさまざまなサービスのログが出力されており、あまり可読性が良いとはいえません。また、BINDのログももう少しきめ細かいものが望まれます。そこで、named.confにloggingセクションを追加してさまざまなログを出力するようにします。

logging {
        channel "log_default" { #ログの出力方法をlog_defaultとして定義
                file "/var/log/named.log" versions 7 size 10m;  #1ファイル当たり10Mで最大7世代までログファイルをローテーションする
                                                                #chrootを利用している場合は、あらかじめ$jail/var/logを用意する
                severity info;  #ログレベルinfo以上のものを記録
                print-time yes; #時間をログに表記
                print-category yes; #カテゴリをログに表記
        };
        channel "log_security" {    #ログの出力方法をlog_securityとして定義
                file "/var/log/security.log" versions 3 size 10m;   #1ファイル当たり10Mで最大3世代までログファイルをローテーションする
                severity info;
                print-time yes;
                print-category yes;
        };
        category default { "log_default"; };    #デフォルトログをlog_defaultの定義どおりに出力
        category security { "log_security"; };  #セキュリティログをlog_securityの定義どおりに出力
        category client { "log_security"; };    #クライアントログをlog_securityの定義どおりに出力
};
/etc/named.conf(jailを導入している際には$jail/etc/named.conf)

 攻撃元を簡単に特定するため、問い合わせクエリーをすべて記録することもできます。ただし全クエリーが出力されるため、サーバリソースの消費に注意します。

logging {
        (省略)
        channel "log_queries" {
                file "/var/log/queries.log" versions 3 size 10m;
                severity info;
                print-time yes;
                print-category yes;
        };

        category queries { "log_queries"; };
        (省略)
};
/etc/named.conf(jailを導入している際には$jail/etc/named.conf)

 categoryにはdefault/security/clien以外にもgeneral/configなどが指定できますが、ここでは上記にとどめ、詳細については次回以降「BIND 9の運用監視」で紹介します。

       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。