feat: add Juniper chassis alarm alerting, Discord notifications, and global timezone configuration for accurate syslog timestamp parsing
This commit is contained in:
parent
eed2de0571
commit
0db85fa860
8 changed files with 182 additions and 52 deletions
49
README.md
49
README.md
|
|
@ -268,6 +268,34 @@ each widget needs a matching `search_types` entry (in `search.json`) and
|
||||||
docs still reference 6.0.x — don't trust cached documentation over
|
docs still reference 6.0.x — don't trust cached documentation over
|
||||||
what the running server actually reports).
|
what the running server actually reports).
|
||||||
|
|
||||||
|
- **RFC3164 syslog timestamps have no timezone - Graylog assumes UTC by
|
||||||
|
default**: most network gear and accel-ppp send classic RFC3164 syslog
|
||||||
|
(`Jul 22 09:15:13`, no year, no offset). Without an explicit `timezone`
|
||||||
|
setting on the input, Graylog stores that bare timestamp as if it were
|
||||||
|
already UTC - so a device logging in local Kyiv time (UTC+3) shows up
|
||||||
|
3 hours in the future in Graylog. Confirmed live: a raw test packet with
|
||||||
|
`Jul 22 09:15:13` was stored as `09:15:13Z` (wrong) until each Syslog UDP
|
||||||
|
input's `timezone` config was set to the container's own timezone
|
||||||
|
(`Europe/Kyiv` here); afterwards it correctly stored as `06:15:13Z`
|
||||||
|
(`09:15:13` Kyiv = `06:15:13` UTC). `step_inputs()` in
|
||||||
|
`install-graylog.sh` sets this automatically from `/etc/timezone` for
|
||||||
|
every input it creates, and self-heals it on existing inputs that
|
||||||
|
predate this fix.
|
||||||
|
|
||||||
|
- **The built-in `admin` user's displayed timezone is a separate setting
|
||||||
|
from the input-level fix above**: even after the RFC3164 fix, the Web UI
|
||||||
|
can still show times in UTC for the read-only built-in `admin` account.
|
||||||
|
That account's timezone is `read_only: true` and **cannot** be changed
|
||||||
|
via `PUT /api/users/admin` (confirmed live - fails with `"state should
|
||||||
|
be: hexString has 24 characters"`, since that endpoint isn't meant for
|
||||||
|
the special built-in account). It can only be set server-side via the
|
||||||
|
`root_timezone` config option, i.e. Docker's `GRAYLOG_ROOT_TIMEZONE` env
|
||||||
|
var. `step_compose_files()` sets this from `/etc/timezone` for fresh
|
||||||
|
installs and self-heals it into any pre-existing `.env` that predates
|
||||||
|
this fix; `docker-compose.yml` passes it through to the `graylog`
|
||||||
|
service. A `docker compose up -d` re-run picks up the change and
|
||||||
|
recreates the container automatically.
|
||||||
|
|
||||||
- **Network gear sends syslog to port 514, not a custom port**: most
|
- **Network gear sends syslog to port 514, not a custom port**: most
|
||||||
switches/OLTs (confirmed live with a BDCOM S5612) only support
|
switches/OLTs (confirmed live with a BDCOM S5612) only support
|
||||||
`logging <host>`, which always uses the standard UDP/514, with no way to
|
`logging <host>`, which always uses the standard UDP/514, with no way to
|
||||||
|
|
@ -299,6 +327,7 @@ are parsed and searchable but never page anyone):
|
||||||
| RADIUS server unreachable | `radius: server(N) not responding` or `radius: no available servers` (verified strings from accel-ppp source, `radius/req.c`) | High |
|
| RADIUS server unreachable | `radius: server(N) not responding` or `radius: no available servers` (verified strings from accel-ppp source, `radius/req.c`) | High |
|
||||||
| conntrack table full (packet loss) | `nf_conntrack: table full, dropping packet` (standard Linux kernel message - active, ongoing packet loss) | High |
|
| conntrack table full (packet loss) | `nf_conntrack: table full, dropping packet` (standard Linux kernel message - active, ongoing packet loss) | High |
|
||||||
| Unrecognized critical-severity syslog | Any message (any vendor, either stream) with RFC5424/3164 syslog severity Emergency/Alert/Critical (0-2) that no specific pipeline rule already classified | Medium |
|
| Unrecognized critical-severity syslog | Any message (any vendor, either stream) with RFC5424/3164 syslog severity Emergency/Alert/Critical (0-2) that no specific pipeline rule already classified | Medium |
|
||||||
|
| 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 |
|
||||||
|
|
||||||
That third one is the "universal network equipment problem" catch-all: it
|
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
|
doesn't depend on knowing any vendor's specific message format, just the
|
||||||
|
|
@ -316,13 +345,29 @@ when running `create-graylog-lxc.sh`. Under the hood this uses Graylog's
|
||||||
built-in **Slack** notification type pointed at
|
built-in **Slack** notification type pointed at
|
||||||
`<your-webhook-url>/slack` - Discord's Slack-compatibility endpoint - so no
|
`<your-webhook-url>/slack` - Discord's Slack-compatibility endpoint - so no
|
||||||
extra converter service is needed. The message template shows the event
|
extra converter service is needed. The message template shows the event
|
||||||
title/description plus the source and raw text of every matched message:
|
title/description plus the source, sending IP, and raw text of every
|
||||||
|
matched message:
|
||||||
```
|
```
|
||||||
*${event_definition_title}*
|
*${event_definition_title}*
|
||||||
${event_definition_description}
|
${event_definition_description}
|
||||||
${if backlog}${foreach backlog message}• `${message.source}`: ${message.message}
|
${if backlog}${foreach backlog message}• `${message.source}` (IP: ${message.fields.gl2_remote_ip}): ${message.message}
|
||||||
${end}${end}
|
${end}${end}
|
||||||
```
|
```
|
||||||
|
`gl2_remote_ip` is a field Graylog attaches automatically to every message
|
||||||
|
based on the actual UDP packet's source address, regardless of what
|
||||||
|
hostname the device itself claims in the syslog `source` field.
|
||||||
|
|
||||||
|
### Searching by IP address
|
||||||
|
|
||||||
|
Every message is searchable by the sending device's real IP via the same
|
||||||
|
`gl2_remote_ip` field, in the Search page query bar:
|
||||||
|
```
|
||||||
|
gl2_remote_ip:93.171.243.4
|
||||||
|
```
|
||||||
|
`source:<value>` also works, but only matches if the device's self-reported
|
||||||
|
hostname was used (some equipment sends its actual IP as the hostname,
|
||||||
|
others send a configured name) - `gl2_remote_ip` is the reliable one since
|
||||||
|
it's derived from the packet itself, not device-supplied data.
|
||||||
|
|
||||||
### Verifying a test alert
|
### Verifying a test alert
|
||||||
|
|
||||||
|
|
|
||||||
50
README.uk.md
50
README.uk.md
|
|
@ -277,6 +277,35 @@ keyboard/click-події без одночасного тригера внут
|
||||||
документація досі згадує 6.0.x — не довіряйте кешованій документації
|
документація досі згадує 6.0.x — не довіряйте кешованій документації
|
||||||
більше, ніж тому, що фактично повідомляє запущений сервер).
|
більше, ніж тому, що фактично повідомляє запущений сервер).
|
||||||
|
|
||||||
|
- **У RFC3164 syslog немає часового поясу — Graylog за замовчуванням
|
||||||
|
вважає це UTC**: більшість мережевого обладнання і accel-ppp шлють
|
||||||
|
класичний RFC3164 syslog (`Jul 22 09:15:13`, без року, без зсуву). Без
|
||||||
|
явного налаштування `timezone` на input Graylog зберігає цей "голий"
|
||||||
|
час так, ніби він уже в UTC — тож пристрій, що пише за київським часом
|
||||||
|
(UTC+3), в Graylog виглядає так, ніби події відбулись на 3 години в
|
||||||
|
майбутньому. Перевірено наживо: тестовий пакет із `Jul 22 09:15:13`
|
||||||
|
зберігався як `09:15:13Z` (неправильно), поки в конфігурації кожного
|
||||||
|
Syslog UDP input не виставили `timezone` на часовий пояс самого
|
||||||
|
контейнера (`Europe/Kyiv`); після цього він коректно зберігається як
|
||||||
|
`06:15:13Z` (`09:15:13` за Києвом = `06:15:13` UTC). `step_inputs()` у
|
||||||
|
`install-graylog.sh` виставляє це автоматично з `/etc/timezone` для
|
||||||
|
кожного input, який створює, і сам виправляє це на вже існуючих input,
|
||||||
|
створених до цього фіксу.
|
||||||
|
|
||||||
|
- **Часовий пояс відображення для вбудованого користувача `admin` — це
|
||||||
|
окреме налаштування, не те саме, що фікс вище**: навіть після фіксу
|
||||||
|
RFC3164 веб-інтерфейс міг далі показувати час в UTC для вбудованого
|
||||||
|
read-only акаунта `admin`. Часовий пояс цього акаунта **не можна**
|
||||||
|
змінити через `PUT /api/users/admin` (перевірено наживо — падає з
|
||||||
|
`"state should be: hexString has 24 characters"`, оскільки цей ендпоінт
|
||||||
|
не призначений для спеціального вбудованого акаунта). Він
|
||||||
|
налаштовується лише на рівні сервера — опція `root_timezone`, тобто
|
||||||
|
змінна оточення Docker `GRAYLOG_ROOT_TIMEZONE`. `step_compose_files()`
|
||||||
|
виставляє її з `/etc/timezone` для нових інсталяцій і сам додає її в
|
||||||
|
уже існуючий `.env`, створений до цього фіксу; `docker-compose.yml`
|
||||||
|
прокидає її в сервіс `graylog`. Повторний запуск `docker compose up -d`
|
||||||
|
підхоплює зміну і сам перестворює контейнер.
|
||||||
|
|
||||||
- **Retention індексів звужений навмисно**: фабричний дефолт Graylog 7.1
|
- **Retention індексів звужений навмисно**: фабричний дефолт Graylog 7.1
|
||||||
зберігає 30-40 днів даних у до 20 індексах — прийнятно загалом, але
|
зберігає 30-40 днів даних у до 20 індексах — прийнятно загалом, але
|
||||||
ризиковано на малому диску (це розгортання: 50GB) у поєднанні з
|
ризиковано на малому диску (це розгортання: 50GB) у поєднанні з
|
||||||
|
|
@ -316,6 +345,7 @@ keyboard/click-події без одночасного тригера внут
|
||||||
| RADIUS server unreachable | `radius: server(N) not responding` або `radius: no available servers` (перевірені рядки з вихідного коду accel-ppp, `radius/req.c`) | High |
|
| RADIUS server unreachable | `radius: server(N) not responding` або `radius: no available servers` (перевірені рядки з вихідного коду accel-ppp, `radius/req.c`) | High |
|
||||||
| conntrack table full (packet loss) | `nf_conntrack: table full, dropping packet` (стандартне повідомлення ядра Linux — активна втрата пакетів прямо зараз) | High |
|
| conntrack table full (packet loss) | `nf_conntrack: table full, dropping packet` (стандартне повідомлення ядра Linux — активна втрата пакетів прямо зараз) | High |
|
||||||
| Unrecognized critical-severity syslog | Будь-яке повідомлення (будь-який вендор, будь-який стрім) із syslog-severity Emergency/Alert/Critical (0-2) за RFC5424/3164, яке не класифікувало жодне спеціальне правило | Medium |
|
| Unrecognized critical-severity syslog | Будь-яке повідомлення (будь-який вендор, будь-який стрім) із syslog-severity Emergency/Alert/Critical (0-2) за RFC5424/3164, яке не класифікувало жодне спеціальне правило | Medium |
|
||||||
|
| Juniper chassis hardware alarm | `CHASSISD_SNMP_TRAP`/`CHASSISD_SNMP_TRAP6` (перегрів, вентилятор, блок живлення тощо) — перевірено наживо на реальному шасі `VC.MYRONIVKA` | High |
|
||||||
|
|
||||||
Третій алерт і є тим самим "універсальним" покриттям проблем мережевого
|
Третій алерт і є тим самим "універсальним" покриттям проблем мережевого
|
||||||
обладнання: він не залежить від знання формату повідомлень конкретного
|
обладнання: він не залежить від знання формату повідомлень конкретного
|
||||||
|
|
@ -334,13 +364,29 @@ keyboard/click-події без одночасного тригера внут
|
||||||
використовується вбудований тип нотифікації Graylog **Slack**, спрямований
|
використовується вбудований тип нотифікації Graylog **Slack**, спрямований
|
||||||
на `<ваш-webhook-url>/slack` — Slack-сумісний ендпоінт Discord — тож окремий
|
на `<ваш-webhook-url>/slack` — Slack-сумісний ендпоінт Discord — тож окремий
|
||||||
конвертер не потрібен. Шаблон повідомлення показує заголовок/опис події
|
конвертер не потрібен. Шаблон повідомлення показує заголовок/опис події
|
||||||
плюс джерело і повний текст кожного повідомлення, що спрацювало:
|
плюс джерело, IP відправника і повний текст кожного повідомлення, що
|
||||||
|
спрацювало:
|
||||||
```
|
```
|
||||||
*${event_definition_title}*
|
*${event_definition_title}*
|
||||||
${event_definition_description}
|
${event_definition_description}
|
||||||
${if backlog}${foreach backlog message}• `${message.source}`: ${message.message}
|
${if backlog}${foreach backlog message}• `${message.source}` (IP: ${message.fields.gl2_remote_ip}): ${message.message}
|
||||||
${end}${end}
|
${end}${end}
|
||||||
```
|
```
|
||||||
|
`gl2_remote_ip` — поле, яке Graylog проставляє автоматично для кожного
|
||||||
|
повідомлення на основі реальної адреси відправника UDP-пакета, незалежно
|
||||||
|
від того, яке ім'я хоста заявляє сам пристрій у полі syslog `source`.
|
||||||
|
|
||||||
|
### Пошук за IP-адресою
|
||||||
|
|
||||||
|
Кожне повідомлення можна знайти за реальною IP-адресою пристрою через те
|
||||||
|
саме поле `gl2_remote_ip`, у рядку пошуку на сторінці Search:
|
||||||
|
```
|
||||||
|
gl2_remote_ip:93.171.243.4
|
||||||
|
```
|
||||||
|
`source:<значення>` теж працює, але лише якщо пристрій сам надсилає свою
|
||||||
|
IP-адресу як ім'я хоста (деяке обладнання так робить, інше шле
|
||||||
|
налаштоване ім'я) — `gl2_remote_ip` надійніший, бо береться з самого
|
||||||
|
пакета, а не з даних, які надає пристрій.
|
||||||
|
|
||||||
### Перевірка тестового алерту
|
### Перевірка тестового алерту
|
||||||
|
|
||||||
|
|
|
||||||
21
alerts/alert4_juniper_chassis_alarm.json
Normal file
21
alerts/alert4_juniper_chassis_alarm.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"title": "CRITICAL: Juniper chassis hardware alarm",
|
||||||
|
"description": "Chassis-level SNMP trap from Juniper equipment (over temperature, fan, power supply, etc.) - potential hardware failure risk",
|
||||||
|
"priority": 3,
|
||||||
|
"alert": true,
|
||||||
|
"config": {
|
||||||
|
"type": "aggregation-v1",
|
||||||
|
"query": "event_type:chassis_alarm",
|
||||||
|
"streams": ["__NETWORK_STREAM_ID__"],
|
||||||
|
"group_by": [],
|
||||||
|
"series": [],
|
||||||
|
"conditions": {"expression": null},
|
||||||
|
"search_within_ms": 60000,
|
||||||
|
"execute_every_ms": 60000,
|
||||||
|
"event_limit": 100
|
||||||
|
},
|
||||||
|
"field_spec": {},
|
||||||
|
"key_spec": [],
|
||||||
|
"notification_settings": {"grace_period_ms": 600000, "backlog_size": 5},
|
||||||
|
"notifications": [{"notification_id": "__DISCORD_NOTIFICATION_ID__"}]
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"color": "#FF0000",
|
"color": "#FF0000",
|
||||||
"webhook_url": "__DISCORD_WEBHOOK_URL_SLACK__",
|
"webhook_url": "__DISCORD_WEBHOOK_URL_SLACK__",
|
||||||
"channel": "#alerts",
|
"channel": "#alerts",
|
||||||
"custom_message": "*${event_definition_title}*\n${event_definition_description}\n${if backlog}${foreach backlog message}• `${message.source}`: ${message.message}\n${end}${end}",
|
"custom_message": "*${event_definition_title}*\n${event_definition_description}\n${if backlog}${foreach backlog message}• `${message.source}` (IP: ${message.fields.gl2_remote_ip}): ${message.message}\n${end}${end}",
|
||||||
"user_name": "Graylog",
|
"user_name": "Graylog",
|
||||||
"notify_channel": false,
|
"notify_channel": false,
|
||||||
"notify_here": false,
|
"notify_here": false,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ services:
|
||||||
- GRAYLOG_PASSWORD_SECRET=${GRAYLOG_PASSWORD_SECRET}
|
- GRAYLOG_PASSWORD_SECRET=${GRAYLOG_PASSWORD_SECRET}
|
||||||
- GRAYLOG_ROOT_PASSWORD_SHA2=${GRAYLOG_ROOT_PASSWORD_SHA2}
|
- GRAYLOG_ROOT_PASSWORD_SHA2=${GRAYLOG_ROOT_PASSWORD_SHA2}
|
||||||
- GRAYLOG_HTTP_EXTERNAL_URI=${GRAYLOG_HTTP_EXTERNAL_URI}
|
- GRAYLOG_HTTP_EXTERNAL_URI=${GRAYLOG_HTTP_EXTERNAL_URI}
|
||||||
|
- GRAYLOG_ROOT_TIMEZONE=${GRAYLOG_ROOT_TIMEZONE}
|
||||||
- GRAYLOG_MONGODB_URI=mongodb://mongodb:27017/graylog
|
- GRAYLOG_MONGODB_URI=mongodb://mongodb:27017/graylog
|
||||||
- GRAYLOG_ELASTICSEARCH_HOSTS=http://opensearch:9200
|
- GRAYLOG_ELASTICSEARCH_HOSTS=http://opensearch:9200
|
||||||
- "GRAYLOG_SERVER_JAVA_OPTS=-Xms1g -Xmx1g -XX:NewRatio=1 -server -XX:+UseG1GC"
|
- "GRAYLOG_SERVER_JAVA_OPTS=-Xms1g -Xmx1g -XX:NewRatio=1 -server -XX:+UseG1GC"
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,21 @@ step_compose_files() {
|
||||||
mkdir -p "$INSTALL_DIR"
|
mkdir -p "$INSTALL_DIR"
|
||||||
cp "$SCRIPT_DIR/docker-compose.yml" "$INSTALL_DIR/docker-compose.yml"
|
cp "$SCRIPT_DIR/docker-compose.yml" "$INSTALL_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
# Same timezone detection used by step_inputs() for Syslog input parsing.
|
||||||
|
# GRAYLOG_ROOT_TIMEZONE governs the display timezone of the built-in
|
||||||
|
# read-only "admin" user - it CANNOT be changed via the Users REST API
|
||||||
|
# (confirmed live: PUT /api/users/admin with a timezone field fails with
|
||||||
|
# "state should be: hexString has 24 characters"), only via this env var.
|
||||||
|
local tz
|
||||||
|
tz="$(cat /etc/timezone 2>/dev/null || echo UTC)"
|
||||||
|
|
||||||
if [ -f "$INSTALL_DIR/.env" ]; then
|
if [ -f "$INSTALL_DIR/.env" ]; then
|
||||||
skip ".env already exists, keeping existing secrets"
|
if grep -q '^GRAYLOG_ROOT_TIMEZONE=' "$INSTALL_DIR/.env"; then
|
||||||
|
skip ".env already exists, keeping existing secrets"
|
||||||
|
else
|
||||||
|
echo "GRAYLOG_ROOT_TIMEZONE=$tz" >> "$INSTALL_DIR/.env"
|
||||||
|
ok ".env existed but was missing GRAYLOG_ROOT_TIMEZONE - added it ($tz)"
|
||||||
|
fi
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
log "Generating fresh secrets into $INSTALL_DIR/.env ..."
|
log "Generating fresh secrets into $INSTALL_DIR/.env ..."
|
||||||
|
|
@ -115,6 +128,7 @@ step_compose_files() {
|
||||||
GRAYLOG_PASSWORD_SECRET=$secret
|
GRAYLOG_PASSWORD_SECRET=$secret
|
||||||
GRAYLOG_ROOT_PASSWORD_SHA2=$admin_sha2
|
GRAYLOG_ROOT_PASSWORD_SHA2=$admin_sha2
|
||||||
GRAYLOG_HTTP_EXTERNAL_URI=$GRAYLOG_EXTERNAL_URI
|
GRAYLOG_HTTP_EXTERNAL_URI=$GRAYLOG_EXTERNAL_URI
|
||||||
|
GRAYLOG_ROOT_TIMEZONE=$tz
|
||||||
EOF
|
EOF
|
||||||
chmod 600 "$INSTALL_DIR/.env"
|
chmod 600 "$INSTALL_DIR/.env"
|
||||||
|
|
||||||
|
|
@ -217,57 +231,55 @@ print(json.dumps(d))
|
||||||
|
|
||||||
step_inputs() {
|
step_inputs() {
|
||||||
log "Creating Syslog UDP inputs (idempotent)..."
|
log "Creating Syslog UDP inputs (idempotent)..."
|
||||||
local existing
|
local existing tz
|
||||||
existing="$(gcurl GET /system/inputs)"
|
existing="$(gcurl GET /system/inputs)"
|
||||||
|
|
||||||
|
# RFC3164 syslog (which is what most network gear/accel-ppp send) has no
|
||||||
|
# timezone in its timestamp ("Jul 22 09:15:13"). Without this setting,
|
||||||
|
# Graylog defaults to treating that bare timestamp as UTC - so a device
|
||||||
|
# logging in local Kyiv time (UTC+3) shows up 3 hours in the future.
|
||||||
|
# Confirmed live: a raw test packet with "Jul 22 09:15:13" was stored as
|
||||||
|
# 09:15:13Z UTC (wrong) until this was set; afterwards it correctly
|
||||||
|
# stored as 06:15:13Z UTC (09:15:13 Kyiv time). Uses the container's own
|
||||||
|
# configured timezone so it stays correct regardless of where this is
|
||||||
|
# deployed.
|
||||||
|
tz="$(cat /etc/timezone 2>/dev/null || echo UTC)"
|
||||||
|
|
||||||
|
ensure_syslog_input() {
|
||||||
|
local port="$1" title="$2" var_name="$3"
|
||||||
|
local id current_tz
|
||||||
|
id="$(echo "$existing" | python3 -c "import json,sys;d=json.load(sys.stdin);print(next((i['id'] for i in d['inputs'] if i['attributes'].get('port')==$port),''))")"
|
||||||
|
local body="{
|
||||||
|
\"title\": \"$title\",
|
||||||
|
\"type\": \"org.graylog2.inputs.syslog.udp.SyslogUDPInput\",
|
||||||
|
\"global\": true,
|
||||||
|
\"configuration\": {\"bind_address\":\"0.0.0.0\",\"port\":$port,\"recv_buffer_size\":262144,
|
||||||
|
\"number_worker_threads\":2,\"force_rdns\":false,\"allow_override_date\":true,
|
||||||
|
\"store_full_message\":false,\"expand_structured_data\":true,\"charset_name\":\"UTF-8\",
|
||||||
|
\"timezone\":\"$tz\"}
|
||||||
|
}"
|
||||||
|
if [ -z "$id" ]; then
|
||||||
|
id="$(gcurl POST /system/inputs "$body" | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")"
|
||||||
|
ok "Created $title: $id"
|
||||||
|
else
|
||||||
|
current_tz="$(echo "$existing" | python3 -c "import json,sys;d=json.load(sys.stdin);print(next((i['attributes'].get('timezone') for i in d['inputs'] if i['attributes'].get('port')==$port),''))")"
|
||||||
|
if [ "$current_tz" != "$tz" ]; then
|
||||||
|
gcurl PUT "/system/inputs/$id" "$body" >/dev/null
|
||||||
|
ok "$title already existed - fixed timezone ($current_tz -> $tz)"
|
||||||
|
else
|
||||||
|
skip "$title already exists: $id"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
printf -v "$var_name" '%s' "$id"
|
||||||
|
}
|
||||||
|
|
||||||
# Port 514 is the standard syslog port and the one most network gear
|
# Port 514 is the standard syslog port and the one most network gear
|
||||||
# actually sends to (confirmed live: BDCOM switches here can't be pointed
|
# actually sends to (confirmed live: BDCOM switches here can't be pointed
|
||||||
# at a custom port). Port 1514 is kept as a secondary input for any
|
# at a custom port). Port 1514 is kept as a secondary input for any
|
||||||
# equipment that *can* be configured with a non-standard destination port.
|
# equipment that *can* be configured with a non-standard destination port.
|
||||||
NETWORK_INPUT_ID_514="$(echo "$existing" | python3 -c "import json,sys;d=json.load(sys.stdin);print(next((i['id'] for i in d['inputs'] if i['attributes'].get('port')==514),''))")"
|
ensure_syslog_input 514 "Network Equipment Syslog (standard port 514)" NETWORK_INPUT_ID_514
|
||||||
if [ -z "$NETWORK_INPUT_ID_514" ]; then
|
ensure_syslog_input 1514 "Network Equipment Syslog (Juniper-ZTE-DLink)" NETWORK_INPUT_ID_1514
|
||||||
NETWORK_INPUT_ID_514="$(gcurl POST /system/inputs '{
|
ensure_syslog_input 5140 "Servers Syslog (RADIUS-accel-ppp)" SERVERS_INPUT_ID
|
||||||
"title": "Network Equipment Syslog (standard port 514)",
|
|
||||||
"type": "org.graylog2.inputs.syslog.udp.SyslogUDPInput",
|
|
||||||
"configuration": {"bind_address":"0.0.0.0","port":514,"recv_buffer_size":262144,
|
|
||||||
"number_worker_threads":2,"force_rdns":false,"allow_override_date":true,
|
|
||||||
"store_full_message":false,"expand_structured_data":true,"charset_name":"UTF-8"},
|
|
||||||
"global": true
|
|
||||||
}' | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")"
|
|
||||||
ok "Created Network Equipment input (514): $NETWORK_INPUT_ID_514"
|
|
||||||
else
|
|
||||||
skip "Network Equipment input (514) already exists: $NETWORK_INPUT_ID_514"
|
|
||||||
fi
|
|
||||||
|
|
||||||
NETWORK_INPUT_ID_1514="$(echo "$existing" | python3 -c "import json,sys;d=json.load(sys.stdin);print(next((i['id'] for i in d['inputs'] if i['attributes'].get('port')==1514),''))")"
|
|
||||||
if [ -z "$NETWORK_INPUT_ID_1514" ]; then
|
|
||||||
NETWORK_INPUT_ID_1514="$(gcurl POST /system/inputs '{
|
|
||||||
"title": "Network Equipment Syslog (Juniper-ZTE-DLink)",
|
|
||||||
"type": "org.graylog2.inputs.syslog.udp.SyslogUDPInput",
|
|
||||||
"configuration": {"bind_address":"0.0.0.0","port":1514,"recv_buffer_size":262144,
|
|
||||||
"number_worker_threads":2,"force_rdns":false,"allow_override_date":true,
|
|
||||||
"store_full_message":false,"expand_structured_data":true,"charset_name":"UTF-8"},
|
|
||||||
"global": true
|
|
||||||
}' | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")"
|
|
||||||
ok "Created Network Equipment input (1514): $NETWORK_INPUT_ID_1514"
|
|
||||||
else
|
|
||||||
skip "Network Equipment input (1514) already exists: $NETWORK_INPUT_ID_1514"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SERVERS_INPUT_ID="$(echo "$existing" | python3 -c "import json,sys;d=json.load(sys.stdin);print(next((i['id'] for i in d['inputs'] if i['attributes'].get('port')==5140),''))")"
|
|
||||||
if [ -z "$SERVERS_INPUT_ID" ]; then
|
|
||||||
SERVERS_INPUT_ID="$(gcurl POST /system/inputs '{
|
|
||||||
"title": "Servers Syslog (RADIUS-accel-ppp)",
|
|
||||||
"type": "org.graylog2.inputs.syslog.udp.SyslogUDPInput",
|
|
||||||
"configuration": {"bind_address":"0.0.0.0","port":5140,"recv_buffer_size":262144,
|
|
||||||
"number_worker_threads":2,"force_rdns":false,"allow_override_date":true,
|
|
||||||
"store_full_message":false,"expand_structured_data":true,"charset_name":"UTF-8"},
|
|
||||||
"global": true
|
|
||||||
}' | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")"
|
|
||||||
ok "Created Servers input: $SERVERS_INPUT_ID"
|
|
||||||
else
|
|
||||||
skip "Servers input already exists: $SERVERS_INPUT_ID"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
step_pipeline_rules() {
|
step_pipeline_rules() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"title": "Network Equipment Parsing",
|
"title": "Network Equipment Parsing",
|
||||||
"description": "Parses Juniper and BDCOM/OLT-style syslog, plus universal critical-severity fallback",
|
"description": "Parses Juniper and BDCOM/OLT-style syslog, plus universal critical-severity fallback",
|
||||||
"source": "pipeline \"Network Equipment Parsing\"\nstage 0 match either\n rule \"juniper_ntp_unreachable\";\n rule \"juniper_ssh_login_success\";\n rule \"juniper_ssh_login_failed_tagged\";\n rule \"juniper_ssh_failed_password\";\n rule \"juniper_ui_configuration_error\";\n rule \"olt_privilege_mode\";\n rule \"olt_cli_logout\";\n rule \"olt_ip_arp_moved\";\n rule \"olt_config_write\";\n rule \"generic_critical_severity\";\nend"
|
"source": "pipeline \"Network Equipment Parsing\"\nstage 0 match either\n rule \"juniper_ntp_unreachable\";\n rule \"juniper_ssh_login_success\";\n rule \"juniper_ssh_login_failed_tagged\";\n rule \"juniper_ssh_failed_password\";\n rule \"juniper_ui_configuration_error\";\n rule \"juniper_chassis_alarm\";\n rule \"olt_privilege_mode\";\n rule \"olt_cli_logout\";\n rule \"olt_ip_arp_moved\";\n rule \"olt_config_write\";\n rule \"generic_critical_severity\";\nend"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
rules/rule19_juniper_chassis_alarm.json
Normal file
5
rules/rule19_juniper_chassis_alarm.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"title": "juniper_chassis_alarm",
|
||||||
|
"description": "Juniper chassisd SNMP trap (hardware health: over temperature, fan, power supply, etc.) - CHASSISD_SNMP_TRAP6",
|
||||||
|
"source": "rule \"juniper_chassis_alarm\"\nwhen\n contains(to_string($message.message), \"CHASSISD_SNMP_TRAP\")\nthen\n set_field(\"vendor\", \"juniper\");\n set_field(\"event_type\", \"chassis_alarm\");\n set_field(\"severity_tag\", \"critical\");\n let m = regex(\"SNMP trap generated: ([^(]+?)\\\\s*\\\\(\", to_string($message.message), [\"alarm\"]);\n set_field(\"chassis_alarm_type\", m[\"alarm\"]);\n let d = regex(\"jnxContentsDescr ([^,]+)\", to_string($message.message), [\"descr\"]);\n set_field(\"chassis_component\", d[\"descr\"]);\nend"
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue