From 31258eee013af91430b9b3ea48f74fd89b040d0f Mon Sep 17 00:00:00 2001 From: byrsapty Date: Thu, 23 Jul 2026 16:20:13 +0300 Subject: [PATCH] Add error/warn-only Vector filter + 5 new pipeline rules + repetition alert Analyzed a real 1GB accel-ppp log (2026-07-23): only 2,819 of its lines were error:/warn:, and one pattern - "can't determine router address" - repeated 2,746 times over ~4 hours for two specific subscriber interfaces before self-resolving, completely unalerted since no alert covered it. - New pipeline rules for 3 previously-unclassified real message types (radius:dm_coa session not found, mac change detected, dhcpv4 short packet) plus a text-based fallback pair (accelppp_unclassified_error/ warn) for anything not yet specifically classified - needed because Vector-shipped accel-ppp lines have no real syslog PRI header, so the numeric-severity generic_critical_severity rule never fires for this source. - New alert7: group by gl2_remote_ip + event_type, fires on >5 occurrences in 5 minutes - low enough to have caught the real incident within its first cycle, high enough to tolerate a single transient warning. - vector-accel-ppp-setup.md gained a "keep only error/warn" filter option (Step 2b), explicitly documented as a deliberate tradeoff: it also drops RADIUS accounting, so the Servers & Sessions dashboard and session correlation go empty for any server that applies it. Both the with-filter and without-filter full configs are included so the choice is per-server, not global. Co-Authored-By: Claude Sonnet 5 --- README.md | 1 + README.uk.md | 1 + .../alert7_accelppp_repeated_error_warn.json | 21 +++ pipelines/pipeline2_servers.json | 2 +- ...accelppp_radius_coa_session_not_found.json | 5 + .../rule23_accelppp_mac_change_detected.json | 5 + .../rule24_accelppp_dhcpv4_short_packet.json | 5 + rules/rule25_accelppp_unclassified_error.json | 5 + rules/rule26_accelppp_unclassified_warn.json | 5 + vector-accel-ppp-setup.md | 162 ++++++++++++++---- 10 files changed, 182 insertions(+), 30 deletions(-) create mode 100644 alerts/alert7_accelppp_repeated_error_warn.json create mode 100644 rules/rule22_accelppp_radius_coa_session_not_found.json create mode 100644 rules/rule23_accelppp_mac_change_detected.json create mode 100644 rules/rule24_accelppp_dhcpv4_short_packet.json create mode 100644 rules/rule25_accelppp_unclassified_error.json create mode 100644 rules/rule26_accelppp_unclassified_warn.json diff --git a/README.md b/README.md index 7ceefa2..f1fa019 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,7 @@ parsed and searchable but never page anyone): | Juniper chassis hardware alarm | `CHASSISD_SNMP_TRAP`/`CHASSISD_SNMP_TRAP6` (over temperature, fan, power supply, etc.) - confirmed live against a real `VC.MYRONIVKA` chassis | High | | Abnormal message volume from one server | A single server in the Servers stream sends more than 150,000 messages in a 10-minute window - see "Message-volume (flood) alerts" below | Medium | | Abnormal syslog volume from network equipment | A single device in the Network Equipment stream sends more than 500 messages in a 5-minute window - see "Message-volume (flood) alerts" below | Medium | +| Repeated accel-ppp error/warning from one server | The same `event_type` (from `error:`/`warn:` accel-ppp lines) recurs more than 5 times in 5 minutes from one server - catches a stuck session or ongoing condition rather than a single transient warning. Threshold confirmed live against a real incident: `can't determine router address` repeating 2,746 times over ~4 hours for two specific interfaces | Medium | That third one is the "universal network equipment problem" catch-all: it doesn't depend on knowing any vendor's specific message format, just the diff --git a/README.uk.md b/README.uk.md index 4de6193..9d94fa4 100644 --- a/README.uk.md +++ b/README.uk.md @@ -469,6 +469,7 @@ Graylog відхиляє його з помилкою Jackson-поліморфі | Juniper chassis hardware alarm | `CHASSISD_SNMP_TRAP`/`CHASSISD_SNMP_TRAP6` (перегрів, вентилятор, блок живлення тощо) — перевірено наживо на реальному шасі `VC.MYRONIVKA` | High | | Аномальний обсяг повідомлень від одного сервера | Один сервер у стрімі Servers шле понад 150 000 повідомлень за 10-хвилинне вікно — див. "Алерти на обсяг (flood)" нижче | Medium | | Аномальний обсяг syslog від мережевого обладнання | Один пристрій у стрімі Network Equipment шле понад 500 повідомлень за 5-хвилинне вікно — див. "Алерти на обсяг (flood)" нижче | Medium | +| Повторюваний error/warning accel-ppp з одного сервера | Той самий `event_type` (з рядків `error:`/`warn:` accel-ppp) повторюється понад 5 разів за 5 хвилин з одного сервера — ловить завислу сесію чи триваючий стан, а не разове попередження. Поріг підтверджено наживо на реальному інциденті: `can't determine router address` повторився 2 746 разів за ~4 години для двох конкретних інтерфейсів | Medium | Третій алерт і є тим самим "універсальним" покриттям проблем мережевого обладнання: він не залежить від знання формату повідомлень конкретного diff --git a/alerts/alert7_accelppp_repeated_error_warn.json b/alerts/alert7_accelppp_repeated_error_warn.json new file mode 100644 index 0000000..dd6e181 --- /dev/null +++ b/alerts/alert7_accelppp_repeated_error_warn.json @@ -0,0 +1,21 @@ +{ + "title": "WARNING: repeated accel-ppp error/warning from one server", + "description": "The same category of accel-ppp error/warning is repeating from one server - a single occurrence can be transient (a RADIUS retry, a one-off MAC change), but sustained repetition usually means a stuck session or an ongoing condition. Confirmed live on 2026-07-23 against a real 1GB accel-ppp log: 'can't determine router address' repeated 2,746 times over ~4 hours for two specific subscriber interfaces before self-resolving, with no alert firing at the time since this alert didn't exist yet. Threshold (>5 in 5 minutes) is set low enough to have caught that incident within its first cycle, while still tolerating an occasional single warning.", + "priority": 2, + "alert": true, + "config": { + "type": "aggregation-v1", + "query": "vendor:accel-ppp", + "streams": ["__SERVERS_STREAM_ID__"], + "group_by": ["gl2_remote_ip", "event_type"], + "series": [{"type": "count", "id": "count-", "field": null}], + "conditions": {"expression": {"expr": ">", "left": {"expr": "number-ref", "ref": "count-"}, "right": {"expr": "number", "value": 5.0}}}, + "search_within_ms": 300000, + "execute_every_ms": 300000, + "event_limit": 50 + }, + "field_spec": {}, + "key_spec": [], + "notification_settings": {"grace_period_ms": 900000, "backlog_size": 5}, + "notifications": [{"notification_id": "__DISCORD_NOTIFICATION_ID__"}] +} diff --git a/pipelines/pipeline2_servers.json b/pipelines/pipeline2_servers.json index b524cb6..5e8808d 100644 --- a/pipelines/pipeline2_servers.json +++ b/pipelines/pipeline2_servers.json @@ -1,5 +1,5 @@ { "title": "Servers Parsing", "description": "Parses accel-ppp/RADIUS/conntrack syslog", - "source": "pipeline \"Servers Parsing\"\nstage 0 match either\n rule \"accelppp_router_address_error\";\n rule \"accelppp_radius_accounting\";\n rule \"accelppp_radius_access_request\";\n rule \"accelppp_radius_server_down\";\n rule \"accelppp_no_radius_servers\";\n rule \"accelppp_auth_failed\";\n rule \"conntrack_table_full\";\n rule \"freeradius_login_ok\";\n rule \"freeradius_login_incorrect\";\n rule \"generic_critical_severity\";\n rule \"accelppp_interface_tag\";\nend" + "source": "pipeline \"Servers Parsing\"\nstage 0 match either\n rule \"accelppp_router_address_error\";\n rule \"accelppp_radius_accounting\";\n rule \"accelppp_radius_access_request\";\n rule \"accelppp_radius_server_down\";\n rule \"accelppp_no_radius_servers\";\n rule \"accelppp_auth_failed\";\n rule \"accelppp_radius_coa_session_not_found\";\n rule \"accelppp_mac_change_detected\";\n rule \"accelppp_dhcpv4_short_packet\";\n rule \"conntrack_table_full\";\n rule \"freeradius_login_ok\";\n rule \"freeradius_login_incorrect\";\n rule \"generic_critical_severity\";\n rule \"accelppp_unclassified_error\";\n rule \"accelppp_unclassified_warn\";\n rule \"accelppp_interface_tag\";\nend" } diff --git a/rules/rule22_accelppp_radius_coa_session_not_found.json b/rules/rule22_accelppp_radius_coa_session_not_found.json new file mode 100644 index 0000000..107b897 --- /dev/null +++ b/rules/rule22_accelppp_radius_coa_session_not_found.json @@ -0,0 +1,5 @@ +{ + "title": "accelppp_radius_coa_session_not_found", + "description": "accel-ppp: RADIUS CoA/Disconnect-Message arrived for a session that no longer exists - found live in a real 1GB accel-ppp log sample (2026-07-23), format: 'warn: radius:dm_coa: session not found'", + "source": "rule \"accelppp_radius_coa_session_not_found\"\nwhen\n contains(to_string($message.message), \"radius:dm_coa: session not found\")\nthen\n set_field(\"vendor\", \"accel-ppp\");\n set_field(\"event_type\", \"radius_coa_session_not_found\");\n set_field(\"severity_tag\", \"warning\");\nend" +} diff --git a/rules/rule23_accelppp_mac_change_detected.json b/rules/rule23_accelppp_mac_change_detected.json new file mode 100644 index 0000000..e5c767d --- /dev/null +++ b/rules/rule23_accelppp_mac_change_detected.json @@ -0,0 +1,5 @@ +{ + "title": "accelppp_mac_change_detected", + "description": "accel-ppp: a subscriber's MAC address changed mid-session - found live in a real 1GB accel-ppp log sample (2026-07-23), format: 'warn: : mac change detected'. accelppp_interface is filled in by the accelppp_interface_tag fallback rule, not here.", + "source": "rule \"accelppp_mac_change_detected\"\nwhen\n contains(to_string($message.message), \"mac change detected\")\nthen\n set_field(\"vendor\", \"accel-ppp\");\n set_field(\"event_type\", \"mac_change_detected\");\n set_field(\"severity_tag\", \"warning\");\nend" +} diff --git a/rules/rule24_accelppp_dhcpv4_short_packet.json b/rules/rule24_accelppp_dhcpv4_short_packet.json new file mode 100644 index 0000000..b273c85 --- /dev/null +++ b/rules/rule24_accelppp_dhcpv4_short_packet.json @@ -0,0 +1,5 @@ +{ + "title": "accelppp_dhcpv4_short_packet", + "description": "accel-ppp: malformed/truncated DHCPv4 packet received - found live in a real 1GB accel-ppp log sample (2026-07-23), format: 'warn: dhcpv4: short packet received' (no interface in the text itself, unlike most other accel-ppp warnings)", + "source": "rule \"accelppp_dhcpv4_short_packet\"\nwhen\n contains(to_string($message.message), \"dhcpv4: short packet received\")\nthen\n set_field(\"vendor\", \"accel-ppp\");\n set_field(\"event_type\", \"dhcpv4_short_packet\");\n set_field(\"severity_tag\", \"warning\");\nend" +} diff --git a/rules/rule25_accelppp_unclassified_error.json b/rules/rule25_accelppp_unclassified_error.json new file mode 100644 index 0000000..f2d3720 --- /dev/null +++ b/rules/rule25_accelppp_unclassified_error.json @@ -0,0 +1,5 @@ +{ + "title": "accelppp_unclassified_error", + "description": "accel-ppp: fallback for any 'error:' line that no specific rule already classified - same catch-all philosophy as generic_critical_severity, but text-based (Vector-shipped accel-ppp lines have no real syslog PRI header, so the numeric severity field is always -1/unparseable and generic_critical_severity never fires for this source).", + "source": "rule \"accelppp_unclassified_error\"\nwhen\n !has_field(\"event_type\") && contains(to_string($message.message), \"error:\")\nthen\n set_field(\"vendor\", \"accel-ppp\");\n set_field(\"event_type\", \"accelppp_unclassified_error\");\n set_field(\"severity_tag\", \"critical\");\nend" +} diff --git a/rules/rule26_accelppp_unclassified_warn.json b/rules/rule26_accelppp_unclassified_warn.json new file mode 100644 index 0000000..e5d04ce --- /dev/null +++ b/rules/rule26_accelppp_unclassified_warn.json @@ -0,0 +1,5 @@ +{ + "title": "accelppp_unclassified_warn", + "description": "accel-ppp: fallback for any 'warn:' line that no specific rule already classified - see accelppp_unclassified_error for why a text-based fallback is needed instead of the numeric-severity one.", + "source": "rule \"accelppp_unclassified_warn\"\nwhen\n !has_field(\"event_type\") && contains(to_string($message.message), \"warn:\")\nthen\n set_field(\"vendor\", \"accel-ppp\");\n set_field(\"event_type\", \"accelppp_unclassified_warn\");\n set_field(\"severity_tag\", \"warning\");\nend" +} diff --git a/vector-accel-ppp-setup.md b/vector-accel-ppp-setup.md index b799cd3..f207881 100644 --- a/vector-accel-ppp-setup.md +++ b/vector-accel-ppp-setup.md @@ -139,45 +139,46 @@ Якщо є помилка — `vector validate` вкаже точний рядок і причину; не запускайте службу, поки ця команда не відповість без помилок. -## Крок 2b. (Опційно) Фільтрація DHCP-шуму +## Крок 2b. (Опційно) Фільтрація — лишити тільки error/warn -**Навіщо:** `accel-ppp` при кожній оренді/оновленні DHCP пише повний -обмін (`recv [DHCPv4 Discover...]`, `send [DHCPv4 Ack...]` тощо) — це -основна частина обсягу логів, а не RADIUS/accounting-дані, заради яких -власне і збирається моніторинг. Порахували наживо на реальних цифрах -(2 активні NAS-сервери, ~634 байти/повідомлення в Graylog): при -масштабуванні до 10+ таких серверів це ~38.5 GiB/добу лише "сирих" даних -— диск, розрахований на 14-21 день retention, заповниться за лічені дні. -Прибирання DHCP на рівні Vector (до того, як пакет узагалі покине цей -сервер) — найдешевший спосіб скоротити обсяг, бо не витрачається ні -мережевий трафік, ні дисковий IO/CPU Graylog на індексацію того, що -однаково ніхто не шукає. +**Навіщо:** `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-шум. -Додайте `filter`-transform між `format_for_graylog` і `sinks`. Умова -навмисно **не** дропає DHCP-рядки, якщо вони позначені як `error:` чи -`warn:` — щоб реальна проблема (наприклад, вичерпання DHCP-пулу чи -таймаут оренди) не загубилась разом зі звичайним шумом: +**Свідомий компроміс:** цей фільтр лишає **тільки** `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, без змін ... - # Прибирає рутинний DHCP-обмін (Discover/Request/Offer/Ack), АЛЕ - # залишає DHCP-рядки, позначені error:/warn: - щоб не втратити реальну - # проблему (вичерпання пулу, таймаут оренди тощо) разом зі звичайним - # шумом. Умова true = повідомлення проходить далі. - drop_dhcp_noise: + # Лишає тільки error:/warn: - усе info/debug (RADIUS accounting, DHCP, + # ipoe housekeeping) відкидається. Умова true = повідомлення проходить + # далі. + keep_error_warn_only: type: "filter" inputs: - "format_for_graylog" - condition: '!contains(string!(.message), "[DHCPv4 ") || contains(string!(.message), "error:") || contains(string!(.message), "warn:")' + condition: 'contains(string!(.message), "error:") || contains(string!(.message), "warn:")' sinks: graylog_out: type: "socket" inputs: - - "drop_dhcp_noise" # було "format_for_graylog" - тепер через фільтр + - "keep_error_warn_only" # було "format_for_graylog" - тепер через фільтр address: "udp://10.254.254.202:5140" mode: "udp" encoding: @@ -189,13 +190,116 @@ sinks: ```bash vector validate /etc/vector/vector.yaml sudo systemctl restart vector -sudo journalctl -u vector -f # переконайтесь, що йдуть RADIUS-рядки, а DHCPv4 - ні +sudo journalctl -u vector -f # переконайтесь, що йдуть лише error:/warn: рядки ``` -Якщо для якогось джерела DHCP-дані навпаки потрібні (наприклад, окремий -DHCP-сервер, а не accel-ppp) — просто не додавайте `drop_dhcp_noise` у -ланцюжок цього джерела; фільтр застосовується лише до того `transform`, -який у нього `inputs`. +**Знайдені наживо типи `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) @@ -383,7 +487,7 @@ Graylog двічі з різними форматами. | 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`); якщо побачите знову — перевірте, чи не відкотилось це правило | -| DHCP-рядки більше не з'являються в Graylog | Це очікувано, якщо додано `drop_dhcp_noise` з кроку 2b | Перевірте `vector.yaml` — якщо DHCP-дані все ж потрібні, приберіть цей transform з ланцюжка `inputs` sink'у | +| 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) — має впасти до звичайного рівня | ## Довідка: структура конфігурації