Update ssh-login-alerting.md via n8n
This commit is contained in:
parent
dda6eac571
commit
bdcc51b9aa
@ -7,13 +7,13 @@ tags:
|
|||||||
- pbs
|
- pbs
|
||||||
- security
|
- security
|
||||||
- ssh
|
- ssh
|
||||||
- auditd
|
- journald
|
||||||
|
- python
|
||||||
- n8n
|
- n8n
|
||||||
- monitoring
|
- monitoring
|
||||||
created: 2026-04-19
|
created: 2026-04-19
|
||||||
updated: 2026-04-19
|
updated: 2026-04-21
|
||||||
---
|
---
|
||||||
|
|
||||||
# SSH Login Alerting
|
# SSH Login Alerting
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
@ -25,24 +25,39 @@ key-based account.
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
|
||||||
SSH login → auditd captures event → trigger script parses it →
|
sshd → journald → Python sdjournal tailer → POST to n8n webhook →
|
||||||
curl POST to n8n webhook → n8n formats + sends to Google Chat
|
n8n formats + sends to Google Chat
|
||||||
```
|
|
||||||
|
|
||||||
## Design Decisions
|
## Design Decisions
|
||||||
|
|
||||||
- **auditd over log-parsing** — kernel-level event capture, more reliable
|
- **journald over auditd** — v1 attempted `auditd → shell dispatcher →
|
||||||
than tailing `/var/log/auth.log`.
|
regex → webhook` and was abandoned after silent event drops and two hours
|
||||||
- **Shell script as middleman** — dumb, single-purpose: parse auditd event,
|
of failed debugging. Root cause: auditd emits multiple records per logical
|
||||||
POST to n8n webhook. No logic in the script beyond formatting.
|
event with format variations that break line-by-line regex parsing.
|
||||||
- **n8n handles notification routing** — keeps all alert
|
Journald is one-line-per-event with structured metadata
|
||||||
formatting/delivery centralized where other PBS notifications already live.
|
(`_SYSTEMD_UNIT=ssh.service`), which eliminates the multi-record coalescing
|
||||||
Server just fires events; n8n decides what to do with them.
|
problem entirely.
|
||||||
- **Alert on every login for v1** — simpler than trying to define
|
- **Python sdjournal tailer, not pam_exec** — pam_exec would give fully
|
||||||
"unexpected" up front. Tune later if noise becomes a problem.
|
structured data with zero regex via PAM env vars (`PAM_USER`, `PAM_RHOST`,
|
||||||
- **Scope: all accounts, all PBS servers** — staging and production, every
|
etc.), but touches the SSH auth path. Explicit tradeoff: accept one
|
||||||
user (`pbsdeploy`, admin, root, etc.).
|
remaining regex pattern in exchange for zero risk to login infrastructure.
|
||||||
|
If the tailer crashes, SSH keeps working.
|
||||||
|
- **Cursor persistence solves the silent-drop problem** —
|
||||||
|
`systemd.journal.Reader.seek_cursor()` with cursor saved to disk after each
|
||||||
|
successful webhook POST gives exactly-once delivery across restarts,
|
||||||
|
crashes, and n8n outages. This was the actual failure mode of v1.
|
||||||
|
- **Regex is contained and testable** — one pattern against sshd's
|
||||||
|
`MESSAGE` field (stable across OpenSSH versions for ~10 years), in one
|
||||||
|
Python function, with unit tests against fixture strings. When it breaks,
|
||||||
|
fix is one line in one place.
|
||||||
|
- **n8n handles notification routing** — unchanged from v1. Server fires
|
||||||
|
events; n8n formats and delivers.
|
||||||
|
- **Alert on every login for v1** — unchanged. Tune later if noise becomes
|
||||||
|
a problem.
|
||||||
|
- **Scope: all accounts, all PBS servers** — unchanged. Staging and
|
||||||
|
production, every user.
|
||||||
|
|
||||||
## Why This Matters
|
## Why This Matters
|
||||||
|
|
||||||
@ -54,26 +69,42 @@ main threat vector.
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- [ ] Configure auditd rules on staging server to capture SSH login events
|
- [ ] Decide deployment model: host systemd service vs containerized
|
||||||
- [ ] Write shell script that parses auditd events and POSTs to n8n webhook
|
(leaning host systemd — simpler, more reliable for journal access)
|
||||||
|
- [ ] Write Python tailer using `systemd.journal.Reader` with
|
||||||
|
`add_match(_SYSTEMD_UNIT="ssh.service")`, cursor persistence, and HTTP POST
|
||||||
|
to n8n
|
||||||
|
- [ ] Unit test the MESSAGE regex against fixture strings (accepted
|
||||||
|
publickey, accepted password, failed attempts, invalid user)
|
||||||
|
- [ ] Deploy to staging, test cursor behavior across restarts
|
||||||
- [ ] Build n8n workflow: webhook trigger → format message → send to Google
|
- [ ] Build n8n workflow: webhook trigger → format message → send to Google
|
||||||
Chat
|
Chat
|
||||||
- [ ] Test end-to-end on staging (SSH in, confirm alert arrives)
|
- [ ] End-to-end test on staging (SSH in, kill tailer, SSH in again,
|
||||||
- [ ] Roll out to production
|
restart tailer, confirm replay)
|
||||||
- [ ] Decide on Chat space (webadmin? dedicated security space?)
|
- [ ] Add Uptime Kuma monitor for the tailer service
|
||||||
|
- [ ] Roll out to production via Ansible
|
||||||
|
- [ ] Decommission v1 auditd dispatcher
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
- Which Google Chat space should alerts go to?
|
- Host systemd service or containerized? Leaning host service — touches
|
||||||
- Do we want any metadata in the alert beyond username + source IP +
|
host-level journal, simpler bind-mount story. Breaks Compose aesthetic but
|
||||||
timestamp (e.g., geolocation lookup on source IP)?
|
fits the job.
|
||||||
- Do we want a separate "critical" alert path for root logins?
|
- Where does cursor state live? (Path, ownership, permissions — probably
|
||||||
|
`/var/lib/ssh-login-alerter/cursor`.)
|
||||||
|
- Which Google Chat space — `webadmin` or a dedicated security space?
|
||||||
|
- Metadata beyond username + source IP + timestamp (e.g., geolocation on
|
||||||
|
source IP)?
|
||||||
|
- Separate "critical" alert path for root logins?
|
||||||
|
- How does Uptime Kuma know the tailer is alive? Heartbeat ping from the
|
||||||
|
tailer itself, or process check?
|
||||||
|
|
||||||
## Priority
|
## Priority
|
||||||
|
|
||||||
Higher priority than env file hardening — it's a force multiplier. Even if
|
Still higher priority than env file hardening. v1's silent drops mean we
|
||||||
the env hardening isn't done yet, knowing about an unauthorized login
|
currently have *no* working alert on the `pbsdeploy` account — the
|
||||||
immediately limits blast radius.
|
force-multiplier argument is sharper now than it was at project inception.
|
||||||
|
|
||||||
|
...sent from Jenny & Travis
|
||||||
|
|
||||||
...sent from Jenny & Travis
|
...sent from Jenny & Travis
|
||||||
Loading…
Reference in New Issue
Block a user