Отправка логов с OpenWRT/LEDE в syslog и обработка событий
Вдогонку к статье о syslog-ng решил сделать дополнение о том, как завернуть логи с OpenWRT и настроить реакцию на соответствие какому-нибудь фильтру. Дома у меня есть два Xiaomi MiWifi 3G (оказалось крайне доступным и достойным по характеристикам устройством), три штуки Netgear WNR3500L, которые в текущий момент работают в качестве гигабитных свичей в разных частях квартиры и Nexx 3020 для экспериментов. Одним словом, правило для сохранения логов должно быть общее для всех этих устройств, чтобы не писать шесть отдельных конфигурационных файлов. Начать я решил со своего основного Xiaomi роутера с хостнеймом gw01, на котором стоит OpenWRT 18.06.
Настройка роутера
Есть два пути настройки - через веб интерфейс и через консоль. Первый прост как угол дома. Авторизуемся на роутере, переходим в раздел System -> System и прописываем путь к нашему syslog серверу во вкладке Logging. В принципе, этого достаточно, но тут возникает одна проблема - нам необходимо некое ключевое слово, по которому мы будем фильтровать логи, приходящие именно с OpenWRT таким же образом, как было сделано ранее для докер контейнеров с опцией логирования tag, но в веб интерфейсе такой опции нет, поэтому придётся настраивать через консоль, благо, это не намного сложнее. Заходим на роутер по ssh и редактируем конфигурационный файл system:
vim /etc/config/system
В раздел system нам нужно добавить следующие параметры:
option log_proto 'udp' option log_ip '10.11.11.4' option log_prefix 'OpenWRT-Routers' option conloglevel '7' option cronloglevel '7'
Если вы не сильны в vi/vim (выход c записью через Esc, затем ввод :wq), есть и другой способ:
uci set system.@system[0].log_proto='udp' uci set system.@system[0].log_ip='10.11.11.4' uci set system.@system[0].log_prefix='OpenWRT-Routers' uci set system.@system[0].conloglevel='7' uci set system.@system[0].cronloglevel='7'
После изменения настроек, чтобы они вступили в силу, нужно выполнить
/etc/init.d/log restart
В официальной документации так же было написано о необходимости затем выплонить
/etc/init.d/system restart
Но у меня прекрасно заработало и без этого. Для сохранения настроек во флешпамять, выполняем
uci commit
В описанных выше настройках log_prefix как раз отвечает за то, что будет приходить на наш syslog в поле PROGRAM, что очень удобно для фильтрации. 10.11.11.4 - это адрес моего syslog-ng сервера, у вас он скорее всего будет другим. conloglevel и cronloglevel - это уровни фильтрации для ядра и тех процессов, которые запускаются из cron. Самый низкий - нулевой, самый высокий - восьмой.
Настройка syslog-ng
Теперь можно заглянуть в логи контейнера syslog-ng. Мы должны увидеть примерно такую запись:
[2019-01-05T10:04:20.894114] Incoming log entry; line='Jan 5 15:04:20 gw01 OpenWRT-Routers: logread[32051]: Logread connected to 10.11.11.4:514' [2019-01-05T10:04:20.894234] Setting value; msg='0x55e2eb847c00', name='HOST_FROM', value='gw01.lan' [2019-01-05T10:04:20.894270] Setting value; msg='0x55e2eb847c00', name='SOURCE', value='src_net'
Как мы видим, logd на роутере успешно подключился к нашему syslog-ng. Теперь напишем конфигурационный файл. Для удобства Будем складывать логи в директорию logs/network/%hostname%.
filter f_openwrt { match("OpenWRT-Routers" value("PROGRAM")); }; destination dst_openwrt { file( "/logs/network/${HOST}/$YEAR-$MONTH-$DAY.log" template("$ISODATE $LEVEL $MSG\n") ); }; log { source(src_net); filter(f_openwrt); destination(dst_openwrt); };
И перезагрузим специально предназначенным для этого скриптом
scripts/reload
В консоли роутера можем проверить отправку логов при помощи команды
logger test123
Можем убедиться, что всё работает.
$ tail -n 1 logs/network/gw01/2019-01-05.log 2019-01-05T20:56:52+00:00 notice root: test123
Реакция на события
А теперь сделаем кое-что поинтереснее - заставим syslog-ng реагировать на какой-нибудь скрипт. Скрипт в свою очередь должен постоянно слушать stdin, иначе вас завалит сообщениями вида
syslog-ng[2673]: POLLERR occurred while idle; fd='12'
В качестве примера будем дёргать http запросы в момент, когда в логах приходит нужная запись. К url будем добавлять последнее слово из входящей записи. Содержимое scripts/testscripts:
#!/bin/sh
while read line; do
echo "$line" | sed 's#^.* #wget -q -O - http://10.11.11.224:8000/#' | sh
done < /dev/stdin
На хосте 10.11.11.224:8000 я подниму тестовый веб сервер. И пусть он отдаёт 404-ю ошибку, но в логах мы будем видеть обращения - это именно то, что нам нужно.
$ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ...
Далее нам нужен конфигурационный файл для syslog-ng:
filter f_openwrttest { match("test123" value("MESSAGE")) and match("OpenWRT-Routers" value("PROGRAM")); }; destination dst_openwrttest { program("/scripts/testscript"); }; log { source(src_net); filter(f_openwrttest); destination(dst_openwrttest); };
Теперь возвращаемся к консоли любого из роутеров и выполняем:
$ logger test12345 $ logger test123456
И в логах веб сервера видим следующее:
[06/Jan/2019 00:48:04] code 404, message File not found 10.11.11.4 - - [06/Jan/2019 00:48:04] "GET /test12345 HTTP/1.1" 404 - 10.11.11.4 - - [06/Jan/2019 00:48:06] code 404, message File not found 10.11.11.4 - - [06/Jan/2019 00:48:06] "GET /test123456 HTTP/1.1" 404 -
Таким образом мы можем вызывать внешние апи. Например, я могу по наступлению какого-нибудь события, (скажем, разрыва VPN соединения) отправить на ближайшую свою управляемую розетку мелодию главной темы из "Семейства Аддамс" в формате RTTTL, или отправить событие в систему мониторинга, но об этом я напишу как-нибудь в другой раз.
Ещё я только что добавил к образу две полезных фичи - сжатие и очистку старых логов. Так как ротацией syslog-ng замечательно занимается сам, то очистка и компрессия никакой сложности для реализации не представляют. Я сделал следующим образом - entrypoint при старте запускает в фоновом режиме скрипт scripts/cleaner, который в свою очередь ищет в директории с логами файлы с именем .clean со следующим содержимым:
archive=7 clean=60
Для данного примера все файлы (кроме самого файла .clean конечно) старше 60-ти дней будут удалены в той же директории и всех поддиректориях. Оставшиеся файлы (кроме того же .clean и файлов с расширением bz2 будут сжаты с помощью bzip2. Скрипт по умолчанию запускается каждые 2 часа.
Репозиторий лежит всё там же: https://github.com/alive-corpse/es-syslog-ng