module fixes from the whole-mesh dry-run: fail2ban capability + tool-runtime credential wiring #20

Merged
jschoubben merged 3 commits from feat/module-cred-fixes into main 2026-09-08 16:45:24 +00:00
Owner

Three fixes the whole-mesh dry-run surfaced:

  • fail2ban required a non-existent intrusion-prevention capability (unassignable on any node) → now requires firewall, which it actually needs to manage bans.
  • 7 tool-runtime modules (plex, bazarr, ombi, home-assistant, nzbget, qbittorrent, umami) whose mesh-<mod> sidecar crash-looped for a missing app credential the manifest never wired → each now declares the credential as an operator-provided own-secret, mounts it read-only into the runtime, and the client reads it from the MESH_*_FILE path (falling back to the old env so nothing regresses), the cloudflare-dns pattern. The operator supplies it with secret accept <node> <module> <name> --from <file>. These are deployable now (they still need a real app token to go green — correct, not a lab thing).

photos (ships a stub app image) and mailu (needs its full Mailu config env + API-token enablement) are deeper conversion jobs, not credential wiring — deferred/tracked. All modules tsc-clean.

Three fixes the whole-mesh dry-run surfaced: - **fail2ban** required a non-existent `intrusion-prevention` capability (unassignable on any node) → now requires `firewall`, which it actually needs to manage bans. - **7 tool-runtime modules** (plex, bazarr, ombi, home-assistant, nzbget, qbittorrent, umami) whose `mesh-<mod>` sidecar crash-looped for a missing app credential the manifest never wired → each now declares the credential as an operator-provided `own-secret`, mounts it read-only into the runtime, and the client reads it from the `MESH_*_FILE` path (falling back to the old env so nothing regresses), the cloudflare-dns pattern. The operator supplies it with `secret accept <node> <module> <name> --from <file>`. These are deployable now (they still need a real app token to go green — correct, not a lab thing). photos (ships a stub app image) and mailu (needs its full Mailu config env + API-token enablement) are deeper conversion jobs, not credential wiring — deferred/tracked. All modules tsc-clean.
jschoubben added 3 commits 2026-09-08 16:45:15 +00:00
The whole-mesh dry-run found fail2ban unassignable on every node: it declared
`capabilities: ["intrusion-prevention"]`, which mesh-host has no detector for
(its detectors are container-runtime, package-manager, service-manager,
firewall, overlay, graphical-session, seat, privileged). intrusion-prevention
is what fail2ban PROVIDES, not a host capability it needs. It bans via
iptables/ufw, so it needs `firewall` — the same capability the firewall module
declares. The `the-intrusion-prevention` claim (node-exclusive) is unchanged.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
The six modules that run a mesh-<mod> tool-runtime sidecar read an app
credential from an env var the manifest never provided, so the sidecar
crash-looped in the whole-mesh dry-run (e.g. "no Plex token — set
MESH_PLEX_TOKEN"). These are operator-set app secrets, so deliver them the
same way cloudflare-dns delivers its API token: an own-secret file mounted
read-only, with a MESH_<APP>_*_FILE env pointing at the mount, and the
runtime code preferring that file (falling back to the existing env so
nothing regresses).

- plex: own-secret token -> /run/secrets/token, MESH_PLEX_TOKEN_FILE
- bazarr: own-secret api-key -> /run/secrets/api-key, MESH_BAZARR_API_KEY_FILE
- ombi: own-secret api-key -> /run/secrets/api-key, MESH_OMBI_API_KEY_FILE
- home-assistant: own-secret token -> /run/secrets/token, MESH_HOMEASSISTANT_TOKEN_FILE
- nzbget: own-secret password -> /run/secrets/password, MESH_NZBGET_PASSWORD_FILE (URL stays plain env)
- qbittorrent: own-secret password -> /run/secrets/password, MESH_QBITTORRENT_PASSWORD_FILE (URL stays plain env)

The operator now completes each with `secret accept <node> <module> <name> --from <file>`.
tsc passes for all six.
The whole-mesh dry-run found umami's runtime crash-looping "admin password is
not set": its `admin` own-secret is mounted at /run/secrets/admin, but the
client read the bare env UMAMI_ADMIN_PASSWORD, which nothing sets. Same shape as
the six tool-runtime credential fixes — read the mounted file first
(MESH_UMAMI_ADMIN_PASSWORD_FILE), falling back to the env. (photos and mailu
remain deeper conversion jobs — a stub app image and a full Mailu config env —
not credential-wiring, tracked separately.)

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
jschoubben merged commit 6a84e97e53 into main 2026-09-08 16:45:24 +00:00
jschoubben deleted branch feat/module-cred-fixes 2026-09-08 16:45:24 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#20