Commit Graph

3 Commits (3fc2edf4fc31bd839cef51382170ec946167e7d2)

Author SHA1 Message Date
boban 3fc2edf4fc feat(hardening): bidirectional on/off toggles, real state read, UFW install, Codex-clean
Rework the server-hardening UX per operator feedback ("only activate, can't
deactivate or adjust, UFW won't even activate, is the existing state read?").

- Bidirectional toggles: each item (SSH root-login, SSH password-login, fail2ban,
  UFW, unattended-upgrades) is a clean Aktivieren/Deaktivieren toggle driven by the
  live feature state, not a one-shot "Anwenden".
- Real state read (HardeningService::state, privileged): SSH from `sshd -T` (the
  EFFECTIVE config — honours Include order + Match blocks), UFW from `ufw status`,
  unattended from `apt-config dump` (effective periodic value), packages via dpkg —
  so nicht-installiert / inaktiv / aktiv are detected; a feature counts as "secure"
  only when installed AND active.
- UFW activation installs ufw if missing (fixes "ufw: not found"); opens the detected
  sshd port + 80/443 before `ufw --force enable`; adds disable().
- SSH drop-in renamed 00-clusev.conf (sorts + wins first); apt periodic written to a
  last-winning 99zz-clusev file. Long timeout for apt installs.
- preview() returns the EXACT command apply() runs (single source — no drift between
  the confirmation preview and the executed mutation). Password-disable lock-out guard
  intact (refused when Clusev uses password auth or no key exists).

R15 — Codex review gate: `codex review --uncommitted` run iteratively; fixed every
finding across 5 rounds (drop-in precedence, apt periodic config, preview/apply drift,
ufw status detection, privileged-read error handling, stale-config secure state) until
"no actionable regressions". 0 security issues throughout.

Live-verified on 10.10.90.162: state via sshd -T (effective), fail2ban + unattended
toggled on/off both ways, ufw installed + state reflects, lock-out guard refuses
disabling password auth. R12: server detail 200 / 0 console errors, toggles render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 03:52:53 +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 a64bd39c6c feat(security): dashboard hardening, credential mgmt, system domain/TLS + channel, self-hardening
Security pivot — make server hardening, SSH access, firewall, dashboard domain/TLS
and the release channel controllable from the dashboard (no SSH needed), with guards
so a remote change can never lock the operator out.

Foundation
- ssh_credentials gains name + disabled_at + last_used_at; CredentialVault refuses a
  disabled credential. New key/value Setting model. FleetService::runPrivileged() /
  runPlain() — central sudo-aware exec (base64-wrapped sh -c), live-verified as root.

A · control-plane self-hardening
- SecurityHeaders middleware: env-aware CSP (allows the Vite dev origin in dev, strict
  same-origin in prod), X-Frame-Options DENY, nosniff, Referrer/Permissions-Policy,
  HSTS when secure. 2FA brute-force throttle (5/60s). install.sh sets
  SESSION_SAME_SITE=strict + EXPIRE_ON_CLOSE + SECURE_COOKIE (only behind TLS).

B · SSH credential management
- name/label on the access; a credential card on the server page with Bearbeiten /
  Sperren-Entsperren (kill-switch) / Löschen (R5), all audited.

C · server hardening from the dashboard (guards + confirmation)
- HardeningService (PermitRootLogin no, PasswordAuthentication no, fail2ban,
  unattended-upgrades) + FirewallService (UFW). HardeningAction modal previews the
  exact root commands before applying. GUARDS: refuse to disable password-login when
  Clusev itself logs in by password or no key exists; UFW opens the real sshd port +
  80/443 before enabling. Live-verified non-destructively (previews, the password
  guard refusing, ufw status read).

D+E · System page (/system)
- Dashboard Domain + Let's-Encrypt email (Setting) -> DeploymentService renders the
  matching Caddy site block (honest: stages the file + shows the reload command, never
  fakes TLS). Release channel (stable|beta|dev) configurable; Versions reads it.

Built largely by 4 parallel agents into disjoint files; shared files integrated + the
security-critical bits hardened by hand (the password-auth lock-out guard + env-aware CSP).

Verified (R12): /system + server detail + all 8 routes 200 / 0 console errors (CSP does
not break Livewire/Alpine/Vite); credential card + 5 hardening "Anwenden" buttons render;
the hardening modal opens with the command preview; System persists domain/channel +
renders valid Caddy config; runPrivileged runs as root + the vault refuses disabled creds.

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