Доброго времени суток, коллеги. В наличии есть Cisco ASA 5512 с настроенным сервером IPSEC туннелей к которому цепляются пользователи для доступа в корпоративную сеть. Поступила задача — выводить список активных пользователей в мониторинге, а также вести логирование кто, когда, с какого адреса и с каким профилем цеплялся.
Полазил по интернету, ничего подходящего для моей задачи не нашёл (может конечно плохо искал), и решил написать скрипт, который парсит вывод SNMP и складывает в таблицу.
Структура таблицы MySQL:
Сам скрипт обработки:
Логика работы:
Скрипт запускается каждый 30 секунд через crone и опрашивает по SNMP оборудование. Так как логинов пользователей в открытом виде Cisco не хранит, то необходимо вытащить логины из динамически формируемого SNMP OID. Каждый символ логин хранится с помощью ASCII кода в части SNMP OID (это почерпнул отсюда).
После отработки функции get_user() переменная $x принимает значение в следующем формате:
Описание значений можно посмотреть тут.
Дальше скрипт проверяет — есть ли логин в таблице Logins. Если такого логина нет — добавляет его туда, если есть — получает его id_l. Затем, смотрит — есть ли у этого логина открытые сессии с неустановленным датой окончания. Если нет — создаёт в таблице sessions новую запись.
Затем скрипт получает из базы список пользователей, у которых есть незавершённые сессии. И проверяет наличие логинов в опросе. Если логина нет в опросе или номер сессии не совпадает с той, которая в базе (s_index) — ему проставляется время завершения сессии.
Готов к замечаниям/исправлениям/доработкам/вопросам.
UPD: добавил strtolower() в функциях парсинга логина и в проверке существования логина в активных сессиях (почти в конце уже). Иначе, если логин содержит заглавные буквы, тогда некорректно работает всё.
Полазил по интернету, ничего подходящего для моей задачи не нашёл (может конечно плохо искал), и решил написать скрипт, который парсит вывод SNMP и складывает в таблицу.
Структура таблицы MySQL:
CREATE DATABASE `vpn_log` /*!40100 DEFAULT CHARACTER SET latin1 */; CREATE TABLE `logins` ( `id_l` int(10) unsigned NOT NULL AUTO_INCREMENT, `login` varchar(90) NOT NULL, `id_s` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`id_l`), UNIQUE KEY `id_l_UNIQUE` (`id_l`), UNIQUE KEY `login_UNIQUE` (`login`) ) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=latin1; CREATE TABLE `sessions` ( `id_s` int(10) unsigned NOT NULL AUTO_INCREMENT, `id_l` int(10) unsigned NOT NULL, `time_start` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `time_end` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `ip_source` varchar(45) NOT NULL, `ip_lan` varchar(45) NOT NULL, `s_index` int(10) unsigned NOT NULL, `profile` varchar(255) NOT NULL, PRIMARY KEY (`id_s`), UNIQUE KEY `id_s_UNIQUE` (`id_s`) ) ENGINE=InnoDB AUTO_INCREMENT=2923 DEFAULT CHARSET=latin1;
Сам скрипт обработки:
#!/usr/bin/php <?php ##Database settings $settings['sql_host']="localhost"; $settings['sql_db']="vpn_log"; $settings['sql_user']="vpn_log"; $settings['sql_password']="vpn_log"; #Подключение к базе# function connectdb(){ global $settings; $dbconn = mysqli_connect( $settings['sql_host'], $settings['sql_user'], $settings['sql_password'], $settings['sql_db'] ) or die('Could not connect: ' . mysqli_connect_errno()); return $dbconn; } #Получение списка пользователей и параметров IPSEC сессий# function get_users(){ $ret = snmp3_real_walk( '10.10.10.10', #IP адрес Cisco ASA 'snmpuser', #SNMP авторизация 'authNoPriv', # 'MD5', # 'authpassword', # '', # '', # '1.3.6.1.4.1.9.9.392.1.3.21' # ветка OID в которой расположены пользователи ); $result = []; $user = []; foreach ($ret as $oid=>$value){ $re = '/(SNMPv2-SMI::enterprises\.9\.9\.392\.1\.3\.21\.1\.[0-9]{1,2}\.[0-9]{1,2}\.)([\.0-9]*)\.([0-9]{4,10})/'; # регулярное выражение, которое парсит вывод и выцепляет логины пользователей $str = $oid; preg_match_all($re, $str, $matches); $oid = explode(".",$matches[2][0]); $value = explode(": ",$value); $login = ""; foreach ($oid as $chr){ $login.=chr($chr); }; $result[strtolower($login)][$matches[3][0]][] = @str_ireplace("\"","",$value[1]); }; return $result; #на выходе получаем массив из объектов вида [login][s_index][value] }; $x = get_users(); $connect = connectdb(); foreach ($x as $user=>$sessions){ $user = addslashes($user); foreach ($sessions as $session=>$value ){ if (preg_match("/((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)/",$value[7]) and preg_match("/((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)/",$value[5])){ $sql = "SELECT * from logins where login = '$user'"; $result = mysqli_query($connect, $sql); $res = mysqli_fetch_array($result); $time = date("Y-m-d H:i:s",time()-$value[3]); $id_l = $res['id_l']; if (mysqli_num_rows($result)==0){ $sql_1 = "INSERT INTO logins value ('','".$user."','')"; $result_1 = mysqli_query($connect, $sql_1); $id_l = mysqli_insert_id($connect); $sql_1 = "INSERT INTO sessions (id_l,time_start,ip_source,ip_lan,s_index, profile)value ('$id_l','$time','$value[7]','$value[5]','$session', '$value[0]')"; $result_1 = mysqli_query($connect, $sql_1); $id_s = mysqli_insert_id($connect); } else{ $sql_1 = "SELECT * from sessions where (id_l = (select id_l from logins where login = '$user'))and(s_index = '$session')and(time_end = '0')"; $result_1 = mysqli_query($connect, $sql_1); if (mysqli_num_rows($result_1)==0){ $sql_2 = "INSERT INTO sessions (id_l,time_start,ip_source,ip_lan,s_index, profile)value ('$id_l','$time','$value[7]','$value[5]','$session', '$value[0]')"; $result_2 = mysqli_query($connect, $sql_2); } } } } } $sql = "SELECT l.login,s.s_index FROM logins as l left join sessions as s on l.id_l=s.id_l where (s.time_end=0)"; $rw = mysqli_query($connect,$sql); $result = mysqli_fetch_array($rw); while ($result['s_index']>0){ if (@!$x[strtolower($result['login'])][$result['s_index']][0]){ $sql_1 = "UPDATE sessions SET time_end = '".date("Y-m-d H:i:s",time())."' where s_index='".$result['s_index']."'"; mysqli_query($connect, $sql_1); }; $result = mysqli_fetch_array($rw); }; mysqli_close($connect); ?>
Логика работы:
Скрипт запускается каждый 30 секунд через crone и опрашивает по SNMP оборудование. Так как логинов пользователей в открытом виде Cisco не хранит, то необходимо вытащить логины из динамически формируемого SNMP OID. Каждый символ логин хранится с помощью ASCII кода в части SNMP OID (это почерпнул отсюда).
После отработки функции get_user() переменная $x принимает значение в следующем формате:
[login] => Array ( [s_index] => Array ( [0] => profile_name ............................. [34] => 0 ) )
Описание значений можно посмотреть тут.
Дальше скрипт проверяет — есть ли логин в таблице Logins. Если такого логина нет — добавляет его туда, если есть — получает его id_l. Затем, смотрит — есть ли у этого логина открытые сессии с неустановленным датой окончания. Если нет — создаёт в таблице sessions новую запись.
Затем скрипт получает из базы список пользователей, у которых есть незавершённые сессии. И проверяет наличие логинов в опросе. Если логина нет в опросе или номер сессии не совпадает с той, которая в базе (s_index) — ему проставляется время завершения сессии.
Готов к замечаниям/исправлениям/доработкам/вопросам.
UPD: добавил strtolower() в функциях парсинга логина и в проверке существования логина в активных сессиях (почти в конце уже). Иначе, если логин содержит заглавные буквы, тогда некорректно работает всё.
