Files
lab-ops/running_log.md

8.9 KiB

Running Log

Most recent entries at the top, oldest at the bottom. Each entry separated by a horizontal rule.


2026-09-03 — CrowdSec down after reboot + long-standing firewall-bouncer auth failure (Net-Controller)

Symptom: CrowdSec showing "Down" in Uptime Kuma after the earlier full host reboot (same reboot that broke mailcow reachability, see entry below).

Issue 1 — CrowdSec engine container not running

Investigation:

  • docker ps -a showed crowdsec container Exited (0) ~17 hours prior — a clean shutdown (SIGTERM received, shutting down), not a crash.
  • docker inspect crowdsec confirmed exit code 0, no OOM kill, no error — it simply received the reboot's shutdown signal and never came back.
  • Root cause: docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' showed RestartPolicy: no — unlike the rest of the stack, this container had no auto-restart policy configured, so it stayed down after the reboot instead of self-healing like every other service on the host.
  • Container's compose label pointed to /data/compose/14, which no longer exists on disk (stale label from original provisioning, likely Coolify/Portainer-managed at some point) — not pursued further since fixing the running container directly was simpler and safer.

Fix:

  • docker start crowdsec
  • docker update --restart unless-stopped crowdsec — ensures it now survives future reboots automatically.
  • Verified: container came up clean, re-downloaded/updated its hub (parsers, scenarios, collections — flagged one outdated collection crowdsecurity/http-cve, auto-updated to CVE-2022-44877 v0.4), started processing normally, confirmed pulling from CrowdSec Central API.

Issue 2 — firewall-bouncer crash-looping since ~Aug 18 (pre-existing, unrelated to reboot)

Discovery: While verifying CrowdSec's engine logs, noticed the crowdsec-firewall-bouncer client was receiving 403 Forbidden on every single /v1/decisions/stream poll (every 10s), both before and after the reboot — meaning this was a standing issue, not caused by tonight's outage.

Investigation:

  • The bouncer is not a docker container — it's a host-level systemd service: crowdsec-firewall-bouncer.service (installed via crowdsec-firewall-bouncer-iptables package, config at /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml).
  • systemctl status showed it activating (auto-restart) — crash-looping continuously.
  • journalctl -u crowdsec-firewall-bouncer.service showed the same fatal error on every cycle: level=fatal msg="process terminated with error: bouncer stream halted", immediately followed by systemd restarting it (10s backoff).
  • Scale of the problem: restart counter was at 5,829 by the time this was caught, with log rotation files dating back to Aug 18, 2026 — meaning this bouncer had been non-functional (crashing every ~10s) for roughly 2.5 weeks before discovery.
  • Root cause confirmed via docker exec crowdsec cscli bouncers list on the engine side — zero bouncers registered. The API key configured in the bouncer's yaml (api_key: MRPnN0XqxrVOspREia3c4jvRYfi+/Cr/9bUKAcxdoWg) did not correspond to any bouncer known to the CrowdSec engine (likely invalidated by a prior CrowdSec engine reset/reinstall, or the bouncer was never properly registered after an update).
  • Security impact: with the bouncer unable to authenticate, ban decisions from CrowdSec's engine (local detections + CrowdSec Central API community blocklist) were never being enforced at the firewall (nftables) layer for the ~2.5 week window. CrowdSec's detection/logging continued working the whole time (it runs independently), but actual IP blocking was not happening.

Fix:

  1. Generated a fresh bouncer API key on the engine: docker exec crowdsec cscli bouncers add firewall-bouncer-netcontroller → new key DkIEnS1TfGfly49gAAb4cbn1T6/+EZBVSEFCoEojt0M.
  2. Backed up the existing config: /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml.bak.<timestamp>.
  3. Replaced the api_key: line in /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml with the new key via sed.
  4. sudo systemctl restart crowdsec-firewall-bouncer.service.

Verification:

  • systemctl is-active → active and stable (previously dying in under a second on every cycle; now running continuously with no restarts).
  • cscli bouncers list on the engine now shows firewall-bouncer-netcontroller with Valid: ✔️, IP 172.18.0.1, auth type api-key, and a fresh Last API pull timestamp.
  • CrowdSec engine logs confirmed the bouncer's /v1/decisions/stream polls now return HTTP 200 (was 403) every 10s as expected.
  • Confirmed actual enforcement is live: sudo nft list ruleset shows the ip crowdsec and ip6 crowdsec6 tables (matching the nftables: config block — note: NOT under the inet family, took one wrong guess before finding the right table family) with active, incrementing packet/byte counters on the block chains (17,990+ packets processed at time of check).

Takeaways for next time:

  • CrowdSec's own container needed an explicit RestartPolicy fix (unless-stopped) that most of the other stack containers already had — worth spot-checking other standalone/non-compose-managed containers on this host for the same gap.
  • The firewall-bouncer is a host-level systemd service, not a docker container — easy to overlook when doing container-focused health checks (docker ps alone won't show it). Add it to the regular health-check rotation going forward.
  • Bouncer API keys are one-time-display (cscli bouncers add only shows the key once) and are independent of the CrowdSec engine's own restarts — if the engine's bouncer registry is ever wiped/reset, all bouncer services depending on old keys will silently start failing until manually re-registered. Consider a monitoring check on cscli bouncers list validity or on the bouncer's own systemd restart count as an early-warning signal for this specific failure mode.

2026-09-03 — Mailcow unreachable after full host reboot (Net-Controller)

Symptom: After a full reboot of Net-Controller, mail.infernalaquatics.com was unreachable externally (timeouts on both HTTP and HTTPS), despite all mailcow containers reporting healthy.

Investigation:

  • Confirmed all 18 mailcowdockerized-* containers up and healthy post-reboot; mail ports (25/465/587/143/993/110/995) listening on host.
  • Mailcow's nginx web UI (port 8087) responded HTTP 200 locally on the host, but NPM (Nginx Proxy Manager) could not reach it — requests timed out.
  • Found mailcow's built-in network isolation firewall rule (MAILCOW chain in iptables FORWARD) drops any forwarded traffic not entering via the br-mailcow bridge. NPM lives on a separate docker bridge, so its proxied requests to 192.168.1.3:8087 (the LAN IP) were being silently dropped by this isolation rule after the reboot re-applied it.

Fix (Option 1 — network attach, no firewall changes):

  • Attached the npm container to mailcowdockerized_mailcow-network (docker network connect).
  • Updated the mail.infernalaquatics.com proxy host's Forward Hostname/IP in NPM from 192.168.1.3 to the mailcow nginx container name (mailcowdockerized-nginx-mailcow-1), keeping port 8087, so traffic routes container-to-container instead of crossing the isolated LAN bridge.

Regression hit during the fix:

  • Attaching NPM to a second docker network flipped its default outbound route to the mailcow bridge gateway (no internet access), breaking NPM's own outbound connectivity (Let's Encrypt renewals, IP-range fetches all started failing with "Network is unreachable").
  • Fixed by disconnecting and reconnecting NPM to the mailcow network with --gw-priority -1, so the original proxy-manager_npm-net gateway remains the default route while still allowing reachability into the mailcow network.

Second issue — stale generated nginx config:

  • After the DB record was updated to point at the container name, the proxy host still failed intermittently — traced to NPM's generated nginx config file (/data/nginx/proxy_host/3.conf) still containing the old LAN IP (set $server "192.168.1.3";), i.e. the DB change never propagated to the on-disk config via a restart alone.
  • Resolved by re-saving the proxy host in the NPM web UI (Details tab, no actual value change needed) — this forced NPM to regenerate the config file from the DB and reload nginx with the correct upstream (mailcowdockerized-nginx-mailcow-1).

Verification:

  • curl https://mail.infernalaquatics.com → consistent HTTP 200, ~25-35ms response time across multiple attempts.
  • Confirmed via Uptime Kuma dashboard: mail.infernalaquatics.com back to "Up".

Takeaway for next time: When touching NPM's forward_host in the DB directly (vs. through the UI), always re-save through the UI (or explicitly restart+verify the generated .conf file) to guarantee the config regenerates — a raw container restart is not sufficient to pick up DB-only changes.