4 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 470823b347 |
feat(auth): login-guard the Furtka UI with a cookie session
All checks were successful
Build ISO / build-iso (push) Successful in 17m30s
CI / lint (push) Successful in 27s
CI / test (push) Successful in 43s
CI / validate-json (push) Successful in 31s
CI / markdown-links (push) Successful in 15s
Release / release (push) Successful in 11m38s
One-admin, one-password model — all of /apps, /api/*, /, and
/settings/ now require a signed-in session. Passwords are werkzeug
PBKDF2-hashed in /var/lib/furtka/users.json (mode 0600, atomic write
via the same .tmp+chmod+rename dance installer.write_env uses).
Sessions are secrets.token_urlsafe(32) tokens held in a module-level
SessionStore dict (thread-safe lock included for when we swap to
ThreadingHTTPServer). Cookies are HttpOnly, SameSite=Strict, and
Path=/, with Secure set when X-Forwarded-Proto from Caddy says HTTPS.
Two bootstrap paths:
* Fresh install — webinstaller step-1 collects Linux user + password,
the chroot post-install step hashes the password and writes
users.json on the target partition. First browser visit lands on
/login with the account already present.
* Upgrade from 26.10-alpha — no users.json yet, so /login detects
setup_needed() and renders a first-run setup form. POST creates
the admin and immediately logs in.
POST /logout revokes the server session and clears the cookie.
Unauthenticated HTML requests 302 to /login; unauthenticated API
requests 401 JSON so fetch() callers see a clean error. A sleep(0.5)
on failed logins is the brute-force speed bump on top of werkzeug's
~600k-iter PBKDF2.
Caddyfile gains /login* and /logout* handle blocks in the shared
furtka_routes snippet so both :80 and the HTTPS hostname block
forward the auth endpoints to localhost:7000. Without this Caddy
would 404 from the static file server.
Test surface:
* tests/test_auth.py (new, 19 cases): hash roundtrip, users.json
I/O, session create/lookup/expire/revoke.
* tests/test_api.py: new admin_session fixture; existing HTTP
tests updated to send the cookie; new tests cover login setup,
login success, wrong-password 401, logout revocation, and the
guard's 302/401 split.
* tests/test_webinstaller_assets.py: new case that unpacks the
users.json _write_file_cmd body and verifies the werkzeug hash
round-trips against the step-1 password.
Bumped version to 26.11-alpha and rolled CHANGELOG. Also folded in
the ruff-format fix that was pending from 26.10-alpha's lint red.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 8fbe67ffb9 |
fix(https): restore TLS handshake — name hostname + correct PKI path
Some checks failed
Closes #10. Two linked bugs in 26.4-alpha's Phase 1 HTTPS made the force-HTTPS toggle fatal: every SNI handshake on :443 died with SSL_ERROR_INTERNAL_ERROR_ALERT, so the toggle redirected users from working HTTP to broken HTTPS. Root cause 1: bare `:443 { tls internal }` gives Caddy no hostname to issue a leaf cert for, so /var/lib/caddy/certificates/ stayed empty and Caddy sent TLS `internal_error` on every handshake. Fix: the :443 block is now `__FURTKA_HOSTNAME__.local, __FURTKA_HOSTNAME__ { tls internal }`, with the marker substituted by webinstaller/app.py at install time and by furtka.updater._refresh_caddyfile on self-update (reads /etc/hostname, falls back to "furtka"). `auto_https disable_redirects` keeps Caddy's built-in redirect out of the way of the /settings toggle. Root cause 2: furtka/https.py and the /rootCA.crt handler both referenced /var/lib/caddy/.local/share/caddy/pki/authorities/local/ — a path that doesn't exist. caddy.service sets XDG_DATA_HOME=/var/lib, so Caddy's storage is /var/lib/caddy/ directly. Fix: both paths corrected. Verified on the 192.168.178.110 smoke VM by swapping the Caddyfile in, reloading, handshaking, restoring: TLS 1.3 handshake succeeds, leaf cert issued under /var/lib/caddy/certificates/local/, /rootCA.crt returns 200. Tests: new cases assert the Caddyfile ships the hostname placeholder, the webinstaller substitutes it, _refresh_caddyfile re-substitutes from /etc/hostname on update, and the asset sets auto_https disable_redirects. Unit tests still stub the Caddy reload — the real handshake regression needs a smoke-VM integration test (follow-up, separate from this fix). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 663bd74572 |
feat(https): local HTTPS via Caddy tls internal + opt-in redirect toggle
Caddy now serves both :80 (plain HTTP, unchanged default) and :443 with tls internal — it generates its own per-box root CA on first start, stored under /var/lib/caddy/.local/share/caddy/pki/authorities/local/. Users can download rootCA.crt at /rootCA.crt (served on both listeners) and install it per-OS via the new /https-install/ guide. Settings page grows a Local HTTPS card with CA fingerprint, download button, reachability probe, and an opt-in "force HTTPS" toggle. The toggle only unhides itself once the current browser already trusts the cert, so enabling it can't lock the user out of the settings page. Backend: GET /api/furtka/https/status and POST /api/furtka/https/force in furtka.https. The force toggle drops a Caddy import snippet into /etc/caddy/furtka.d/redirect.caddyfile and reloads Caddy; reload failure rolls the snippet state back so a bad config can't wedge the next service start. updater._refresh_caddyfile() ensures /etc/caddy/furtka.d exists before every reload so 26.3-alpha → 26.4-alpha self-updates don't trip on the new glob import directive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| c080764c7e |
fix(furtka): move assets/ to repo top level so Caddy + systemd find it
Root cause of today's 403 on a fresh install: assets/ lived inside the Python package at furtka/assets/, so the resource-manager tarball extracted to /opt/furtka/versions/<ver>/furtka/assets/. But Caddyfile has `root * /opt/furtka/current/assets/www`, systemd units point at /opt/furtka/current/assets/bin/furtka-status, and the install-time `systemctl link /opt/furtka/current/assets/systemd/*.service` expected the top-level layout. All three found nothing: - Caddy → 403 Forbidden (empty/missing document root) - systemctl link → silent no-op, nothing ever linked into /etc/systemd/system/ - furtka-api.service + furtka-reconcile.service → "inactive" because they were never registered Nothing in the Python package ever imported furtka.assets — these are shell scripts, HTML/CSS, systemd units, and a Caddyfile, which is config data, not package data. Promoting assets/ to the repo root matches how it's referenced everywhere downstream and eliminates the path mismatch. Changes: - git mv furtka/assets assets - iso/build.sh: tarball-staging step now also `cp -a "$REPO_ROOT/assets"` so the tarball ships ./assets at its root, and the live-ISO copy reads from $REPO_ROOT/assets instead of $REPO_ROOT/furtka/assets. - scripts/build-release-tarball.sh: same for release tarballs. - webinstaller/app.py: _resolve_assets_dir's dev fallback walks one level up to REPO_ROOT/assets/. - tests/test_webinstaller_assets.py: ASSETS constant updated. Tests still green (150/150) because both paths were fs-level — no code imports changed. Next ISO build will land assets at the path everything downstream expects. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
Renamed from furtka/assets/Caddyfile (Browse further)