crowdsec: detect vaultwarden and navidrome native-login brute force #294

Merged
mysticalsoap merged 2 commits from crowdsec-native-logins into trunk 2026-09-20 01:06:44 -04:00
Owner

Problem

Vaultwarden and Navidrome authenticate users themselves behind an Authelia bypass, and CrowdSec had no log source for either, so a credential-guessing run only ever met each app's own rate limit (#121). Navidrome's Subsonic API has no rate limit at all and answers a bad password with HTTP 200, so the Traefik access log can't see it either.

Fix

  • Both hub collections (Dominic-Wagner/vaultwarden, sdwilsh/navidrome), declared in COLLECTIONS together with the four collections that had only ever been installed by hand.

  • Each app logs to /var/log/<app>, bind-mounted read-only into crowdsec; logrotate rules (HUP reopen for Vaultwarden, copytruncate for Navidrome, which treats SIGHUP as shutdown); just normalize creates the dirs.

  • local/navidrome-auth (s02-enrich): strips the port from Navidrome's remoteAddr so the private-range whitelist can match it, and tags Subsonic API: Invalid login lines with the log_type the hub scenario keys on.

  • Docs: CrowdSec section rewritten to point at COLLECTIONS and acquis.yaml, plus the Navidrome specifics and a first-start gotcha.

  • ND_EXTAUTH_TRUSTEDSOURCES (the media subnet, Traefik's address there is dynamic) so Navidrome logs the client instead of Traefik. The same setting enables Navidrome's header login: Traefik's forwardAuth strips Remote-User from client requests and Authelia sets it for a session holder even under bypass, so a signed-in browser lands in Navidrome by username (created if missing; knock keeps the names aligned). Subsonic clients, Feishin's web build included (credentials: same-origin), keep using the Navidrome password. User docs note the removal order (LLDAP first).

Verification

  • cscli explain on captured lines: the Vaultwarden failed-login line reaches vaultwarden-bf and vaultwarden-bf_user-enum; Navidrome Subsonic and web-401 lines with a public IP reach navidrome-bf; the same lines with Traefik's ip:port get the port stripped and are whitelisted.
  • Live: just normalize, logrotate rules installed, just up infra vaultwarden media, one bad login per app. cscli metrics shows both files read, the failed-login lines parsed, then whitelisted (LAN source). With the trust setting deployed, Navidrome's remoteAddr equals Traefik's ClientHost for the same request, bare IP, no port. A bucket overflow can't be produced from the LAN, so that part rests on explain.
  • Vaultwarden keeps logging to stdout with LOG_FILE set; docker logs navidrome goes quiet by design.
  • Gotcha hit and documented: CrowdSec logged No matching files for pattern and never picked up the files created after it started; one restart after the first deploy fixed it.
  • _helper/ci/check-compose.sh green.
## Problem Vaultwarden and Navidrome authenticate users themselves behind an Authelia bypass, and CrowdSec had no log source for either, so a credential-guessing run only ever met each app's own rate limit (#121). Navidrome's Subsonic API has no rate limit at all and answers a bad password with HTTP 200, so the Traefik access log can't see it either. ## Fix - Both hub collections (`Dominic-Wagner/vaultwarden`, `sdwilsh/navidrome`), declared in `COLLECTIONS` together with the four collections that had only ever been installed by hand. - Each app logs to `/var/log/<app>`, bind-mounted read-only into `crowdsec`; logrotate rules (`HUP` reopen for Vaultwarden, `copytruncate` for Navidrome, which treats `SIGHUP` as shutdown); `just normalize` creates the dirs. - `local/navidrome-auth` (s02-enrich): strips the port from Navidrome's `remoteAddr` so the private-range whitelist can match it, and tags Subsonic `API: Invalid login` lines with the `log_type` the hub scenario keys on. - Docs: CrowdSec section rewritten to point at `COLLECTIONS` and `acquis.yaml`, plus the Navidrome specifics and a first-start gotcha. - `ND_EXTAUTH_TRUSTEDSOURCES` (the media subnet, Traefik's address there is dynamic) so Navidrome logs the client instead of Traefik. The same setting enables Navidrome's header login: Traefik's forwardAuth strips `Remote-User` from client requests and Authelia sets it for a session holder even under `bypass`, so a signed-in browser lands in Navidrome by username (created if missing; knock keeps the names aligned). Subsonic clients, Feishin's web build included (`credentials: same-origin`), keep using the Navidrome password. User docs note the removal order (LLDAP first). ## Verification - `cscli explain` on captured lines: the Vaultwarden failed-login line reaches `vaultwarden-bf` and `vaultwarden-bf_user-enum`; Navidrome Subsonic and web-401 lines with a public IP reach `navidrome-bf`; the same lines with Traefik's `ip:port` get the port stripped and are whitelisted. - Live: `just normalize`, logrotate rules installed, `just up infra vaultwarden media`, one bad login per app. `cscli metrics` shows both files read, the failed-login lines parsed, then whitelisted (LAN source). With the trust setting deployed, Navidrome's `remoteAddr` equals Traefik's `ClientHost` for the same request, bare IP, no port. A bucket overflow can't be produced from the LAN, so that part rests on explain. - Vaultwarden keeps logging to stdout with `LOG_FILE` set; `docker logs navidrome` goes quiet by design. - Gotcha hit and documented: CrowdSec logged `No matching files for pattern` and never picked up the files created after it started; one restart after the first deploy fixed it. - `_helper/ci/check-compose.sh` green.
crowdsec: detect vaultwarden and navidrome native-login brute force
All checks were successful
validate / validate (pull_request) Successful in 20s
92865dfea8
Both apps sit behind an Authelia bypass, so a wrong password never
reaches Authelia's log or metrics; each app's own rate limit was the
only thing in the way. The hub has a collection for each and the live
log lines match their parsers as-is.

Logs go to /var/log/<app> like jellyfin and authelia rather than a file
inside the data dir like calibre-web: a single-file bind keeps the
inode, so the first rotation would leave crowdsec tailing the old file.
Both dirs need `just normalize` once on the host.

Vaultwarden keeps stdout when LOG_FILE is set and reopens on SIGHUP.
Navidrome's ND_LOGFILE replaces stderr and SIGHUP is its shutdown
signal, hence copytruncate.

The hub navidrome parser only flags the web login, which the traefik
log already covers via http-generic-401-bf. A Subsonic failure is a 200
with an error body and skips Navidrome's rate limiter, so a local
s02-enrich parser tags that line for the hub scenario.

COLLECTIONS now also lists the four collections that were installed by
hand (authelia, jellyfin, jellyseerr, calibre-web) so the compose file
is the record; the entrypoint skips the locally patched jellyseerr one.

Refs #121

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
media: trust traefik as navidrome's reverse proxy
All checks were successful
validate / validate (pull_request) Successful in 24s
51f99e2bdf
Without ExtAuth.TrustedSources Navidrome logs every request as coming
from Traefik's media-network address, so the failed-login source would
attribute every guess to a private IP the whitelist drops. The media
subnet rather than Traefik's address: it is dynamically assigned.

The same switch turns on Remote-User header login from trusted peers.
Traefik's forwardAuth strips it from client requests, and Authelia sets
it for anyone holding a session even on a bypass domain, so an Authelia
session now also signs into Navidrome, creating the account on first
visit. See Auth-and-Security.md § CrowdSec.

Refs #121

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mysticalsoap deleted branch crowdsec-native-logins 2026-09-20 01:06:44 -04:00
Sign in to join this conversation.
No description provided.