Skip to content

fix: security hardening for v1.0.2#18

Merged
Romain-Grosos merged 10 commits intomainfrom
fix/security-hardening-1.0.2
Mar 12, 2026
Merged

fix: security hardening for v1.0.2#18
Romain-Grosos merged 10 commits intomainfrom
fix/security-hardening-1.0.2

Conversation

@Romain-Grosos
Copy link
Contributor

Summary

  • Mnemonic encryption hardened: root-only /etc/buncker/key-material combined with /etc/machine-id for AES key derivation
  • analysis_id (UUID) on analyze/generate-manifest to prevent concurrent race conditions (409 ANALYSIS_REPLACED)
  • Redundant ".." in parts path traversal check removed (Path.resolve() + is_file() is sufficient)
  • 60s per-read socket timeout to mitigate slowloris-style thread exhaustion
  • Import size limit raised from 4 GiB to 40 GiB for large ML image bundles

Commits (8)

  1. feat(crypto): add root-only key-material to mnemonic encryption
  2. feat(server): add analysis_id to prevent analyze/generate race condition
  3. refactor(server): remove redundant path traversal check
  4. fix(server): add 60s per-read socket timeout and raise import limit to 40 GiB
  5. test(server): update handler tests for required analysis_id parameter
  6. docs(security): update changelog and security docs for 1.0.2 hardening
  7. test(integration): add 4th container and Phase 4 for concurrency/timeout/large transfer
  8. test(integration): add Phase 5 for docker pull, crash recovery and GC lifecycle

Test plan

  • 581 unit/e2e tests passing (pytest)
  • 35 packaging tests passing (Docker debian:bookworm-slim)
  • Integration Docker tests Phase 1-3 (existing, need docker compose up)
  • Integration Docker tests Phase 4: concurrent analysis race, socket timeout, large transfer limits
  • Integration Docker tests Phase 5: real docker pull, crash recovery (kill -9), GC lifecycle

Mnemonic encryption now combines /etc/machine-id with a root-only
/etc/buncker/key-material file (mode 0600) generated at setup. This
prevents non-root processes from reconstructing the AES key, upgrading
the threat model from disk-theft-only to disk-theft + local process
isolation.
Each analyze response now includes a UUID analysis_id. The
generate-manifest endpoint requires this ID and returns 409
ANALYSIS_REPLACED if a concurrent analyze overwrote the result.
The ".." in parts check was redundant after Path.resolve() which
already normalizes symlinks and relative components. The is_file()
check after resolve() is sufficient for the admin-only localhost
endpoint.
…o 40 GiB

Set BaseHTTPRequestHandler.timeout = 60 to drop idle connections after
60 seconds of inactivity, mitigating slowloris-style thread exhaustion
with the 16-worker bounded pool. Streaming uploads sending at least one
chunk per minute are unaffected.

Raise _MAX_IMPORT_SIZE from 4 GiB to 40 GiB to accommodate large ML
image bundles transferred via USB or LAN.
…out/large transfer

- Add client-offline container (2nd LAN client) to docker-compose.yml
- Update Phase 1 and Phase 2 for analysis_id requirement
- Phase 4 tests:
  - Concurrent analyze: client 1 gets 409 ANALYSIS_REPLACED after client 2 overwrites
  - Socket timeout: idle connection dropped after 60s (slowloris mitigation)
  - Large transfer: 34 GiB Content-Length accepted (within 40 GiB limit)
  - 45 GiB Content-Length rejected with BODY_TOO_LARGE
  - Real 100 MiB sparse file PUT upload accepted
- Update e2e tests for analysis_id in generate-manifest calls
… lifecycle

- Real docker pull from client container via buncker OCI registry
- docker run to verify pulled image is functional
- Daemon crash recovery: kill -9, restart, verify blob persistence
- GC lifecycle: report candidates, impact analysis, execute deletion
- Post-GC docker pull failure validates blob removal
- docker system prune before post-GC pull to prevent layer cache flakiness
@Romain-Grosos Romain-Grosos added this to the v1.0.2 milestone Mar 12, 2026
@Romain-Grosos Romain-Grosos self-assigned this Mar 12, 2026
generate-manifest now requires analysis_id - extract it from
analyze output and pass via --analysis-id for both nginx and
alpine transfer cycles.
@Romain-Grosos Romain-Grosos merged commit e233e54 into main Mar 12, 2026
7 checks passed
@Romain-Grosos Romain-Grosos deleted the fix/security-hardening-1.0.2 branch March 12, 2026 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant