Commit Graph

108 Commits (fd6eba5c29b8be33a4ffca6fafafaceedc110705)

Author SHA1 Message Date
boban c34ced0c0e fix(tls/ui): fully automatic TLS (status-only) + uniform hardening buttons
Per operator feedback ("das soll automatisch sein, die Caddy-Config muss man nicht
sehen, nur 'SSL steht'"):

- TLS is fully automatic via Caddy (auto-issue + auto-renew + HTTP→HTTPS). The System
  page now shows only the STATUS (domain + access URL) — the generated Caddy config and
  the manual `caddy reload` command are gone, and the domain/email edit form is removed.
- The panel domain is an INSTALL-TIME value (APP_DOMAIN → config('clusev.domain')), so it
  stays consistent with APP_URL, the Reverb (wss) endpoint and cookie security — none of
  which can be re-derived at runtime (Caddy's site address + the browser's Reverb host are
  fixed at install/build, and .env is never rewritten). DeploymentService is now read-only.
- Removed AppServiceProvider's runtime app.url override (it let a stale DB value shadow the
  install-time URL). Migration drops the inert empty dashboard_domain/dashboard_email rows
  (a real configured value is preserved as history; re-apply via install.sh — see CHANGELOG).
- Hardening checklist: all toggle buttons share one style (secondary/bordered) instead of
  varying with state (R10).

Bump 0.2.0 -> 0.2.1 + CHANGELOG. Pint clean; Codex reviewed (the remaining note is a
deliberate install-time-domain tradeoff — a runtime/DB domain cannot be served by the
templated Caddy without a false "TLS active" status); R12 — system/server-show/dashboard
HTTP 200, 0 console errors, raw config + edit form gone, buttons uniform.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 21:17:26 +02:00
boban 4a4d107bfc chore(release): v0.2.0 — System TLS rationale + version bump + CHANGELOG
- System page: collapsible "Warum läuft TLS über Caddy?" explaining the single
  host-exposed TLS terminator (auto Let's Encrypt + auto-renew + wss for Reverb,
  decoupled from app updates) — answers the operator's question in the UI; Caddy stays.
- Bump 0.1.1 -> 0.2.0 (cross-OS management, firewall + fail2ban control, command
  palette) + CHANGELOG entry.

Pint clean; Codex review clean; R12 — all 9 routes HTTP 200, 0 console errors,
TLS note renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 20:43:07 +02:00
boban 0f57074e6f chore(audit): full code sweep — fix open-redirect, drop dead code, clean docs
Full-codebase audit + automatic cleanup (no behavioural change for users).

Security
- ServerSwitcher: post-switch redirect is now reduced to a SAME-ORIGIN
  relative path. Rejects off-host referers, non-http(s) schemes
  (javascript:/data:), different ports (https://host:444/x -> /x), and
  protocol-relative tricks (//host, /\host). Closes a Referer open-redirect
  (Codex P2).
- EditCredential: a key passphrase is stored only for key auth, never for
  password auth.

Removed dead code
- FirewallService::status/allow/deny (+ orphaned clampPort)
- Server::auditEvents(), SshCredential::scopeActive(), unused 'bell' icon

Cleanups / improvements
- Files list + editor use a dedicated 'file' icon (was the audit icon)
- Import RuntimeException/Throwable instead of FQN; fix stale docblocks
  (FirewallService, HardeningService) and the Files delete comment
- Quote base64 in the sudo command path (defensive, consistent)
- Null-safe Auth::user()?->email in Settings; single domain() read in hasTls()

Release
- Bump 0.1.0 -> 0.1.1 + CHANGELOG entry

Verified: Pint clean, Codex review clean (no actionable regressions),
R12 browser check — 9 routes HTTP 200, 0 console errors, lazy pages loaded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 12:54:26 +02:00
boban 0b2dffe0de fix+feat(release/security): apt timeout, real version/release model, domain in DB not .env, R15
- fix(hardening): fail2ban/unattended apt installs failed because the 12s SSH timeout
  aborted the package download. runPrivileged/runPlain take a $timeout; apt actions use
  600s. Live-verified: fail2ban installs + is active/enabled on 10.10.90.162.
- feat(release): cut the foundation as the first real semantic release v0.1.0 (no more
  -dev). CHANGELOG is Keep-a-Changelog BY VERSION; the Versions page parses it by version,
  reads the newest git TAG as "latest release", and honestly compares installed vs latest
  in the channel. Channels reduced to stable|beta (no user-facing dev); System clamps a
  legacy value back to stable. Tagged v0.1.0.
- fix(domain/.env): the panel domain lives in the DATABASE (Settings); the app never
  rewrites .env. AppServiceProvider overrides config('app.url') at runtime from the Setting
  (guarded against a missing table); /system states this explicitly. Caddy config is a
  standalone generated file; a Caddy reload is the only infra step.
- docs(arch): Caddy is the prod-only TLS reverse proxy IN FRONT of the app container's
  nginx (Internet -> Caddy:443 -> nginx:80 -> php-fpm); no Caddy in dev.
- R15 (rules.md + CLAUDE.md): Codex must review every change for errors + security before
  "done". The Codex CLI is not installed in this runtime (needs `sudo npm i -g @openai/codex`
  + `/codex:setup`); an independent adversarial security review of this diff returned CLEAN
  (no vulnerabilities, no bugs) as the interim gate.

Verified (R12): /system + /versions 200 / 0 console errors; channels stable|beta only,
DB-not-.env callout; versions by-version with v0.1.0; app.url override resolves from the
Setting; fail2ban live install ok.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 03:06:35 +02:00
boban 875678d0cd feat(versions)+fix: password-sudo service control, honest login, real versions page
- Service control now works with a sudo-PASSWORD credential (not only NOPASSWD):
  FleetService::sudoFn() defines a remote priv() that feeds the vaulted password to
  `sudo -S` over stdin (base64 over the encrypted channel, never on the argv) when not
  root, else falls back to sudo -n. Verified LIVE on 10.10.90.162: cron restart/stop/
  start executed for real (ActiveEnterTimestamp advanced to now, is-active=inactive
  after stop, restarted clean), journal reads real entries via sudo.
- Login brand panel: removed fabricated telemetry — fake "24 Hosts erreichbar",
  "cpu 31% mem 48% load 0.86", "clusev connect 10.10.90.0/24". Replaced with true
  capability lines (agentless SSH/phpseclib, TOFU host-key pinning, 2FA, audit log,
  AGPL). Only real claims now (per "nur eintragen was auch wirklich geht").
- New Version & Releases page (/versions, EN route per R13) with REAL data only:
  version from config/clusev.php, build SHA + branch read from .git at runtime,
  changelog parsed from a real CHANGELOG.md, real Gitea repo + AGPL license, honest
  update path (deploy commands) — NO fake updater / stars / forks / CVEs / "update
  available v2.5.0". Sidebar nav + tag/git-branch icons added.
- Dummy-data sweep (12-auditor workflow + adversarial verify): 1 confirmed finding —
  removed the unused sine/cosine series() fake-sparkline generator in Dashboard.php;
  also fixed a stale "static seed" chart comment and a stale "mock listing" comment;
  derived the versions repo label from config (DRY). 5 false positives dismissed.

Verified (R12): /login + /versions + all 7 routes 200 / 0 console errors; login
fake-metrics gone + honest lines + fonts load; versions shows real version/build/
changelog/repo with zero fabricated stats.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 01:43:48 +02:00
boban 218806727c feat(r5): wire-elements/modal confirmations for destructive actions
Add a generic ConfirmAction modal (LivewireUI\Modal\ModalComponent). On confirm
it persists exactly one AuditEvent and re-dispatches a page event so the origin
applies its own state change — domain-agnostic and reused everywhere (R5).

Wired the destructive actions, each writing an audit row:
- Services: start/stop/restart -> confirm + audit; service state reflects result
- Files: delete -> confirm + audit; entry removed
- Server-Details: revoke SSH key (new per-row trash button) -> confirm + audit
  (carries server_id)

Supporting changes:
- Publish + restyle the modal container for the dark theme (void backdrop +
  surface panel + shadow-pop instead of the package's gray/white defaults)
- Toaster island in the app layout that catches the `notify` browser event
- Add alert/power/rotate/trash icons to x-icon

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:05:50 +02:00
boban 8df4456108 feat: live metrics over Reverb (mock emitter)
- MetricsTicked broadcast event (channel 'metrics', as 'tick') + a
  clusev:mock-metrics command (dev placeholder for the SSH MetricsPoller),
  broadcasting a rolling CPU value every 2s.
- Echo + pusher-js client in app.js with an env-driven Reverb connection
  (VITE_REVERB_*). metricsChart Alpine island seeds from server data, appends
  each tick and redraws the sparkline; the indicator reflects the real WS state.
- Published config/reverb.php + config/broadcasting.php.
- Backend verified: the queue processes MetricsTicked every 2s with no errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 01:23:10 +02:00
boban 53f7309c0b feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb
Working dev stack on :80 (Freigabe step 5: app boots, page renders, Vite HMR runs):
- Multi-stage Dockerfile (php8.3-fpm + nginx + supervisor + node). Dev runs
  php-fpm + nginx + vite in ONE app container; prod target bakes vendor + assets.
- docker-compose.yml (dev, bind-mount) + docker-compose.prod.yml (GHCR image).
  Env-driven ports/UID (APP_PORT, VITE_PORT, REVERB_HOST_PORT, DB_PORT,
  HOST_UID/HOST_GID) — nothing hardcoded. MariaDB only on 127.0.0.1:3306.
- Laravel 13, Livewire 3.8 (class-based, no Volt), wire-elements/modal 2,
  phpseclib 3, laravel/reverb 1. composer platform pinned to php 8.3.
- Tailwind v4 @theme tokens ("Tactical Terminal") in app.css; Vite remote HMR
  (host 0.0.0.0, hmr.host 10.10.90.136).
- Dashboard as a full-page Livewire route (/) in layouts/app; §5 folder skeleton.
- Docs: rules.md R1-R11 (incl. R11 UUID-in-URLs) + CLAUDE.md updated for
  Laravel 13, clusev/ root, port 80, vite-in-app, env-driven config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 00:31:50 +02:00