Логирование VPN-подключений на Cisco ASA

в 19:30, , рубрики: asa, Cisco, ipsec, php, Серверное администрирование, Сетевые технологии, системное администрирование

Доброго времени суток, коллеги. В наличии есть Cisco ASA 5512 с настроенным сервером IPSEC туннелей к которому цепляются пользователи для доступа в корпоративную сеть. Поступила задача — выводить список активных пользователей в мониторинге, а также вести логирование кто, когда, с какого адреса и с каким профилем цеплялся.

Полазил по интернету, ничего подходящего для моей задачи не нашёл (может конечно плохо искал), и решил написать скрипт, который парсит вывод 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[$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]?dd?).){3}(25[0-5]|2[0-4]d|[01]?dd?)/",$value[7]) and preg_match("/((25[0-5]|2[0-4]d|[01]?dd?).){3}(25[0-5]|2[0-4]d|[01]?dd?)/",$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[$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) — ему проставляется время завершения сессии.

Готов к замечаниям/исправлениям/доработкам/вопросам.

Автор: NvAriec

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js