Commit graph

13 commits

Author SHA1 Message Date
d76f1e766b Fix the same update-gap bug in step_streams()/step_dashboard() as rules/pipelines/alerts
Both only checked title existence, so editing a stream's rules or a
dashboard's widgets under the same title silently skipped the update
forever. step_dashboard() now diffs the search's queries content
(delete+recreate view+search on change - no subscribers/history to lose,
unlike alerts or streams); step_streams() diffs rules individually by
(field,type,value,inverted) and PUT-updates metadata in place, since
delete+recreating a stream would briefly break live message routing.

Confirmed live on a local test deployment: a false "changed" positive
from search_types list-reordering and from search_servers.json's own
query-format inconsistency (bare string vs the {query_string,type} object
Graylog actually stores) had to be fixed before step_dashboard() settled
into a stable no-op; step_streams() was verified with a full inert
rule-add/remove and a metadata-change round-trip against the live API.
2026-07-23 21:00:06 +03:00
32f2a922e8 Normalize list order before comparing alert config - fixes flip-flop update
Confirmed live: "CRITICAL: unrecognized critical-severity syslog" (the
only alert with a 2-element streams list) kept reporting "config changed"
every single run even with no actual edit, because a plain dict/list ==
comparison in Python is order-sensitive and Graylog doesn't necessarily
return the streams array in the same order it was submitted in. List
values are now sorted before comparing on both sides, since list order
was never semantically meaningful here. Verified locally against a
same-elements-different-order case (now matches) and a genuinely-changed
case (still correctly detected as different) before pushing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:37:27 +03:00
19643a74dd Fix false-positive "config changed" on every alert, every run
The previous fix compared the whole live 'config' object against the
whole desired one - but Graylog's GET response fills in extra defaults
(query_parameters, filters, use_cron_scheduling, cron_expression,
cron_timezone, ...) that never appear in our alert JSON files. That made
every single alert compare as "different" on every single run, not just
the one actually edited - confirmed live: a re-run PUT-updated all 7
alerts when only alert7's query had changed.

Fixed by comparing only the keys our own files actually author (project
the live config down to just those keys before comparing), and switched
from interpolating JSON into python -c string literals to writing to temp
files and reading them - the former is fragile the moment a value contains
a quote or backslash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:34:32 +03:00
c1361f16c9 Fix the same update-gap bug in step_alerts() - alerts weren't updating either
Same class of bug just fixed in step_pipeline_rules()/step_pipelines():
title-only existence check meant an alert's config (query, threshold,
group_by) could never actually change on a re-run once created. Confirmed
live: alert7's query was corrected in git (vendor:accel-ppp ->
vendor:accel-ppp AND event_type:*) but a subsequent re-run still reported
"already exists" with the old, buggy query untouched. Now compares by the
'config' object (not just title) and PUT-updates + re-enables the
schedule if it differs, same pattern as the other two fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:30:43 +03:00
609ac74574 Fix two bugs found live: pipeline/rule update gap, and alert7's over-broad query
- step_pipeline_rules() and step_pipelines() only ever created new
  rules/pipelines by title, never updated existing ones whose content
  changed under the same title. Confirmed live this session: 5 new rules
  were added to rules/*.json and created fine in Graylog, but "Servers
  Parsing"'s stage list was never updated to actually call them, since
  the pipeline already existed. Both functions now compare by 'source'
  content and PUT-update if it differs, matching the pattern already used
  elsewhere (e.g. step_inputs()'s timezone self-heal).
- alert7's query (vendor:accel-ppp) was too broad: it also matches
  routine traffic tagged only by the generic accelppp_interface_tag
  fallback rule, which sets vendor but never event_type - producing false
  "(Empty Value)" group_by matches on ordinary volume. Confirmed live.
  Fixed to vendor:accel-ppp AND event_type:* - severity_tag was
  considered instead but rejected, since accelppp_router_address_error
  (the rule that inspired this alert) never set it either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 16:28:50 +03:00
eb6225f2f8 Let GRAYLOG_ADMIN_PASSWORD pin a custom password, add cores input to CI
- step_compose_files() now uses GRAYLOG_ADMIN_PASSWORD as the actual admin
  password on a fresh install if set, instead of always generating a
  random one. Solves the CI secret-staleness problem at the root: pin a
  password once and it's correct both at creation time and on every later
  re-run, instead of a fresh install randomly generating a password the
  stored secret then has to be manually kept in sync with.
- deploy-from-scratch.yml gained a `cores` workflow_dispatch input
  (default 4), passed through to create-graylog-lxc.sh's --cores flag.
  Only takes effect when the container is actually created fresh, same as
  every other --cores usage in this project.
- Documented both in README.md/README.uk.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 03:37:03 +03:00
5316136eb6 Fix real root cause: password-extraction regex matched a second "password: "
Introduced by an earlier edit to .admin_credentials_ONE_TIME's own
instructional text ("...you've stored the password: rm <path>"), which
itself contains "password: " - the same substring resolve_admin_password()
greps for. grep -oP matched BOTH occurrences; command substitution joined
them with a real newline, producing a corrupted two-line "password" that
never matched .env's GRAYLOG_ROOT_PASSWORD_SHA2, causing every gcurl call
to silently 401 and wait_for_api_ready to time out no matter how generous
the timeout was (confirmed by re-verifying live: the real password,
extracted correctly, hashes to exactly what .env already has).

Fixed both ends: reworded the instructional text to not repeat "password:",
and hardened the regex itself (^Graylog admin password: anchor, grep -m1)
so a future wording change can't reintroduce the same class of bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 23:13:09 +03:00
9cdef5d521 Bump wait_for_api_ready timeout from 60s to 180s
Confirmed live: on a CI-triggered run, the REST API genuinely took longer
than 60s to accept authenticated requests even though the container had
already reported healthy - verified after the fact that both the API and
the admin credentials were fine, this was purely insufficient margin
(likely due to concurrent load during the run), not a logic bug. 180s
matches the same order of magnitude as the existing 300s docker-health
wait elsewhere in this script.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 23:05:52 +03:00
63e6ce88d0 Fix startup race: wait for the REST API before the first authenticated call
Docker's healthcheck can report graylog-server "healthy" a few seconds
before the REST API is actually ready to serve authenticated requests -
confirmed live: the first gcurl call (step_index_retention) intermittently
got an empty response body, crashing the downstream `python3 -c
"json.load(sys.stdin)"` with "Expecting value: line 1 column 1".

Adds wait_for_api_ready(), polling the same endpoint step_index_retention
already needs (up to 60s) before proceeding, mirroring the existing
retry-loop pattern already used for docker compose pull.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 22:45:28 +03:00
83a3388d41 Silence locale warnings and auto-display/delete the one-time admin password
- export LC_ALL=C.UTF-8 LANG=C.UTF-8 at the top of the script: the
  container inherits LANG=en_US.UTF-8 from pct exec's calling shell but
  never generates that locale, so every apt-get call printed "Setting
  locale failed" warnings from perl/apt-listchanges. C.UTF-8 is glibc-
  builtin, no locale-gen needed.
- The final summary now prints the generated admin user/password directly
  and deletes .admin_credentials_ONE_TIME right after, instead of just
  pointing at the file and leaving it for the operator to read and clean
  up by hand.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 22:29:31 +03:00
510ca37e48 Add flood alerts, session correlation, focused dashboards, and CI deploy workflow
- Two flood-detection alerts (per-source message volume, calibrated live
  against real traffic) grouped by gl2_remote_ip
- Session correlation: accelppp_interface fallback tagging plus
  radius_session_id/calling_station_id/radius_username extraction, so a
  subscriber's full session lifecycle is searchable by one key
- Replace the single combined dashboard with three focused ones (Overview
  & Alerts, Network Equipment, Servers & Sessions)
- Propagate GRAYLOG_ROOT_TIMEZONE and IP-in-alerts fixes into the reusable
  install script and templates
- Add a Forgejo Actions workflow (manual trigger) that re-runs
  install-graylog.sh on a self-hosted runner living in the container,
  automating the deploy step this project has done by hand all along

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 21:31:08 +03:00
0db85fa860 feat: add Juniper chassis alarm alerting, Discord notifications, and global timezone configuration for accurate syslog timestamp parsing 2026-07-22 18:48:26 +03:00
06233065cb feat: add automated deployment scripts and monitoring configuration for Graylog infrastructure 2026-07-22 04:52:27 +03:00