5335

Пишем скрипт для MikroTik об успешном входе на устройстводля.

Статья будет больше интересна специалистам использующим небольшой парк устройств (не использующим отдельный сервер, для системы мониторинга или логирования), домашним пользователям, тем кто в первый раз подступается к написанию скриптов устройства и тем у кого нет времени/желания разбираться.

Пишем скрипт об успешном входе на устройстводля MikroTik.
Пишем скрипт об успешном входе на устройстводля MikroTik.

Написать свой скрипт меня сподвигло желание упростить монструозные скрипты, которые можно найти по этому запросу в интернете, выполняющие это несложное действие (пример скрипта с Wiki MikroTik), а так же показать почему инженеры MikroTik сделали невозможным простой способ парсинга, если вы не житель Лондона.

Статья разбирает пример уведомления о входе и выходе пользователя с устройства MikroTik, но так же покажет примеры:

  1. Организация времени в журнале устройства;
  2. Парсинг журнала устройства, поиск событий по критериям;
  3. Отправка уведомлений на электронную почту;
  4. Отправка сообщения Telegram.

Предыстория. Почему скрипты парсинга логов MikroTik «монструозны»?

Под монструозностью будем понимать большой объем логики скрипта и конструкции вида:

:set tmpstring ([:pick [:tostr $tmpstring] 0 $findindex] . [:pick [:tostr $tmpstring] ($findindex + [:len [:tostr $ruleop]]) [:len [:tostr $tmpstring]]])

Они показывают умение администратора «оптимизировать» код, но здорово усложняют возможности понимания скриптов другими пользователями.

Но самую огромную роль в усложнение логики этого скрипта внесла сама компания MIkroTik, с интересной логикой журнала на устройстве. 🙂

Что может быть проще конструкции: «найди все события по времени старше последнего запуска с темой «account», запущенной простым казахстанцем (UTC+06)?

/log find where time > $LastRunTime topics ~ "account"

Это даже будет работать, ровно до 23:59:59 текущего дня. А после 12 ночи, скрипт превратится в тыкву А вот после 00:00:00 система начнет вываливать все события предыдущего дня. Почему?

Инженеры MikroTik большие оригиналы решившие сделать хранение записей журнала следующим образом: система хранит в журнале события сегодняшнего дня только с параметром времени, а чтобы не путаться, когда сменяется день, перезаписывает время событий добавляя дату, во все события «вчерашнего» дня. Для пользователя, в журнале событий все события отображаются дата/время, но сама система, событиям текущего дня присваивает только время.

Ну и где здесь оригинальность? А оригинальность в том, что MikroTik считает началом нового дня время 00:00:00 по UTC±0:00. Игнорируя часовой пояс самого устройства, т.е. у меня (UTC+06), до 6 утра, выдавались все уведомления за предыдущий день. В 06:00:00 Микротик перезаписывал всем событиям дату и скрипт снова начинал корректно работать.

Так что если вы не житель Лондона (UTC±0:00), для парсинга журнала устройства по времени вам приходилось использовать костыли, решая логикой скрипта проблему организации времени на устройстве.

Костыли делать мне не хотелось (в частности однажды это могут исправить), поэтому подумалось над вариантом который был бы проще в работе и проще в понимании другими пользователями.

Логика скрипта

Помимо параметров время события, текст события, MikroTik использует уникальный параметр id события, который мы будем использовать (.id уникален до перезапуска устройства, потом отчет начинается заново, с 0).

  • Обозначаем глобальную переменную ParseLogAccountEndArrayID — хранит последний проверенный .id сообщения;
  • Собираем в массив IDsEventsAccount все .id сообщений, в теме которых встречается «account» — (события: успешный вход на устройство, завершение сессии пользователя). Стандартное ограничение журнала лога 1000 строк, это не вызовет значимой нагрузки на устройство;
  • Получаем LenArrayIDs — количество элементов массива, StartArrayID — номер элемента с которого начнем перебор (это как раз ID последнего запуска), и EndArrayID — номер последнего элемента массива минус 1(массив начинается с элемента с индексом 0).
  • Если последний элемент .id массива (IDsEventsAccount) не равен последнему проверенному .id (ParseLogAccountEndArrayID) (т.е. появились новые события «account») и последний элемент (ParseLogAccountEndArrayID) — не пустой (первый запуск/в журнале нет событий авторизации) начинаем формировать и отправлять сообщения;
  • Если в журнале присутствуют необработанные события «account», начинаем перебор ключей в массиве (IDsEventsAccount) по их номерам, начиная с «последнего +1» (чтобы не отправлять вновь предыдущее последнее событие) до «последнего -1» (т.к. индекс начинается с 0);
  • Получаем .id сообщения (IDMessage) по его номеру в массиве;
  • Формируем текст email, записывая новой строкой сообщение журнала MikroTik;
  • Формируем текст Telegram сообщения, используя %0D%0A для переноса строки;
  • Записываем в ParseLogAccountEndArrayID последний ID сообщения с темой «account» (EndArrayID).
  • Отправляем сформированное сообщение на email;
  • Отправляем сформированное сообщение в Telegram;

Создать скрипт

Для запуска скрипта необходимы разрешения: read, write, test, policy.

[System] -> [Scripts] -> [+] -> [Name: ParseLogAccountEvents] -> [Policy: read, write, test, policy]

Код скрипта

:local DeviceName [/system identity get name];
:local Time [/system clock get time];
:local Date [/system clock get date];
:local EmailMessageText;
:local TelegramMessageText;

:global ParseLogAccountEndArrayID;

:local IDsEventsAccount [/log find where  topics ~ "account"];

:local LenArrayIDs [:len $IDsEventsAccount];
:local StartArrayID [:find $IDsEventsAccount $ParseLogAccountEndArrayID];
:local EndArrayID ($IDsEventsAccount -> ($LenArrayIDs-1));

:if ($EndArrayID != $ParseLogAccountEndArrayID and [:tobool $ParseLogAccountEndArrayID] ) do={

    :local StartArray [:find $IDsEventsAccount $ParseLogAccountLastRunID];

    :for KeyArray from=($StartArrayID+1) to=($LenArrayIDs-1) do={
        :local IDMessage ($IDsEventsAccount ->$KeyArray );
        :set EmailMessageText "$EmailMessageText \n\r  $[/log get number=$IDMessage time] - $[/log get number=$IDMessage message];";
        :set TelegramMessageText "$TelegramMessageText %0D%0A  $[/log get number=$IDMessage time] - $[/log get number=$IDMessage message];";
        }

    :set ParseLogAccountEndArrayID $EndArrayID;

    # START SEND EMAIL
    :local SendFrom "[email protected]";
    :local PasswordMail "yourpassword";
    :local SmtpServer [:resolve "smtp.mail.ru"];
    :local UserName "[email protected]";
    :local SmtpPort 465;
    :local UseTLS "tls-only";
    :local SendTo "[email protected]";
    :local Subject "\F0\9F\94\93 AUTH: $DeviceName [$Date $Time]";
    :local MessageText $EmailMessageText;
    /tool e-mail send to=$SendTo server=$SmtpServer port=$SmtpPort start-tls=$UseTLS user=$SendFrom password=$PasswordMail  from=$SendFrom subject=$Subject body=$MessageText;
    # END SEND EMAIL

    # START SEND TELEGRAM MESSAGE
    :local BotToken "YourBotID";
    :local ChatID "YourChatID";
    :local ParseMode "html";
    :local DisableWebPagePreview True;
    :local SendText "\F0\9F\94\93 <b>$DeviceName: AUTH</b> $TelegramMessageText";
    :local tgUrl "https://api.telegram.org/bot$BotToken/sendMessage?chat_id=$ChatID&text=$SendText&parse_mode=$ParseMode&disable_web_page_preview=$DisableWebPagePreview";
    /tool fetch http-method=get url=$tgUrl keep-result=no;
    # END SEND TELEGRAM MESSAGE

}

Добавление скрипта в Планировщик

Для запуска скрипта необходимы разрешения: read, write, test, policy.

[System] -> [Schedule] -> [+] -> [Name: ParseLogAccountEvents] —> [Interval: 00:05:00] -> [Policy: read, write, policy, test]

Или выполните в терминале:

/system scheduler add name=ParseLogAccountEvents policy=read,write,policy,test on-event="/system script run ParseLogAccountEvents" interval=5m

Заключение

Надеюсь пишем скрипт для MikroTik будет вам полезен, вы поймете как легко и просто парсить журнал устройства MikroTik выставляя триггеры по теме сообщения, или тексту сообщения.

Возможные темы сообщений в журнале устройства, можно увидеть попытавшись создать правило Logging:

[System] -> [Logging] -> [Rules] -> [+] -> [Topics]

Для парсинга текста сообщений используйте регулярные выражения и команду вида:

[/log find where message ~ «log»]

Установив более частое время проверки скрипта, вы можете выполнить дополнительные действия при входе/выходе пользователя, например автоматическое создание резервной копии (для тех кто любит править Firewall в пятницу вечером, забывая устанавливать MikroTik Safe Mode) или что еще подскажет воображение.

Мой скрипт выглядит проще, чем что я находил в интернете и доступен к оптимизации, если вы любите оптимизировать код в минимальное количество строк.

Работа скрипта проверена на: hAP ac lite, RouterOS 6.47.8 (stable).

P.S. Это моя первая статья на Хабре, можно судить строго, но справедливо. Статья не даст ничего нового специалистам использующим системы мониторинга или отдельные лог серверы. Но для новичков, домашних пользователей, администраторов с небольшим парком сетевых устройств — надеюсь будет полезна.

Возможно вам также будет интересно:

MikroTik — автоматически выключаем и включаем WiFi в заданное время

Настройка роутера MikroTik для работы с двумя провайдерами

источник