Статья будет больше интересна специалистам использующим небольшой парк устройств (не использующим отдельный сервер, для системы мониторинга или логирования), домашним пользователям, тем кто в первый раз подступается к написанию скриптов устройства и тем у кого нет времени/желания разбираться.
Написать свой скрипт меня сподвигло желание упростить монструозные скрипты, которые можно найти по этому запросу в интернете, выполняющие это несложное действие (пример скрипта с Wiki MikroTik), а так же показать почему инженеры MikroTik сделали невозможным простой способ парсинга, если вы не житель Лондона. :)
Статья разбирает пример уведомления о входе и выходе пользователя с устройства MikroTik, но так же покажет примеры:
Организация времени в журнале устройства;
Парсинг журнала устройства, поиск событий по критериям;
Отправка уведомлений на электронную почту;
Отправка сообщения 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 для переноса строки;
Отправляем сформированное сообщение на email;
Отправляем сформированное сообщение в Telegram;
Записываем в ParseLogAccountEndArrayID последний ID сообщения с темой "account" (EndArrayID).
Создать скрипт
Для запуска скрипта необходимы разрешения: 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];";
}
# START SEND EMAIL
:local SendFrom "ToMail@mail.ru";
:local PasswordMail "yourpassword";
:local SmtpServer [:resolve "smtp.mail.ru"];
:local UserName "FromMail@mail.ru";
:local SmtpPort 465;
:local UseTLS "tls-only";
:local SendTo "ToMail@mail.ru";
: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
}
:set ParseLogAccountEndArrayID $EndArrayID;
Добавление скрипта в Планировщик
Для запуска скрипта необходимы разрешения: 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 выставляя триггеры по теме сообщения, или тексту сообщения.
Возможные темы сообщений в журнале устройства, можно увидеть попытавшись создать правило Logging:
[System] -> [Logging] -> [Rules] -> [+] -> [Topics]
Для парсинга текста сообщений используйте регулярные выражения и команду вида:
[/log find where message ~ "log"]
Установив более частое время проверки скрипта, вы можете выполнить дополнительные действия при входе/выходе пользователя, например автоматическое создание резервной копии (для тех кто любит править Firewall в пятницу вечером, забывая устанавливать MikroTik Safe Mode) или что еще подскажет воображение.
Мой скрипт выглядит проще, чем что я находил в интернете и доступен к оптимизации, если вы любите оптимизировать код в минимальное количество строк.
Если вы используете множество скриптов на вашем устройстве, указывать параметры почты и Telegram бота, в каждом из скриптов нерационально, особенно если возникнет необходимость изменить параметры. Я использую в своих скриптах вызов скриптов функций: "Отправить Email" и "Отправить сообщение Telegram", возможно и Вам это тоже будет полезно, упрощая управление устройством MikroTik.
Если Вам стали интересны и другие мои скрипты для MikroTik, их можно увидеть - здесь.
Работа скрипта проверена на: hAP ac lite, RouterOS 6.47.8 (stable).
P.S. Это моя первая статья на Хабре, можно судить строго, но справедливо. Статья не даст ничего нового специалистам использующим системы мониторинга или отдельные лог серверы. Но для новичков, домашних пользователей, администраторов с небольшим парком сетевых устройств - надеюсь будет полезна.
UPD 11.12.2020: Выставляйте права на запуск скрипта в Scheduler и на сам скрипт, как указано в статье: read, write, test, policy. Излишние права (выставляются по умолчанию новому скрипту) могут привести к появлению ошибки "could not run script ParseLogAccountEvents: not enough permissions". Проверяйте журнал устройства.
le1ic
> если вы не житель Лондона (UTC±0:00)
В Лондоне есть летнее время )
YunSergey Автор
Я не житель Лондона, не знал такой подробности. :)