# Vector: перетрансляція логів у Graylog (Debian) Інструкція з встановлення та налаштування агента [Vector](https://vector.dev) для збору логів з окремих файлів (сервіси на кшталт `accel-ppp`, які не пишуть у syslog, а тільки у власний лог-файл) і пересилання їх на централізований Graylog по UDP. Використовується там, де сервіс не вміє писати напряму в syslog, або де потрібен більш гнучкий контроль над форматом і тегуванням повідомлень, ніж дає стандартний `rsyslog`. ## Зміст - [Огляд архітектури](#огляд-архітектури) - [Передумови](#передумови) - [Крок 1. Встановлення Vector](#крок-1-встановлення-vector) - [Крок 2. Базова конфігурація (приклад: accel-ppp)](#крок-2-базова-конфігурація-приклад-accel-ppp) - [Крок 2b. (Опційно) Фільтрація DHCP-шуму](#крок-2b-опційно-фільтрація-dhcp-шуму) - [Крок 3. Права доступу (запуск від root)](#крок-3-права-доступу-запуск-від-root) - [Крок 4. Запуск та перевірка роботи](#крок-4-запуск-та-перевірка-роботи) - [Крок 5. Додавання інших джерел логів](#крок-5-додавання-інших-джерел-логів) - [Крок 6. Прибирання старих правил rsyslog](#крок-6-прибирання-старих-правил-rsyslog) - [Діагностика проблем](#діагностика-проблем) - [Довідка: структура конфігурації](#довідка-структура-конфігурації) ## Огляд архітектури ``` /var/log/accel-ppp/accel-ppp.log ──┐ /var/log/freeradius/radius.log ────┼──▶ Vector ──▶ UDP:5140 ──▶ Graylog /var/log/<інший файл>.log ──────────┘ (read + tag + forward) (Servers Syslog input) ``` Кожен лог-файл — це окреме **джерело** (`source`) у Vector. Кожне джерело проходить через своє **перетворення** (`transform`), яке додає тег і метадані. Усі перетворення збираються в одне **призначення** (`sink`), яке одним UDP-потоком шле все на Graylog. Додавання нового файлу логів = один новий `source` + один новий `transform` + один рядок у списку `inputs` існуючого sink — сам sink і адреса Graylog не міняються. ## Передумови - [ ] Операційна система: Debian Linux (11/12) - [ ] Сервіс, що пише лог у файл (наприклад `accel-ppp`, `freeradius`) - [ ] Доступ до сервера під root або користувачем із sudo - [ ] Адреса Graylog-сервера: `10.254.254.202:5140` (UDP, вхід "Servers Syslog (RADIUS-accel-ppp)") - [ ] Порт 5140/udp відкритий між цим сервером і Graylog (перевірте firewall/маршрутизацію заздалегідь — Vector мовчки "губить" пакети, якщо порт недоступний, без явної помилки) ## Крок 1. Встановлення Vector 1. Додайте офіційний репозиторій Vector і встановіть його: ```bash bash -c "$(curl -sSfL https://setup.vector.dev)" sudo apt update sudo apt install vector -y ``` 2. Перевірте, що встановилось коректно: ```bash vector --version ``` ## Крок 2. Базова конфігурація (приклад: accel-ppp) 1. Створіть конфігураційний файл: ```bash sudo nano /etc/vector/vector.yaml ``` 2. Вставте базову конфігурацію. Це робочий приклад для `accel-ppp` — використовуйте його як шаблон і довідку, коли додаватимете інші джерела в кроці 5: ```yaml # ========================================== # 1. ДЖЕРЕЛА (Sources) — які файли читати # ========================================== sources: accel_ppp_log: type: "file" include: - "/var/log/accel-ppp/accel-ppp.log" read_from: "beginning" # ========================================== # 2. ПЕРЕТВОРЕННЯ (Transforms) — тегування джерела # ========================================== transforms: format_for_graylog: type: "remap" inputs: - "accel_ppp_log" source: | # Додаємо лише ім'я хоста спереду, як префікс — оригінальний # рядок логу accel-ppp лишається без змін. Саме під його формат # написані pipeline rules у Graylog (interface, RADIUS-атрибути # тощо), тож його не можна перекручувати. .message = "[" + get_hostname!() + "] " + to_string!(.message) # ========================================== # 3. ПРИЗНАЧЕННЯ (Sinks) — куди відправляти # ========================================== sinks: graylog_out: type: "socket" inputs: - "format_for_graylog" address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` > **Чому не `encode_key_value()`?** Перша версія цього гайду огортала > повідомлення в `encode_key_value({...})` (`timestamp=... host=... > tag=... message="..."`). Це виглядало акуратно, але на практиці > ламало обробку в Graylog двома способами: (1) `encode_key_value()` > екранує лапки всередині значення `message` (`"NAS1"` ставало > `\"NAS1\"`), через що regex-правила Graylog для RADIUS/accel-ppp > переставали витягувати поля (`accelppp_interface`, > `acct_status_type` тощо лишались порожніми); (2) Graylog-вхід тут — > звичайний Syslog UDP, а не GELF/структурований, тож жодної реальної > користі з "красивого" key-value формату однаково не було — усе одно > зберігається як один текстовий рядок. Простий префікс з hostname > дає ідентифікацію джерела без цих побічних ефектів. 3. Збережіть файл (`Ctrl+O`, `Enter`) і вийдіть (`Ctrl+X`). 4. Перевірте синтаксис перед запуском: ```bash vector validate /etc/vector/vector.yaml ``` Якщо є помилка — `vector validate` вкаже точний рядок і причину; не запускайте службу, поки ця команда не відповість без помилок. ## Крок 2b. (Опційно) Фільтрація — лишити тільки error/warn **Навіщо:** `accel-ppp` на кожну оренду DHCP, кожен RADIUS accounting-цикл і кожен ipoe-хендшейк пише повний `info`/`debug` обмін. Порахували наживо на реальних цифрах (2 активні NAS-сервери, ~634 байти/повідомлення в Graylog): при масштабуванні до 10+ таких серверів це ~38.5 GiB/добу лише "сирих" даних — диск, розрахований на 14-21 день retention, заповниться за лічені дні. У реальному 1GB лозі одного сервера за добу лише **2,819 рядків** виявились `error:`/`warn:` — решта (мільйони рядків) це info/debug-шум. **Свідомий компроміс:** цей фільтр лишає **тільки** `error:` і `warn:`, відкидаючи все інше — включно з RADIUS Accounting (сесії, трафік Input/Output-Octets). Якщо для вас важлива аналітика сесій/трафіку (дашборд "Servers & Sessions", кореляція за `accelppp_interface`/ `radius_session_id` у Graylog) — **не застосовуйте цей фільтр**, або тримайте один сервер без нього для порівняння. Це рішення per-server: у `vector.yaml` кожного сервера обирається окремо. Додайте `filter`-transform між `format_for_graylog` і `sinks`: ```yaml transforms: format_for_graylog: # ... як у кроці 2, без змін ... # Лишає тільки error:/warn: - усе info/debug (RADIUS accounting, DHCP, # ipoe housekeeping) відкидається. Умова true = повідомлення проходить # далі. keep_error_warn_only: type: "filter" inputs: - "format_for_graylog" condition: 'contains(string!(.message), "error:") || contains(string!(.message), "warn:")' sinks: graylog_out: type: "socket" inputs: - "keep_error_warn_only" # було "format_for_graylog" - тепер через фільтр address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` Перевірка після зміни: ```bash vector validate /etc/vector/vector.yaml sudo systemctl restart vector sudo journalctl -u vector -f # переконайтесь, що йдуть лише error:/warn: рядки ``` **Знайдені наживо типи `error:`/`warn:`** (з реального 1GB логу, 2026-07-23), усі вже покриті pipeline rules у Graylog: - `error: : can't determine router address` — стабільно повторювався 2,746 разів поспіль ~4 години для двох конкретних інтерфейсів, перш ніж саморозв'язатись; саме цей випадок і привів до появи алерту на повторюваність нижче - `warn: : radius: server(N) not responding` — чергування між серверами кожні ~30с - `warn: radius:dm_coa: session not found` — CoA/DM-запит на вже завершену сесію - `warn: : mac change detected` — зміна MAC-адреси абонента під час сесії - `warn: dhcpv4: short packet received` — пошкоджений/обрізаний DHCP-пакет Для будь-якого нового типу `error:`/`warn:`, якого ще нема серед специфічних правил, спрацює fallback-правило (`accelppp_unclassified_error`/`accelppp_unclassified_warn`) — воно тегує `event_type` і `severity_tag`, тож нічого не губиться навіть без власного правила. ### Алерт на повторюваність Одне повідомлення `error:`/`warn:` часто нормальне (одна невдала спроба RADIUS, разова зміна MAC) — тривожний сигнал саме **повторюваність**. Алерт `alerts/alert7_accelppp_repeated_error_warn.json` групує за `gl2_remote_ip` + `event_type` і спрацьовує, якщо той самий тип повідомлення з того самого сервера трапляється **>5 разів за 5 хвилин** — поріг підібраний так, щоб гарантовано зловити знайдений "router address"-інцидент у перший же цикл, але не спамити на поодинокі попередження. ### Повний конфіг — без фільтра Базовий варіант (усе: RADIUS accounting, DHCP, ipoe housekeeping, error/warn — усе разом): ```yaml sources: accel_ppp_log: type: "file" include: - "/var/log/accel-ppp/accel-ppp.log" read_from: "beginning" transforms: format_for_graylog: type: "remap" inputs: - "accel_ppp_log" source: | .message = "[" + get_hostname!() + "] " + to_string!(.message) sinks: graylog_out: type: "socket" inputs: - "format_for_graylog" address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` ### Повний конфіг — з фільтром "тільки error/warn" Той самий конфіг плюс один `transform` і зміна `inputs` у `sinks`: ```yaml sources: accel_ppp_log: type: "file" include: - "/var/log/accel-ppp/accel-ppp.log" read_from: "beginning" transforms: format_for_graylog: type: "remap" inputs: - "accel_ppp_log" source: | .message = "[" + get_hostname!() + "] " + to_string!(.message) keep_error_warn_only: type: "filter" inputs: - "format_for_graylog" condition: 'contains(string!(.message), "error:") || contains(string!(.message), "warn:")' sinks: graylog_out: type: "socket" inputs: - "keep_error_warn_only" address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` **Коли який обирати:** без фільтра — коли потрібна аналітика сесій/трафіку (дашборд "Servers & Sessions", кореляція сесій); з фільтром — коли головне економія диска/мережі і головний інтерес саме в проблемах (error/warn + алерт на повторюваність). Рішення приймається окремо для кожного сервера — нічого не заважає тримати частину серверів з фільтром, частину без. ## Крок 3. Права доступу (запуск від root) Лог-файли на кшталт `accel-ppp.log` часто мають права `600` (читає лише власник, зазвичай root), тож стандартний користувач `vector` не зможе їх прочитати. Найпростіше рішення — запускати службу від `root`. > Альтернатива, якщо не хочете давати `vector` права root: додайте > користувача `vector` у групу-власника файлу (`usermod -aG <група> > vector`) і виставте на файл права `640`. Це вужче за обсягом, але > вимагає стежити за правами при кожному новому джерелі логів. Для > простоти цей гайд іде шляхом `root`. 1. Відкрийте редактор перевизначення systemd-юніта: ```bash sudo systemctl edit vector ``` 2. У порожні рядки вгорі файлу вставте: ```ini [Service] User=root Group=root ``` 3. Збережіть і закрийте (`Ctrl+O`, `Enter`, `Ctrl+X`). ## Крок 4. Запуск та перевірка роботи 1. Перечитайте конфігурацію systemd і запустіть Vector з автозавантаженням: ```bash sudo systemctl daemon-reload sudo systemctl enable --now vector ``` 2. Перевірте статус і логи в реальному часі: ```bash sudo systemctl status vector sudo journalctl -u vector -f ``` Успішний результат — рядок виду: ``` INFO source{...}: Found new file to watch. file=/var/log/accel-ppp/accel-ppp.log ``` без жодних `Permission denied`. 3. Згенеруйте тестовий рядок у файлі логу і перевірте, що він долетів до Graylog (System → Inputs → відповідний input → "Received messages", або Search з широким часовим діапазоном): ```bash echo "vector test line $(date)" | sudo tee -a /var/log/accel-ppp/accel-ppp.log ``` ## Крок 5. Додавання інших джерел логів Щоб додати ще один лог-файл (наприклад, FreeRADIUS, або будь-який інший сервіс, що пише у файл), потрібні три зміни в `/etc/vector/vector.yaml`: новий `source`, новий `transform`, і додати назву цього transform у список `inputs` існуючого `sink`. Сам `sink` (адреса Graylog) лишається один — усі джерела йдуть через нього. ### Шаблон ```yaml sources: <унікальна_назва_джерела>: type: "file" include: - "<повний шлях до файлу логів>" read_from: "beginning" transforms: <унікальна_назва_transform>: type: "remap" inputs: - "<унікальна_назва_джерела>" source: | .message = "[" + get_hostname!() + "] " + to_string!(.message) ``` > Не додавайте сюди `encode_key_value()` чи інше перепакування — > дивіться пояснення "Чому не `encode_key_value()`?" у кроці 2. Якщо > потрібно розрізняти джерела в Graylog, найпростіше — унікальний > префікс замість hostname, наприклад `"[radius] " + to_string!(.message)`. Потім допишіть `<унікальна_назва_transform>` у список `inputs` sink'у `graylog_out` (він уже є в конфізі з кроку 2): ```yaml sinks: graylog_out: type: "socket" inputs: - "format_for_graylog" # вже було (accel-ppp) - "<унікальна_назва_transform>" # новий рядок address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` **Правила іменування**, щоб уникнути конфліктів при кількох джерелах: - назва `source` і `transform` мають бути унікальними в межах усього конфігураційного файлу (не тільки в межах одного сервісу); - `tag` — це те, за чим ви шукатимете в Graylog (`tag:radius-server` тощо), тож робіть його осмисленим і не повторюваним для різних сервісів. ### Готовий приклад: додавання логу FreeRADIUS ```yaml sources: # ... accel_ppp_log вже є ... freeradius_log: type: "file" include: - "/var/log/freeradius/radius.log" read_from: "beginning" transforms: # ... format_for_graylog вже є ... format_freeradius_for_graylog: type: "remap" inputs: - "freeradius_log" source: | .message = "[" + get_hostname!() + "] " + to_string!(.message) sinks: graylog_out: type: "socket" inputs: - "format_for_graylog" - "format_freeradius_for_graylog" address: "udp://10.254.254.202:5140" mode: "udp" encoding: codec: "text" ``` Після будь-якої зміни конфігурації: ```bash vector validate /etc/vector/vector.yaml # спершу перевірити синтаксис sudo systemctl restart vector # застосувати sudo journalctl -u vector -f # переконатись, що нове джерело підхопилось ``` > Якщо новий лог-файл має інші права доступу/власника — переконайтесь, > що користувач, від якого запущено Vector (за кроком 3 — `root`), може > його прочитати, інакше повторите проблему з кроку 3 для нового файлу. ## Крок 6. Прибирання старих правил rsyslog Якщо раніше для відправки логів цього файлу використовувався `rsyslog` (наприклад, через модуль `imfile`), після переходу на Vector варто прибрати дублюючі правила, щоб те саме повідомлення не потрапляло в Graylog двічі з різними форматами. 1. Відкрийте конфігурацію rsyslog (`/etc/rsyslog.conf` або файли у `/etc/rsyslog.d/`). 2. Знайдіть і видаліть/закоментуйте правила з модулем `imfile`, тегом, що відповідає файлу (наприклад `accel-remote-only`), і командою видалення повідомлення (`~`, щоб воно не йшло далі за замовчуванням). 3. Перезапустіть rsyslog: ```bash sudo systemctl restart rsyslog ``` ## Діагностика проблем | Симптом | Ймовірна причина | Що перевірити | |---|---|---| | `Permission denied` у `journalctl -u vector` | Vector не має прав читати лог-файл | Крок 3 виконано? `ls -la <файл>` — хто власник і які права | | Служба запущена, файл читається, але в Graylog нічого немає | Порт 5140 недоступний з цього сервера (firewall/маршрутизація) | `nc -u -zv 10.254.254.202 5140` з цього сервера; `tcpdump -i eth0 udp port 5140` на стороні Graylog | | `vector validate` видає помилку | Помилка синтаксису YAML або VRL у `transform` | Читайте повідомлення про помилку — воно вказує точний рядок; перевірте відступи (YAML чутливий до пробілів) | | Повідомлення доходять, але без полів `vendor`/`event_type` у Graylog | Pipeline rules у Graylog ще не мають правила під формат саме цього тегу/сервісу | Дайте кілька реальних рядків логу — можна додати нове pipeline rule за тим самим принципом, що й для accel-ppp/RADIUS | | Дублікати повідомлень у Graylog | Старе правило `rsyslog` досі активне паралельно з Vector | Виконайте крок 6 | | Vector запускається, але не бачить оновлень файлу | Файл ротується (logrotate) інакше, ніж очікує Vector | Перевірте `include`/`ignore_older_secs`; за потреби додайте `fingerprint` налаштування у `source`, щоб Vector коректно стежив за файлом після ротації | | `vector validate` видає `error[E103]: unhandled fallible assignment` у `transform` | VRL вимагає явно гарантувати тип операнда перед конкатенацією/операцією (наприклад, `.message` без `to_string!()`) | Обгорніть проблемне поле в `to_string!(...)` (або іншу `!`-функцію відповідного типу) — саме так це вирішено в базовому конфізі кроку 2 | | Критичні алерти в Discord на рутинні `info`-повідомлення від Vector-джерела | Syslog UDP input не бачить валідного `` заголовка в сирому тексті Vector, тож Graylog проставляє severity `-1` ("невідомо") — а не 0-2 (справді критично) | Це вже виправлено на стороні Graylog (правило `generic_critical_severity` тепер явно виключає `level < 0`); якщо побачите знову — перевірте, чи не відкотилось це правило | | RADIUS accounting/DHCP/ipoe-рядки більше не з'являються в Graylog | Це очікувано, якщо додано `keep_error_warn_only` з кроку 2b — фільтр лишає тільки error:/warn: | Перевірте `vector.yaml` — якщо потрібна аналітика сесій/трафіку, приберіть цей transform з ланцюжка `inputs` sink'у (див. "Повний конфіг без фільтра" у кроці 2b) | | Алерт "abnormal message volume" щойно після першого запуску Vector на новому сервері | `read_from: "beginning"` (крок 2) означає, що при першому старті Vector вичитує весь наявний лог-файл одразу, а не тільки нові рядки — одноразовий сплеск, не триваюча проблема | Перевірте обсяг від цього джерела через кілька хвилин (`gl2_remote_ip:` у Search) — має впасти до звичайного рівня | ## Довідка: структура конфігурації - **`sources`** — звідки Vector бере дані. Тип `file` = читає (і "хвостить") вказаний файл, `read_from: "beginning"` = при першому запуску прочитати файл з початку (а не тільки нові рядки). - **`transforms`** (тип `remap`) — обробка даних мовою VRL (Vector Remap Language). У цьому гайді використовується лише конкатенація рядків (`"[" + get_hostname!() + "] " + to_string!(.message)`), щоб додати ідентифікацію хоста, не чіпаючи оригінальний формат рядка логу — саме під цей оригінальний формат написані pipeline rules у Graylog. `to_string!()` потрібен тому, що VRL вимагає явно гарантувати тип рядка перед конкатенацією (`!` означає "перервати обробку цього повідомлення, якщо не вдалось" — тут це станеться хіба що для зовсім нетипових вхідних даних). - **`sinks`** (тип `socket`, `mode: "udp"`) — куди й як відправляти результат. Один sink може приймати від багатьох transforms одночасно (список `inputs`) — саме так додаються нові джерела логів без створення нового sink чи нової адреси.