Firewall定義更新作業の自動化

1.経緯
 Jan/2002にFreeBSD環境でFirewallを構築したものの、監視作業はより煩雑になり、さらには新たなワーム感染行動などで複雑さも増していた。そんな中で遠隔地からの監視作業の必要性が発生し、PDAによるルールセット更新作業を実施可能なように環境を作成した。しかしそもそも監視作業を軽減する目的では無いため、通常の勤務では全く変化が無い。
 そこでこれら一連の監視作業を軽減する目的で、Firewallルールセットの更新を半自動化出来ないか検討することにした。

 PDAによる作業では、監視用プログラムswatchが検知した意図しないアクセスについて携帯電話へメールを送り、確認したIPがFirewallルールセットに含まれていない場合は新たにルールセットを追加出来るように構成。しかしこの作業は遠隔地でPDAを用いた場合に限定して、あくまでも”PCの代わりとして”効果を発揮するものであり、PCで接続可能な勤務先事務所や自宅からの監視作業に貢献できるものでは無かった。

 そこで一連の作業を再度詳細に分類し、それぞれのシーンでシェルスクリプトやPerlスクリプトを組み合わせて自動化出来るよう構成を検討。以下順に解説する。尚、ここで公開するスクリプトに関して著作権云々など書くつもりは無いが出来れば利用は控えて頂きたい。スクリプトを実行する環境やスクリプトに関しての安全性などに於いては全く保証が無い為である。
 もちろんスクリプトの欠点や改良点があれば、連絡頂けるとありがたい。

2.環境
 監視している主なログは二種類。高レベルなswatchルール(swatchのdefault設定)については別途対策が存在するため、本件では触れない。
 (1)公開サーバーの入り口に設置しているルータのログ
 YAMAHA/RT102iはそのログをLinuxへはき出している。このログをswatchで監視し、ルールに適合すればFirewallサーバーへ内容を転送する。
 (2)Firewallで未定義ルールに触れたもののうち、警戒が必要なものについて新たにルールセットへ追加する。


●その1 Router Log
○swatch設定ファイル
# Alert me of bad login attempts and find out who is on that system
watchfor /default(.*)(81|85):25$/
   exec /root/swatchwr.sh $0
   exec /usr/bin/perl /home/foo/www/cgi-bin/atfw/recog.cgi
watchfor /default(.*)(81|85):53$/
   exec /root/swatchwr.sh $0
   exec /usr/bin/perl /home/foo/www/cgi-bin/atfw/recog.cgi
watchfor /default(.*)(81|85):110$/
   exec /root/swatchwr.sh $0
   exec /usr/bin/perl /home/foo/www/cgi-bin/atfw/recog.cgi
watchfor /default(.*)(81|85):113$/
   exec /root/swatchwr.sh $0
   exec /usr/bin/perl /home/foo/www/cgi-bin/atfw/recog.cgi
watchfor /default(.*)(81|85)\s:\secho\srequest$/
   exec /root/swatchwr.sh $0
   exec /usr/bin/perl /home/foo/www/cgi-bin/atfw/recog.cgi

 各ソケットについては言うに及ばず公開しているポートに限定して検査する。その中でもScan行為に関しては厳密に拒否するポリシーである。(81|85)は公開していないソケットを検知する。ログは一行ずつ発生するが、クラスDが81と85の範囲で検知すればこのポリシーに触れることになる。

○IPアドレス転送スクリプト recog.cgi
 1.swatchログから相手先IPアドレスのみを抽出してFirewallへ飛ばす。
 飛ばす方法はPerlのNet::FTPモジュールを使用。
 2.累積ファイルへデータを追加。
  ※FirewallでもRouterでも「累積ファイル」へ受け取ったログそのものを保存している。
 これは該当IPの所在を確認する為に必要なのである。例えば 210.227.170.84 で受け取ったIPを 210.227.170.0/24 に加工してしまうと、その時点のIPを確認できなくなるのでこのような方法を採用している。
 これら累積ファイルなどは「確認スクリプト」で表示して確認できる。この「確認スクリプト」はさらに「記録用CGI」のリンクを出していて、ここでいつでも把握した相手ネットワークの名称やIP範囲などを記録できるようにしている。
 これは自動処理でアクセス拒否設定するネットワークがあくまでもサブネットマスク24に固定している為で、本来制御するべきネットワーク範囲を正確に把握したいからである。

●その2 Firewall Log
○swatch設定ファイル
# Alert me any ICMP packet, that maybe illegal event.
/ipfw: 65515 Deny ICMP:11/
   exec="/root/swatchwr.sh $0",exec="/usr/bin/perl /home/foo/public_html/cgi-bin/atfw/recog.cgi"
/ipfw: 65515 Deny ICMP:4./
   exec="/root/swatchwr.sh $0",exec="/usr/bin/perl /home/foo/public_html/cgi-bin/atfw/recog.cgi"
/ipfw: 65515 Deny ICMP:3.4/
   exec="/root/swatchwr.sh $0",exec="/usr/bin/perl /home/foo/public_html/cgi-bin/atfw/recog.cgi"
/ipfw: 65515 Deny ICMP:3.13/
   exec="/root/swatchwr.sh $0",exec="/usr/bin/perl /home/foo/public_html/cgi-bin/atfw/recog.cgi"
/ipfw: 65515 Deny ICMP:5/
   exec="/root/swatchwr.sh $0",exec="/usr/bin/perl /home/foo/public_html/cgi-bin/atfw/recog.cgi"

 Firewallでは基本ポリシーを「全て許可」で構築している。本来は「全て拒否」から始めるべきであろう。しかし悩んだあげくのポリシーであるから後悔は無い。しかしこれによりルールセットは煩雑かつ膨大になる。
 意図しないアクセスの多くはICMPを検討する事で回避できるのでは無いかと想像している。複数のサーバーへのpingはその代表例だが、ICMP TYPE 11(MaskRequest)なども典型例であろう。TYPE 11は本来ネットワーク内部でのみ発生するべきである。外部からの要求に応えることは「ネットワークの大きさ」をさらしてしまうことになり非常に危険である。
 この要求には神経質になるべきと考える。
 基本的には外部からのICMPは反応しないポリシーであるため、これらの設定に関しては妥当であると判断する。


○/root/swatchwr.sh
#!/bin/sh
echo $* > /home/foo/public_html/cgi-bin/atfw/local/ipfw2.dat

 swatchが検知した行をファイルへ出力する。その後cronがファイルの更新を確認し、定義更新用スクリプトが動き出す。

○recog.cgi
 1.IPアドレスに対して24ビットのサブネットマスクをセットしてフィルター定義を追加する。
  (基本的にフィルター定義に含まれていないもののみswatchで検出される為、既存定義との比較は行わない)
  /sbin/ipfw -q add 65250 deny log all from foo.foo.foo.0/24 to any
 2.次回起動時にセットされるように累積ファイルへ定義を追加。
 3.検知IPなどのデータを整理し、ストック。
○起動時読み込み判別 (rc.localへ記述追加)
if [ -f /home/foo/public_html/cgi-bin/atfw/data/trigger.dat ]; then #トリガが存在した場合に
        sh /home/foo/public_html/cgi-bin/atfw/data/ipfw.dat #累積ファイルを実行
fi

●スクリプト抜粋
○転送されたルータログのIPが既存定義に含まれるかどうか判別
if ($rtflg ne "1"){
 open(R,$datafp);
  while(<R>){
  if ((index $_,"#\${fw",1) eq -1){
   if ((index $_,"add deny",10) ne -1){
    @rules = split(/\s+/, $_);
     if ($rules[5] eq "from"){
      $setip = $rules[6];
     } elsif ($rules[6] eq "from"){
      $setip = $rules[7];
     }
     if ($setip ne "any"){
      ($strip,$sbnet) = split(/\//,$setip);
      $chkdata = $_;
      &subnetctl;
      if ($resltflg eq "1") {
      $maxflg = "1";
     }
    }
   }
  }
 }
 close(R);
 if ($maxflg ne "1"){
  &dateset;
  $sendip = $geta . "\." . $getb . "\." . $getc . "\." . "0";
  $entcmd = "/sbin/ipfw -q add 65250 deny log all from ". $sendip . "/24 to any";
  $setcmd = $entcmd . " # $date from PC6\n";
  system($entcmd);
  &dtstock;
 }
}
○定義済み情報 (”210.228.170/28”など) から計算用の範囲を算出
sub subnetctl {
 if ($sbnet > 23){
  $sbnet = $sbnet - 16;
  $chkcl = "d";
 } elsif ($sbnet > 15){
  $sbnet = $sbnet - 8;
  $chkcl = "c";
 } else {
  $chkcl = "b";   ##サブネットマスク"7"以下は存在しない
 }
 ($neta,$netb,$netc,$netd) = split(/\./,$strip);
 ($seta,$setb,$setc,$setd) = split(/\./,$strip);
 $i = -1;
 foreach (@sbhnk){
  $i++ ;
  if ($_ eq $sbnet){
  $cnt = $iphnk[$i];
  next;
  }
 }
 if ($chkcl eq "b"){
  $netb = $netb + $cnt;
  $netc = 255;
  $netd = 255;
 } elsif ($chkcl eq "c"){
  $netc = $netc + $cnt;
  $netd = 255;
 } elsif ($chkcl eq "d"){
  $netd = $netd + $cnt;
 }
 @nete = ($neta,$netb,$netc,$netd);
 $endip = join("." ,@nete) ;
 $getset = 0;
 $strset = 0;
 $endset = 0;
 $getset = ($geta * 1000000000) + ($getb * 1000000) + ($getc * 1000) + $getd ;
 $strset = ($seta * 1000000000) + ($setb * 1000000) + ($setc * 1000) + $setd ;
 $endset = ($neta * 1000000000) + ($netb * 1000000) + ($netc * 1000) + $netd ;
 if (($getset >= $strset) && ($getset <= $endset)) {
  $resltflg = "1";
 } else {
  $resltflg = "0";
 }
}

●動作等確認用CGI
ルータ及びFirewallログから設定したポリシーに基づき、スクリプトが定義へ追加するべきと判断したルールセットを確認するCGI。  定義確認画像 
ルーターログの検知状況。Firewallサーバーへの転送実績も確認できる。検知したログはそのままストックする。  ルーターログ検知確認画像 

●PDA作業時からの変更点
 PC用編集Perlスクリプトについては、PDA用とPC用の更新用トリガファイルの存在を出力し、さらにPDA定義ファイルが存在すればその時点の更新作業へ含めるよう促し、更新時にPDA用のデータファイルとトリガファイルをリフレッシュしていた。
 ここへ自動処理で発生したトリガファイルとデータファイルの存在を表示し、これらをリフレッシュする部分を追加。


 以上、企画開始から約2週間で完成。


トップへ戻る

08/June/2002

30/Oct/2013 Updated.