Статья давно не обновлялась, поэтому информация могла устареть.
Содержание |
Своя статистика BIND
Почему захотелось
Всегда интересовался DNS и, наконец, захотелось выяснить кто, когда, как и что спрашивает у DNS серверов о домене. Для этой цели был сделан отдельный VDS здесь, собственно поэтому статья размещается здесь.
Требуется
bind9. Как установить - этого полно в интернете. Например, для debian требуется всего одна команда "apt-get install bind9". Запуск\остановка /etc/init.d/bind9 start\stop.
Настройка
Главное, это собирать информацию о принятых запросах. Файл named.conf.
logging { channel query_log { file "/var/log/bind/bind.log"; severity dynamic; print-time yes; }; channel main { file "/var/log/bind/main.log"; severity dynamic; print-time yes; print-category yes; print-severity yes; }; category queries { query_log; }; category xfer-in { main; }; category xfer-out { main; }; category security { main; }; category resolver { main; }; category client { main; }; category unmatched { main; }; category default { main; }; category database { main; }; };
Выше я написал все, что собирает мой DNS сервер. Достаточно будет:
logging { channel query_log { file "/var/log/bind/bind.log"; severity dynamic; print-time yes; }; category queries { query_log; }; };
Сбор статистики
Подготовка
Я использую очень простой механизм хранения логов bind. Для этого создан отдельный пользователь (adduser), для удобного доступа по ftp (демон proftpd). Внутри домашней директории (например /home/net) расположены еще две /home/net/bind - сюда складываются логи DNS сервера, /home/net/stat - сюда будет помещатся интересующая меня статистика. Сбор логов происходит с помощью php скрипта, запускаемого с помощью cron. Статистика собирается за месяц.
Конфигурация cron
# m h dom mon dow command 0 */1 * * * /usr/bin/php /home/net/bind.php > /dev/null 2>&1 1 */1 * * * /usr/bin/php /home/net/stat1.php > /dev/null 2>&1 2 */1 * * * /usr/bin/php /home/net/stat2.php > /dev/null 2>&1 3 */1 * * * /usr/bin/php /home/net/stat3.php > /dev/null 2>&1 4 */1 * * * /usr/bin/php /home/net/stat4.php > /dev/null 2>&1
Первый скрипт, запускаемый каждый час делает остановку DNS сервера, копирование файла с логом и запуск DNS сервера. Интервал можете изменить по своему усмотрению. Мне все-равно необходимо перезапускать DNS сервер каждый час. Если нужно только обновить зоны, то надо "rndc reload". Остальный скрипты создают интересующую меня статистику. Запускаются последовательно, чтобы не нагружать сервер. Многие скрипты можно написать более оптимально, но я предлагаю только основу.
bind.php
<?php $cfg_src1 = "/var/log/bind/bind.log" ; $cfg_drop1 = "/home/net/bind/" ; $f = date('YmdHis') ; $d = date('Y.m') ; if ( !file_exists($cfg_drop1.$d) ) mkdir($cfg_drop1.$d) ; exec('/etc/init.d/bind9 stop') ; copy( $cfg_src1 , $cfg_drop1.$d.'/'.$f ) ; exec('rm '.$cfg_src1) ; exec('/etc/init.d/bind9 start') ; ?>
Статистика
1. Кто, что именно и сколько раз спрашивал
Файл с информацией вида IP адрес [tab] количество запросов. Пример:
xx.143.5.39 954 xxx.177.96.1 858 xx.111.180.223 378 xxx.92.168.54 373 xxx.122.226.27 247 xxx.23.19.2 204
Файл с информацией о том, какими записями сколько раз интересовались. Пример:
$$.ru 19137 $$$$.ru 2969 $$$$.ru 1523 $$$$.ru 1224 $$$$$$.ru 1119 $$$$$.ru 1055 $$$.ru 1047
Файл с более детальной информацией, указывающей какими именно типами записей интересовались. Пример:
A $$.$$.tj 39 AAAA $$$.$$$$.ru 36 AAAA $$$.$$$.ru 34 A $$$$.ru 32 A $$$.$$$$.ru 31 MX $$$.$$$$.ru 30 A $$$.$$$$$$$.com 30 A $$$$.ru 30 A6 $$$.$$$$.ru 29
PHP-скрипт для создания такой информации:
<?php $cfg_drop = "/home/net/bind/" ; $cfg_stat = "/home/net/stat/" ; //$cfg_drop = "D:/Web/bind/bind/" ; //$cfg_stat = "D:/Web/bind/stat/" ; $s_ip = array() ; // IP=>count $s_host = array() ; //host=>count $s_query = array() ; //host:RR=>count $yy = date('Y') ; $mm = date('m') ; $dd = date('d') ; $cdir = date('Y.m') ; if ( !file_exists($cfg_drop.$cdir) ) exit ; $R = scandir($cfg_drop.$cdir) ; foreach ( $R as $k=>$v ) { if ( strlen($v) < 7 ) continue ; echo 'Eval: '.$v."\r\n" ; $F = file($cfg_drop.$cdir.'/'.$v) ; foreach ( $F as $a=>$b ) { $ps = explode(' ',$b) ; $tmp = explode('#',$ps[3]) ; $ip = $tmp[0] ; $host = strtolower($ps[5]) ; $query = strtoupper($ps[7])."\t".$host ; if ( isset($s_ip[$ip]) ) $s_ip[$ip]++ ; else $s_ip[$ip] = 1 ; if ( isset($s_host[$host]) ) $s_host[$host]++ ; else $s_host[$host] = 1 ; if ( isset($s_query[$query]) ) $s_query[$query]++ ; else $s_query[$query] = 1 ; } } arsort( $s_ip ) ; arsort( $s_host ) ; arsort( $s_query ) ; $F = @fopen($cfg_stat.$cdir.'.x01.log','w+') ; if ( $F !== false ) { foreach ( $s_ip as $a=>$b ) fwrite( $F , $a."\t\t".$b."\n") ; fclose( $F ) ; } $F = @fopen($cfg_stat.$cdir.'.x02.log','w+') ; if ( $F !== false ) { foreach ( $s_host as $a=>$b ) fwrite( $F , $a."\t\t".$b."\n") ; fclose( $F ) ; } $F = @fopen($cfg_stat.$cdir.'.x03.log','w+') ; if ( $F !== false ) { foreach ( $s_query as $a=>$b ) fwrite( $F , $a."\t\t".$b."\n") ; fclose( $F ) ; } ?>
--mef 16:16, 26 декабря 2008 (MSK)
2. Распределение запросов по времени
Статистика по часам, такого вида:
00 689 3.25% 01 673 3.17% 02 660 3.11% ... 21 878 4.14% 22 816 3.85% 23 836 3.94%
По минутам (почти все тоже самое) Таблица распределения типов запросов от времени и их общее количество.
H\RR| A | MX | AAAA | SRV | ANY | SOA | A6 | TXT | AXFR | IXFR |CNAME | NS |TYPE99| PTR | ----|------|------|------|------|------|------|------|------|------|------|------|------|------|------| 00 | 276| 333| 40| 21| 5| 11| 2| 1| 0| 0| 0| 0| 0| 0| 01 | 246| 338| 44| 20| 4| 10| 4| 1| 0| 0| 0| 5| 1| 0| 02 | 278| 293| 46| 20| 10| 11| 2| 0| 0| 0| 0| 0| 0| 0| 03 | 274| 243| 23| 20| 22| 13| 0| 2| 0| 0| 0| 0| 0| 0| ... 23 | 310| 363| 54| 21| 68| 11| 0| 9| 0| 0| 0| 0| 0| 0| ----|------|------|------|------|------|------|------|------|------|------|------|------|------|------| Sum | 7853| 10664| 1124| 528| 559| 297| 71| 38| 17| 25| 10| 18| 1| 10| ----|------|------|------|------|------|------|------|------|------|------|------|------|------|------|
Скрипт создания такой информации
<?php $cfg_drop = "/home/net/bind/" ; $cfg_stat = "/home/net/stat/" ; //$cfg_drop = "D:/Web/bind/bind/" ; //$cfg_stat = "D:/Web/bind/stat/" ; function i2 ( $n ) { $n = (int)$n ; if ( $n > 9 ) return (string)$n ; else return '0'.$n ; } $s_hour = array() ; //hour->count for ( $i = 0 ; $i < 24 ; $i++ ) $s_hour[i2($i)] = 0 ; $s_min = array() ; //HH:MM->count for ( $h = 0 ; $h < 24 ; $h++ ) for ( $i = 0 ; $i < 60 ; $i++ ) $s_min[i2($h).':'.i2($i)] = 0 ; $s_rrs = array() ; //Only RR $s_hrr = array() ; //(HOUR:RR)->count ; $yy = date('Y') ; $mm = date('m') ; $dd = date('d') ; $cdir = date('Y.m') ; if ( !file_exists($cfg_drop.$cdir) ) exit ; $R = scandir($cfg_drop.$cdir) ; foreach ( $R as $k=>$v ) { if ( strlen($v) < 7 ) continue ; echo 'Eval: '.$v."\r\n" ; $F = file($cfg_drop.$cdir.'/'.$v) ; foreach ( $F as $a=>$b ) { $ps = explode(' ',$b) ; $A1 = substr($ps[1],0,2) ; $A2 = substr($ps[1],0,5) ; $s_hour[$A1]++ ; $s_min[$A2]++ ; $RR = strtoupper( $ps[7] ) ; if ( !isset($s_rrs[$RR]) ) $s_rrs[$RR]=1; else $s_rrs[$RR]++ ; if ( !isset($s_hrr[$A1.$RR])) $s_hrr[$A1.$RR]=1; else $s_hrr[$A1.$RR]++ ; } } $ALL = 0 ; for ( $i = 0 ; $i < 24 ; $i++ ) $ALL += $s_hour[i2($i)] ; if ( $ALL == 0 ) exit ; $F = @fopen($cfg_stat.$cdir.'.z01.log','w+') ; if ( $F !== false ) { foreach ( $s_hour as $a=>$b ) fwrite( $F , $a."\t".$b."\t".sprintf("%2.2f",100*$b/$ALL)."%\n") ; fclose( $F ) ; } arsort( $s_min ) ; $F = @fopen($cfg_stat.$cdir.'.z02.log','w+') ; if ( $F !== false ) { foreach ( $s_min as $a=>$b ) fwrite( $F , $a."\t".$b."\t".sprintf("%2.4f",100*$b/$ALL)."%\n") ; fclose( $F ) ; } $F = @fopen($cfg_stat.$cdir.'.z03.log','w+') ; if ( $F !== false ) { fwrite( $F , "H\RR|") ; foreach ( $s_rrs as $a=>$b ) fwrite( $F , str_pad($a,6," ",STR_PAD_BOTH)."|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "----|") ; foreach ( $s_rrs as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; for ( $i = 0 ; $i < 24 ; $i++ ) { fwrite( $F , ' '.i2($i).' |' ) ; foreach ( $s_rrs as $a=>$b ) { if ( isset($s_hrr[i2($i).$a])) $k = $s_hrr[i2($i).$a] ; else $k = 0 ; fwrite( $F , str_pad($k,6," ",STR_PAD_LEFT)."|" ) ; } fwrite( $F , "\n" ) ; } fwrite( $F , "----|") ; foreach ( $s_rrs as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "Sum |") ; foreach ( $s_rrs as $a=>$b ) fwrite( $F , str_pad($b,6," ",STR_PAD_LEFT)."|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "----|") ; foreach ( $s_rrs as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; fclose( $F ) ; } ?>
3. Детально, по домена второго уровня
Такую же табличку распределения запросов хочется сделать для каждого домена, обслуживаемого DNS сервером. Очевидно, что для многих она будет "скромнее", чем общая. Пример:
# $$$$.ru ----|------|------|------|------|------| H\RR| A| SRV| ANY| MX| AAAA| ----|------|------|------|------|------| 00 | 44| 21| | 13| | 01 | 25| 20| | 7| 1| 02 | 54| 20| | 35| | ... 20 | 36| 23| | 31| | 21 | 29| 20| 1| 54| | 22 | 35| 20| 2| 24| | 23 | 29| 21| 8| 18| | ----|------|------|------|------|------| | A| SRV| ANY| MX| AAAA| Sum | 895| 528| 27| 783| 3| ----|------|------|------|------|------|
Скрипт:
<?php $cfg_drop = "/home/net/bind/" ; $cfg_stat = "/home/net/stat/" ; //$cfg_drop = "D:/Web/bind/bind/" ; //$cfg_stat = "D:/Web/bind/stat/" ; function i2 ( $n ) { $n = (int)$n ; if ( $n > 9 ) return (string)$n ; else return '0'.$n ; } $s_2L = array() ; // Only 2L domains $s_hrr = array() ; // [2L]->HOUR:RR->count ; $s_rrs = array() ; // [2L]Only RR $yy = date('Y') ; $mm = date('m') ; $dd = date('d') ; $cdir = date('Y.m') ; if ( !file_exists($cfg_drop.$cdir) ) exit ; $R = scandir($cfg_drop.$cdir) ; foreach ( $R as $k=>$v ) { if ( strlen($v) < 7 ) continue ; echo 'Eval: '.$v."\r\n" ; $F = file($cfg_drop.$cdir.'/'.$v) ; foreach ( $F as $a=>$b ) { $ps = explode(' ',$b) ; $A1 = substr($ps[1],0,2) ; $DOM = explode( '.' , $ps[5] ) ; $DOMk = count ( $DOM ) ; if ( $DOMk < 2 ) continue ; $DOM = strtolower($DOM[$DOMk-2].'.'.$DOM[$DOMk-1]) ; if ( !isset($s_2L[$DOM] ) ) { $s_2L[$DOM] = 1 ; $s_hrr[$DOM] = array() ; $s_rrs[$DOM] = array() ; } else $s_2L[$DOM]++ ; $RR = strtoupper( $ps[7] ) ; if ( !isset($s_rrs[$DOM][$RR]) ) $s_rrs[$DOM][$RR]=1; else $s_rrs[$DOM][$RR]++ ; if ( !isset($s_hrr[$DOM][$A1.$RR])) $s_hrr[$DOM][$A1.$RR]=1; else $s_hrr[$DOM][$A1.$RR]++ ; } } if ( !file_exists($cfg_stat.$cdir ) ) @mkdir( $cfg_stat.$cdir ) ; if ( !file_exists($cfg_stat.$cdir ) ) exit ; foreach ( $s_2L as $aaa=>$bbb ) { $F = @fopen($cfg_stat.$cdir.'/'.$aaa.'.log','w+') ; if ( $F !== false ) { fwrite( $F , "# $aaa\n" ) ; fwrite( $F , "----|") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "H\RR|") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , str_pad($a,6," ",STR_PAD_LEFT)."|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "----|") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; for ( $i = 0 ; $i < 24 ; $i++ ) { fwrite( $F , ' '.i2($i).' |' ) ; foreach ( $s_rrs[$aaa] as $a=>$b ) { if ( isset($s_hrr[$aaa][i2($i).$a])) $k = $s_hrr[$aaa][i2($i).$a] ; else $k = ; fwrite( $F , str_pad($k,6," ",STR_PAD_LEFT)."|" ) ; } fwrite( $F , "\n" ) ; } fwrite( $F , "----|") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , " |") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , str_pad($a,6," ",STR_PAD_LEFT)."|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "Sum |") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , str_pad($b,6," ",STR_PAD_LEFT)."|" ) ; fwrite( $F , "\n" ) ; fwrite( $F , "----|") ; foreach ( $s_rrs[$aaa] as $a=>$b ) fwrite( $F , "------|" ) ; fwrite( $F , "\n" ) ; fclose( $F ) ; } } ?>