Commit Graph

13 Commits (a6176341e290c2c981c0606fb483d355b1c953d0)

Author SHA1 Message Date
boban a6176341e2 fix(security): keep internal IPs out of shipped code and docs/ out of the public image
Audit leak-hygiene findings:
- The operator's real internal dev-VM/WireGuard IPs (10.10.90.136/.162/.165) were shipped in
  vite.config.js (hmr.host), lang hint/placeholder strings, a code comment and the CHANGELOG.
  vite HMR host is now env-driven (VITE_HMR_HOST, dev .env only); all other occurrences are replaced
  with RFC5737 documentation addresses (203.0.113.x). Tests updated in lockstep.
- docs/ is export-ignored from the git tree and named in promote.sh's refuse-to-publish list, but was
  never .dockerignore'd, so `COPY . .` baked the private docs (with private host/URL refs) into the
  public image, which the git-tree leak-guard never inspects. Added docs/ (and art/) to .dockerignore.
- /update.log (new repo-root update transcript) gitignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 10:25:10 +02:00
boban e154f7d92f feat(audit): readable action labels + failures surfaced (v0.9.48)
The audit log showed raw machine codes ("Administrator · wg.set-endpoint").
Now each entry renders a localized human-readable label ("WireGuard endpoint
changed"), via an AuditEvent::action_label accessor backed by an audit.actions
lang map (DE/EN); unmapped/dynamic codes (e.g. harden.*.on|off) fall back to a
tidied form, never a bare code. Search also matches the readable label.

Failures are now visible: AuditEvent::is_error flags failed/security events
(failed sign-in, IP ban, failed 2FA, wg.action-failed) which render in red with
an alert icon. And failed WireGuard dashboard actions are now written to the
audit log with the host error message (not just a transient toast), so the
operator can read later what went wrong.

Tests: label mapping + fallback, is_error, page renders label not code, search
by label, WG failure auditing. 360 pass, Pint clean, /audit loads 200 with no
console errors, audit lang parity 59/59.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:41:48 +02:00
boban 60e1edc0d5 feat(wg): configurable DNS + Server/Peers tabs + faster offline (v0.9.45)
Three things the operator asked for on the WireGuard page:

- DNS is no longer hardcoded to 1.1.1.1. New host action set-dns (_set_dns +
  cmd_set_dns + write-bridge branch + WgBridge validation, IPv4 list only),
  stored as WG_DNS in wg.env (appended for pre-existing tunnels), surfaced via
  the collector + WgStatus, baked into new peer configs (DNS = ${WG_DNS:-1.1.1.1}).
  Editable in the Server tab; e.g. point clients at your own gateway.
- The page is split into two independent tabs: "Peers" (list / add / remove /
  traffic) and "Server configuration" (endpoint / DNS / port / subnet / gate /
  SSH lock + server identity). #[Url] $tab makes it deep-linkable (?tab=server).
- Offline detection: ONLINE_WITHIN 180s -> 150s (PersistentKeepalive=25 keeps
  active peers re-handshaking well within it, so no flapping; a disconnect now
  shows offline ~2.5 min sooner). Endpoint hint clarified: the port is optional
  (auto-appended), no need to repeat the listen port.

Tests: set-dns (write+audit, reject), tab switch/clamp, dns in status; the two
configured-render tests now switch to the server tab for server content. 353
pass, shellcheck clean, Pint clean, both tabs load 200 with no console errors,
lang parity 89/89.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:48:46 +02:00
boban 920dfb02bf feat(wg): compact QR + meaningful download filename (v0.9.44)
- QR: 0.9.42 over-enlarged it; bring the show-once peer QR back to a balanced
  ~240px (still crispEdges on a white quiet-zone frame) so it scans on a phone
  without dominating the modal.
- Download filename now becomes a useful tunnel name on import: build it from
  the endpoint host + peer name (e.g. "10.10.90.165-laptop.conf") instead of the
  generic "clusev-wireguard.conf" — WireGuard apps name a tunnel after the file.
  A QR scan can't carry a name (the app prompts for one); the import hint now
  explains both paths.

Tests: download-filename cases (host+peer, fallback); 349 pass, Pint clean,
/wireguard loads 200, lang parity 83/83.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:25:41 +02:00
boban 9c0d390365 refactor(wireguard): remove dead code + DRY the WG surface (no behavior change)
Verified dead-code + duplication cleanup over the WireGuard feature surface
(found via an audit workflow, every item adversarially re-verified by grep):

Dead code removed:
- lang keys not_configured_title / not_configured_body (both locales) — orphaned
  when the unconfigured empty state became the in-page setup form (setup_title /
  setup_intro). 0 references anywhere. de/en parity kept (83/83).
- WgTraffic::series() 'buckets' return key — written, never read by blade/route/test.
- blade $fmtB byte-formatter — byte-identical duplicate of the top-level $fmtBytes
  already in scope; collapsed onto $fmtBytes.

DRY / readability (behavior-preserving):
- Index: PEER_NAME_RE constant (was the same regex literal at 5 sites), portInRange()
  + clampWindow() helpers (window default now self::WINDOWS[0]), openConfirm() helper
  (5 confirm-modal openers shared one dispatch), onFlag() helper (2 apply handlers).
- WgBridge: validPort() helper (port check was duplicated verbatim).
- clusev-wg.sh: _write_server_conf() shared by both setup paths (the byte-identical
  server bring-up was copy-pasted); single CIDR_RE constant for the 3 subnet checks;
  fixed a stale header comment that still claimed SSH is "NEVER matched" (the v0.9.40
  ssh-lock can match 22); documented the defence-in-depth re-validation in _set_*.
- WgStatus: documented server.pubkey is part of the /wg-status.json contract (kept).

No DB/migration changes (peer_name write-only column left intact — needs a
coordinated migration). 348 tests pass, shellcheck clean, Pint clean, R12 verified
in both the configured + unconfigured states (200, no console errors, no leaked
keys, traffic formatter renders 1.5 MB / 200 KB correctly).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:33:41 +02:00
boban 2b245c3d3a feat(wireguard): SSH lock + peer config download (v0.9.40)
Two dashboard additions to the WireGuard page:

- Peer config download: the show-once new-client view gets a "Download"
  button next to the QR code (streams clusev-wireguard.conf) plus an
  inline hint explaining QR-on-phone vs download-on-PC import.

- SSH lock (port 22): an optional toggle in WireGuard settings that walls
  off host SSH to the tunnel via a dedicated CLUSEV-WG-SSH chain on the
  host INPUT chain. Fail-open by construction: ESTABLISHED,RELATED + lo +
  wg0 + WG-subnet RETURN before the DROP (live sessions survive, SSH over
  the tunnel stays open); _ssh_gate_on refuses while wg0 is down; the boot
  unit's ConditionPathExists=wg0 and an in-function self-guard keep a
  broken tunnel from ever applying the DROP; ssh_gate_apply re-validates
  the subnet shape and fails open on a corrupt wg.env. Strong lock-out
  warning shown inline AND in the confirm modal (recommend a backup peer).
  New CLI escapes: clusev wg ssh-lock / ssh-unlock; clusev wg down clears
  both gates. Panel gate and SSH gate are independent toggles.

Write-bridge: new gate-ssh-on/gate-ssh-off actions (no args, host
whitelist + ConfirmToken wgSshGate, single-use uid-bound). Status collector
now reports gate.ssh. Adversarial lock-out review: all 8 failure modes
refuted. 348 tests pass, shellcheck clean, Pint clean, R12 verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 09:25:19 +02:00
boban 94425f93e8 fix(wg): remove SSH hint from setup form + time out a non-responding host (no endless spinner)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 08:38:43 +02:00
boban 8e05cf62ec feat(wg): set WireGuard up from the dashboard (setup form + bridge action)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 08:09:35 +02:00
boban 858a00955a fix(wg): single audit per confirm action (no descriptor double-audit)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:00:26 +02:00
boban 7ccefa5890 feat(wg): gate toggle + endpoint/port/subnet settings from the dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 00:53:29 +02:00
boban ee8f2bac91 feat(wg): add/remove peers from the dashboard (show-once config + QR)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 00:36:26 +02:00
boban 0c9aa12aab feat(wg): traffic-history SVG chart + window selector on /wireguard 2026-06-21 00:07:18 +02:00
boban e640a96ca0 feat(wg): /wireguard live-status page (read-only) + nav (DE/EN)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 23:35:58 +02:00