Перейти к основному содержанию

NXLog — сбор логов Postfix на Ubuntu

NXLog

Пришла задача организовать сбор логов на почтовом сервере Postfix, который развёрнут на Ubuntu Server. Заказчика интересуют логи отправки писем в виде JSON. Он хочет, чтобы почтовый сервер на каждую отправку письма отчитывался — передавал по curl данные из лога. Поможет нам в этом непростом деле NXLog.

NXLog — это высокопроизводительное многоплатформенное решение для управления логами.

https://nxlog.co

Решение существует как в бесплатном исполнении Community Edition, так и в платном — Enterprise Edition. Сравнение редакций:

https://nxlog.co/community-edition-vs-enterprise-edition

Бесплатная редакция полностью удовлетворяет всем требованиям нашей задачи:

  • получение данных из файла и отправка в файл: модуль Files (im_file, om_file)
  • преобразование данных в JSON: модуль JSON (xm_json)
  • получение и отправка данных по HTTP: модуль HTTP (im_http, om_http)

Функции NXLog

  • Непрерывный сбор событий операционной системы, брандмауэров, коммутаторов, маршрутизаторов, модемов, бизнес-систем и пр.
  • Настраиваемая пересылка событий на другие Syslog сервера.
  • Удаление, фильтрация выбранных или ненужных событий.
  • Поиск, сортировка и настраиваемый фильтр событий.

Установка NXLog

У нас почтовый сервер Postfix работает на ОС Ubuntu 16.04.

nxlog

Мануал по установке на Debian нам подойдёт:

https://docs.nxlog.co/userguide/deploy/debian.html

В списке можно найти нужную для нашей ОС версию дистрибутива. Однако, нам она не очень интересна, потому что не Community Edition.

nxlog

Переходим на страничку загрузки пакетов для CE:

https://nxlog.co/downloads/nxlog-ce#nxlog-community-edition

nxlog

Выбираем пакет для нашей операционной системы и скачиваем.

nxlog

No thanks, just start my download.

Загружаем пакет на сервер и пробуем установить:

dpkg -i nxlog-ce_3.1.2319_ubuntu16_amd64.deb

nxlog

И у нас ничего не устанавливается!

nxlog

Есть проблемы зависимостей. Удаляем пакет:

apt-get remove nxlog-ce

Смотрим в документацию и видим волшебную надпись:

If dpkg returned errors about uninstalled dependencies, use apt-get to install them and complete the NXLog installation.

Ну ладно, ставим зависимости:

apt-get install libapr1
apt-get -f install

Повторно устанавливаем пакет NXLog:

dpkg -i nxlog-ce_3.1.2319_ubuntu16_amd64.deb

nxlog

И у нас всё устанавливается!

Автоматически запускается служба nxlog:

service nxlog status

nxlog

Конфигурационный файл находится по адресу:

/etc/nxlog/nxlog.conf

nxlog

И там сейчас находится пример конфигурации.

nxlog

Данный пример нам не нужен, стираем всё, будем настраивать обработку логов Postfix.

Отправка логов Postfix в файл

Для начала просто убедимся в том, что nxlog работает. Для этого организуем запись логов в файл: /var/log/smtp.log. А данные будем брать из логов Postfix: /var/log/mail.log.

Мы будем преобразовывать данные в формат JSON, в конфиге будет модуль xm_json:

<Extension _json>
    Module  xm_json
</Extension>

Для получения данных из файла /var/log/mail.log используем модуль im_file:

<Input postfix>
    Module  im_file
    File    "/var/log/mail.log"
    <Exec>
        if $raw_event =~ /(?x)^(\S+\ +\d+\ \d+:\d+:\d+)\ (\S+)
                          \ postfix\/(\S+)\[(\d+)\]:\ (.+)$/
        {
            $EventTime = parsedate($1);
            $HostName = $2;
            $SourceName = "postfix";
            $Component = $3;
            $ProcessID = $4;
            $Message = $5;
            if $Component == "smtpd" and
               $Message =~ /(\w+): client=(\S+)\[([\d.]+)\]/
            {
                $QueueID = $1;
                $ClientHostname = $2;
                $ClientIP = $3;
            }
            if $Component == "cleanup" and
               $Message =~ /(\w+): message-id=(<\S+@\S+>)/
            {
                $QueueID = $1;
                $MessageID = $2;
            }
            if $Component == "qmgr" and
               $Message =~/(\w+): from=(<\S+@\S+>), size=(\d+), nrcpt=(\w+)/
            {
                $QueueID = $1;
                $Sender = $2;
                $Size = $3;
                $Nrcpt = $4;
            }
            if $Component == "smtp" and
               $Message =~ /(?x)(\w+):\ to=(<\S+@\S+>),\ relay=([\w.]+)\[([\d.]+)\],
                            \ delay=(\d+),\ status=(\w+)\ \((\d+)\ \w+:\ queued\ as
                            \ (\w+)\)/
            {
                $QueueID = $1;
                $Recipient = $2;
                $RelayHostname = $3;
                $RelayIP = $4;
                $Delay = $5;
                $Status = $6;
                $SMTPCode = $7;
                $QueueIDDelivered = $8;
            }
        }
    </Exec>
</Input>

Целиком взял этот блок из документации.

Для записи данных в файл /var/log/smtp.log используем модуль om_file:

<Output out>
    Module  om_file
    File    "/var/log/smtp.log"
    <Exec>
        if $Component != "smtp" drop();
        to_json();
    </Exec>
</Output>

В итоге получаем:

########################################
# Global directives                    #
########################################
User nxlog
Group nxlog

include /etc/nxlog/nxlog.d/*.conf
LogFile /var/log/nxlog/nxlog.log
LogLevel INFO

########################################
# Modules                              #
########################################

<Extension _json>
    Module  xm_json
</Extension>

<Input postfix>
    Module  im_file
    File    "/var/log/mail.log"
    <Exec>
        if $raw_event =~ /(?x)^(\S+\ +\d+\ \d+:\d+:\d+)\ (\S+)
                          \ postfix\/(\S+)\[(\d+)\]:\ (.+)$/
        {
            $EventTime = parsedate($1);
            $HostName = $2;
            $SourceName = "postfix";
            $Component = $3;
            $ProcessID = $4;
            $Message = $5;
            if $Component == "smtpd" and
               $Message =~ /(\w+): client=(\S+)\[([\d.]+)\]/
            {
                $QueueID = $1;
                $ClientHostname = $2;
                $ClientIP = $3;
            }
            if $Component == "cleanup" and
               $Message =~ /(\w+): message-id=(<\S+@\S+>)/
            {
                $QueueID = $1;
                $MessageID = $2;
            }
            if $Component == "qmgr" and
               $Message =~/(\w+): from=(<\S+@\S+>), size=(\d+), nrcpt=(\w+)/
            {
                $QueueID = $1;
                $Sender = $2;
                $Size = $3;
                $Nrcpt = $4;
            }
            if $Component == "smtp" and
               $Message =~ /(?x)(\w+):\ to=(<\S+@\S+>),\ relay=([\w.]+)\[([\d.]+)\],
                            \ delay=(\d+),\ status=(\w+)\ \((\d+)\ \w+:\ queued\ as
                            \ (\w+)\)/
            {
                $QueueID = $1;
                $Recipient = $2;
                $RelayHostname = $3;
                $RelayIP = $4;
                $Delay = $5;
                $Status = $6;
                $SMTPCode = $7;
                $QueueIDDelivered = $8;
            }
        }
    </Exec>
</Input>

<Output out>
    Module  om_file
    File    "/var/log/smtp.log"
    <Exec>
        if $Component != "smtp" drop();
        to_json();
    </Exec>
</Output>

########################################
# Routes                               #
########################################
<Route postfix>
    Path        postfix => out
</Route>

Перезапускаем службу и смотрим что у нас в логе NXLog.

service nxlog restart
LogFile /var/log/nxlog/nxlog.log

nxlog

А у нас не хватает прав на чтение файла /var/log/mail.log. Дадим права. Я пока тестирую, делаю совсем просто, добавляю пользователя nxlog в группу администраторов:

usermod -a -G adm nxlog

nxlog

Перезапускаем службу и смотрим что у нас в логе NXLog.

service nxlog restart

nxlog

Нет доступа к файлу /var/log/smtp.log. Да у нас просто нет такого файла, создадим.

cd /var/log
touch smtp.log
chown nxlog\: ./smtp.log

nxlog

Перезапускаем службу.

service nxlog restart

И у нас всё заработало.

nxlog

Логи Postfix считываются из файла /var/log/mail.log, преобразуются в JSON и записываются в файл /var/log/smtp.log.

Отправка логов Postfix по HTTP

Модифицируем конфигурацию для отправки логов по HTTP на адрес:

http://test.example.local/api/v1/stats/email
########################################
# Global directives                    #
########################################
User nxlog
Group nxlog

include /etc/nxlog/nxlog.d/*.conf
LogFile /var/log/nxlog/nxlog.log
LogLevel INFO

########################################
# Modules                              #
########################################

<Extension _json>
    Module  xm_json
</Extension>

<Input postfix>
    Module  im_file
    File    "/var/log/mail.log"
    <Exec>
        if $raw_event =~ /(?x)^(\S+\ +\d+\ \d+:\d+:\d+)\ (\S+)
                          \ postfix\/(\S+)\[(\d+)\]:\ (.+)$/
        {
            $EventTime = parsedate($1);
            $HostName = $2;
            $SourceName = "postfix";
            $Component = $3;
            $ProcessID = $4;
            $Message = $5;
            if $Component == "smtpd" and
               $Message =~ /(\w+): client=(\S+)\[([\d.]+)\]/
            {
                $QueueID = $1;
                $ClientHostname = $2;
                $ClientIP = $3;
            }
            if $Component == "cleanup" and
               $Message =~ /(\w+): message-id=(<\S+@\S+>)/
            {
                $QueueID = $1;
                $MessageID = $2;
            }
            if $Component == "qmgr" and
               $Message =~/(\w+): from=(<\S+@\S+>), size=(\d+), nrcpt=(\w+)/
            {
                $QueueID = $1;
                $Sender = $2;
                $Size = $3;
                $Nrcpt = $4;
            }
            if $Component == "smtp" and
               $Message =~ /(?x)(\w+):\ to=(<\S+@\S+>),\ relay=([\w.]+)\[([\d.]+)\],
                            \ delay=(\d+),\ status=(\w+)\ \((\d+)\ \w+:\ queued\ as
                            \ (\w+)\)/
            {
                $QueueID = $1;
                $Recipient = $2;
                $RelayHostname = $3;
                $RelayIP = $4;
                $Delay = $5;
                $Status = $6;
                $SMTPCode = $7;
                $QueueIDDelivered = $8;
            }
        }
    </Exec>
</Input>

<Output http>
    Module              om_http
    URL                 http://test.example.local/api/v1/stats/email
    ContentType         text/plain
    <Exec>
        if $Component != "smtp" drop();
        to_json();
    </Exec>
</Output>


########################################
# Routes                               #
########################################
<Route postfix>
    Path        postfix => http
</Route>

P.S.

Почему-то в документации странный конфиг и регулярное выражение. Переделал регулярку под рабочий вариант:

########################################
# Global directives                    #
########################################
User nxlog
Group nxlog

include /etc/nxlog/nxlog.d/*.conf
LogFile /var/log/nxlog/nxlog.log
LogLevel INFO

########################################
# Modules                              #
########################################

<Extension _json>
    Module  xm_json
</Extension>

<Input postfix>
    Module  im_file
    File    "/var/log/mail.log"
    <Exec>
        if $raw_event =~ /(?x)^(\S+\ +\d+\ \d+:\d+:\d+)\ (\S+)
                          \ postfix\/(\S+)\[(\d+)\]:\ (.+)$/
        {
            $eventTime = parsedate($1);
            $hostName = $2;
            $sourceName = "postfix";
            $component = $3;
            $processId = $4;
            $message = $5;
            if $component == "smtpd" and
               $message =~ /(\w+): client=(\S+)\[([\d.]+)\]/
            {
                $queueId = $1;
                $clientHostname = $2;
                $clientIp = $3;
            }
            if $component == "cleanup" and
               $message =~ /(\w+): message-id=(<\S+@\S+>)/
            {
                $queueId = $1;
                $messageId = $2;
            }
            if $component == "qmgr" and
               $message =~ /(\w+): from=(<\S+@\S+>), size=(\d+), nrcpt=(\w+)/
            {
                $queueId = $1;
                $sender = $2;
                $size = $3;
                $nrcpt = $4;
            }
            if $component == "smtp" and
               $message =~ /(?x)(\w+):\ [host]*\ ?([\w.\ -]*)?\[?[\d.]+\]?\ ?(\w*:?\ ?([\d.\ -]*)?\ (.+)?)/
            {
                $queueId = $1;
                $relayHostname = $2;
                $smtpCode = $4;
                if not defined($4)
                {
                    $smtpInfo = $3;
                } else {
                    $smtpInfo = $5;
                }
            }
            if $component == "smtp" and
               $message =~ /(?x)(\w+):\ to=<(\S+@\S+)>,\ relay=([\w.-]+)\[?([\d.]*)\]?:?(\d*),\ delay=([\d.]+),\ delays=[\d.]+\/[\d.]+\/[\d.]+\/[\d.]+,\ dsn=[\d.]+,\ status=(\w+)\ \(((host|connect)?\ ?[\w.\ -]*\[?[\d.]*\]?[\w\ ]*:\ ?)?([\d.\ -]*)\ (.+)\)/
            {
                $queueId = $1;
                $recipient = $2;
                $relayHostname = $3;
                $relayIp = $4;
                $relayPort = $5;
                $delay = $6;
                $status = $7;
                $smtpCode = $10;
                $smtpInfo = $11;
            }
        }
    </Exec>
</Input>

#<Output out>
#    Module  om_file
#    File    "/var/log/smtp.log"
#    <Exec>
#        if $component != "smtp" drop();
#        #if $relayHostname != "mail.example.com" drop();
#        to_json();
#    </Exec>
#</Output>

<Output http>
    Module              om_http
    URL                 http://test.example.local/api/v1/stats/email
    ContentType         text/plain
    <Exec>
        if $component != "smtp" drop();
        to_json();
    </Exec>
</Output>

########################################
# Routes                               #
########################################
<Route postfix>
    Path        postfix => http
</Route>

#<Route postfix>
#    Path       postfix => out
#</Route>

Теги

 

Похожие материалы

Keepalived для Postfix

Есть два сервера с операционной системой Ubuntu 20.04.4 LTS. На обоих серверах развёрнут Postfix сервер. Почтовики работают только на отправку писем, на них всё уже настроено. Пришла задача объединить их в один отказоустойчивый кластер Master — Slave с плавающим виртуальным IP-адресом с помощью keepalived. Главная задача: принимать почту на один IP адрес и обеспечить резервирование postfix.

Теги

Postfix с SMTP аутентификацией Cyrus

Понадобилось сделать Postfix с SMTP аутентификацией. Раньше не настраивал, перерыл кучу статей. Очень сумбурно всё изложено, поэтому пришлось написать свой вариант. Если кто-то знает хорошую инструкцию по данному вопросу без лишней воды — пишите в комментариях.

Теги