feat: manual update via tarball + signature upload #197

Merged
shahondin1624 merged 1 commits from feature/manual-update-upload into main 2026-04-17 21:37:53 +02:00
Owner

Summary

Adds a "Manuelles Update" panel on the Backup view. Admin picks a .tar.gz + matching .tar.gz.sig from disk, server verifies the Ed25519 signature against the hardcoded public key before extraction, then installs and runs occ upgrade.

Motivation: the automatic Gitea fetch sometimes can't reach the repo (mobile hotspot, captive portal, corporate firewall, air-gapped instance). This gives an offline-friendly path with identical security guarantees.

Security

Not a new attack surface — trust model is identical to the automated path:

  • sodium_crypto_sign_verify_detached() against the embedded PUBLIC_KEY constant
  • Verification runs before any extraction touches the filesystem
  • Only the holder of the matching Gitea release private key can produce a valid signature → a hostile upload is rejected cryptographically
  • Endpoint requires an admin session (enforced by AuthorizationMiddleware), CSRF protection inherited from ApiController (no NoCSRFRequired attribute on the new route)
  • Archive upload capped at 25 MiB, signature at 1 KiB (realistically 64 bytes)
  • Pre-verification sanity: gzip magic bytes + signature size exactly matches SODIUM_CRYPTO_SIGN_BYTES (64)
  • Temp files created via tempnam + chmod 0600, cleaned up in a finally block

Both paths now funnel through a single applyVerifiedUpdate() critical section, so there's no divergence in how signature verification → extraction → occ upgrade are ordered.

Test plan

  • All 1122 unit tests pass (including existing SelfUpdateSignatureTest which covers the shared verification logic)
  • Webpack production build clean
  • Version bumped to 0.2.10 in all three locations

🤖 Generated with Claude Code

## Summary Adds a "Manuelles Update" panel on the Backup view. Admin picks a `.tar.gz` + matching `.tar.gz.sig` from disk, server verifies the Ed25519 signature against the hardcoded public key **before** extraction, then installs and runs `occ upgrade`. Motivation: the automatic Gitea fetch sometimes can't reach the repo (mobile hotspot, captive portal, corporate firewall, air-gapped instance). This gives an offline-friendly path with identical security guarantees. ## Security **Not a new attack surface** — trust model is identical to the automated path: - `sodium_crypto_sign_verify_detached()` against the embedded `PUBLIC_KEY` constant - Verification runs **before** any extraction touches the filesystem - Only the holder of the matching Gitea release private key can produce a valid signature → a hostile upload is rejected cryptographically - Endpoint requires an admin session (enforced by `AuthorizationMiddleware`), CSRF protection inherited from `ApiController` (no `NoCSRFRequired` attribute on the new route) - Archive upload capped at 25 MiB, signature at 1 KiB (realistically 64 bytes) - Pre-verification sanity: gzip magic bytes + signature size exactly matches `SODIUM_CRYPTO_SIGN_BYTES` (64) - Temp files created via `tempnam` + `chmod 0600`, cleaned up in a `finally` block Both paths now funnel through a single `applyVerifiedUpdate()` critical section, so there's no divergence in how signature verification → extraction → `occ upgrade` are ordered. ## Test plan - [x] All 1122 unit tests pass (including existing `SelfUpdateSignatureTest` which covers the shared verification logic) - [x] Webpack production build clean - [x] Version bumped to 0.2.10 in all three locations 🤖 Generated with [Claude Code](https://claude.com/claude-code)
shahondin1624 added 1 commit 2026-04-17 21:37:48 +02:00
Adds a "Manuelles Update" panel on the Backup view: admin picks a
.tar.gz + matching .tar.gz.sig from disk, the server verifies the
Ed25519 signature against the hardcoded public key BEFORE extraction,
and installs + runs occ upgrade on success.

Useful when the automatic Gitea fetch can't reach the repo (hotspot,
captive portal, air-gapped instance).

Security model (unchanged from the automatic path):
- sodium_crypto_sign_verify_detached against embedded PUBLIC_KEY
- Verification runs BEFORE any extraction touches the filesystem
- Only the holder of the matching private key can produce a valid
  signature, so a hostile upload is rejected cryptographically
- Endpoint requires admin session (enforced by AuthorizationMiddleware),
  CSRF protection inherited from ApiController (no NoCSRFRequired)
- Uploads capped at 25 MiB (archive) / 1 KiB (signature)
- Pre-extraction sanity: gzip magic check, 64-byte exact signature size
- Temp files created via tempnam + chmod 0600 and deleted in finally

Shared critical section (applyVerifiedUpdate) means both the fetched
and the uploaded paths go through identical verify → extract → occ
steps, so there's no divergence in security posture.

Version bumped to 0.2.10. All 1122 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shahondin1624 merged commit 62f7053e5b into main 2026-04-17 21:37:53 +02:00
shahondin1624 deleted branch feature/manual-update-upload 2026-04-17 21:37:53 +02:00
Sign in to join this conversation.