phpでiptablesを設定するスクリプトを書いて、だいぶ安定してきたようなので。
基本的にはJPNICが逆引き権限を持っているブロックと、APNICがJPにアサインしているブロックについてはFROMJPというChainに追加して許可して、FROMJPをJPFILTERというChainに追加して基本的にドロップするというiptablesコマンドを生成します。ただし、上記ブロックに登録されていないJPなブロックやgooglebotのブロックもあるので、それは適宜調整するという運用になります。要するに日本以外からのアクセスを禁止するということです。
とりあえず/var/log/messagesからドロップされたパケットを抜きだすスクリプトで見る限り、mxやらthやらcnからのお行儀の悪いパケットがドロップされているようなので、よしとするか。
英語でブログを書き始めたら、この対策は出来ないねぇ…。
phpでiptablesを設定するスクリプト
#!/usr/bin/php -q
<?php
/** @file
* @brief 日本以外のIPアドレスからのアクセスを禁止するiptablesスクリプトを作成する
* @author rio fujita
* @date 2005.04.24
* @version 1.0.0
* @note IPv4のみ対応、iptablesが動作している事
*/
//ファイルの場所
define("APNIC_URL", "http://ftp.apnic.net/stats/apnic/");
define("DLGT_LIST", "delegated-apnic-latest");
//define("JPNIC_URL", "http://www.nic.ad.jp/ja/dns/jp-addr-block.html");
//ファイル変更チェック
define("MD5_CACHE", DLGT_LIST."_md5_cache");
define("MD5_LEN", 32+1);
//ファイルのパーサ
define("REC_REG", "/apnic\|JP\|ipv4\|([0-9\.]+)\|([0-9]+)\|[0-9]{8}\|[^\n]+/");
//スクリプトファイル名
define("IPTABLE", "iptables.sh");
//ロギングするかどうか
define("LOGGING", true);
function ipv4_2_cidr($start, $end){
global $logs;
return $start."/".$logs[ip2long($end) - ip2long($start) + 1];
}
//CIDRマスクのテーブル作成
for($i = 0; $i < 32; $i++){
$logs[pow(2, $i)] = 32 - $i;
}
//JPNICが逆引き権限を持つIPアドレス
/*
$list = file(JPNIC_URL, "r");
mb_convert_variables("UTF-8", "JIS", $list);
foreach($list as $line){
$l = trim($line);
if(preg_match("/<table summary=\"list\">/", $l)){
$fl = true;
} else {
if($fl){
if(preg_match("/<td> *([0-9\.]+) *<\/td>/", $l, $c)){
if($start){
$jpnic[] = ipv4_2_cidr($start, $c[1]);
unset($start);
} else {
$start = $c[1];
}
unset($c);
}
}
}
}
foreach($jpnic as $ip){
echo($ip."\n");
}
exit();
*/
//許可するIPアドレス群
$allows = array(
"127.0.0.1/32", //localhost
"10.0.0.0/8", //local IP
"172.16.0.0/12",
"192.168.0.0/16",
"61.112.0.0/12", //JPNIC
"61.192.0.0/13",
"61.200.0.0/12",
"133.0.0.0/8",
"192.50.0.0/16",
"192.218.0.0/16",
"192.244.0.0/16",
"202.11.0.0/16",
"202.13.0.0/16",
"202.15.0.0/15",
"202.17.0.0/15",
"202.19.0.0/16",
"202.23.0.0/14",
"202.32.0.0/14",
"202.48.0.0/16",
"202.208.0.0/11",
"202.240.0.0/12",
"203.136.0.0/14",
"203.140.0.0/15",
"203.178.0.0/14",
"203.182.0.0/15",
"210.128.0.0/11",
"210.160.0.0/12",
"210.188.0.0/14",
"210.196.0.0/14",
"210.224.0.0/12",
"210.248.0.0/13",
"211.0.0.0/13",
"211.8.0.0/13",
"211.16.0.0/14",
"211.120.0.0/12",
"218.40.0.0/13",
"218.110.0.0/16",
"218.216.0.0/12",
"219.96.0.0/11",
"219.160.0.0/13",
"220.96.0.0/14",
"220.104.0.0/13",
"220.144.0.0/15",
"220.208.0.0/12",
"221.112.0.0/13",
"43.244.0.0/16", //FreeBit
"58.0.0.0/8", //USEN ?
"58.70.0.0/16", //K-opticom
"66.187.224.0/20", //RedHat
"66.249.64.0/19", //googlebot
"66.196.64.0/18", //inktomi search
"68.142.192.0/18", //inktomi search
"202.165.96.0/20", //inktomi search
"207.46.0.0/16", //msn bot
);
//ファイルを取得してMD5を生成
if(!$list = file(APNIC_URL.DLGT_LIST, "r")){
exit();
}
$list_imploded = trim(implode("", $list));
$md5_current = md5($list_imploded);
//キャッシュされたMD5を読む
if(file_exists(MD5_CACHE)){
if($fp = fopen(MD5_CACHE, "r")){
$md5_cached = fgets($fp, MD5_LEN);
fclose($fp);
}
}
//違っていたらキャッシュに書き出して処理続行
if(!array_key_exists("f", getopt("f"))){
if($md5_current == $md5_cached){
exit("cached\n");
}
}
if($fp = fopen(MD5_CACHE, "w")){
fwrite($fp, $md5_current);
fclose($fp);
}
$sh_buf .= "iptables -F\n"; //フラッシュする
//chainを削除
$sh_buf .= "iptables -X JPFILTER\n";
$sh_buf .= "iptables -X FROMJP\n";
//chainを作る
$sh_buf .= "iptables -N JPFILTER\n";
$sh_buf .= "iptables -N FROMJP\n";
//アクセスを許可するIP群
foreach($allows as $al){
$sh_buf .= "iptables -A JPFILTER -s ".$al." -j FROMJP\n";
}
//リストのパース
foreach($list as $line){
if(preg_match(REC_REG, $line, $c)){
//許可するリストに追加
$range = $c[1]."/".$logs[(int)$c[2]];
$sh_buf .= "iptables -A JPFILTER -s ".$range." -j FROMJP\n";
}
}
$sh_buf .= "iptables -A FROMJP -j ACCEPT\n"; //日本国内からのアクセス許可
if(LOGGING){
$sh_buf .= "iptables -A JPFILTER -j LOG --log-prefix \"Rej-TCP : \"\n"; //それ以外はログとって...
}
$sh_buf .= "iptables -A JPFILTER -j DROP\n"; //落とす
$sh_buf .= "iptables -A INPUT -p tcp -m state --state NEW -j JPFILTER\n"; //INPUT chainにJPFILTERを追加
//ファイルを作成
if($fp = fopen(IPTABLE, "w")){
fwrite($fp, "# this file was made by make_iptables.php\n\n");
fwrite($fp, $sh_buf);
fclose($fp);
//実行
exec("sh ".IPTABLE);
}
?>
/var/log/messagesからドロップされたパケットを抜きだすスクリプト
#!/usr/bin/php -q
<?php
exec("grep Rej-TCP /var/log/messages | cut -d ' ' -f 11 | sort -u | cut -d '=' -f 2", $file);
foreach($file as $line){
$ip = trim($line);
$names[$ip] = gethostbyaddr($ip);
}
ksort($names);
foreach($names as $ip=>$name){
echo($ip.str_repeat(" ", 15-strlen($ip)+1).": ".$name."\n");
}
?>