Harden the container: non-root, pinned images and dependencies, the key in a file (B-91, SEC-51, SEC-52, SEC-53; closes B-71, B-72)

- app runs as uid 1000; a one-shot data-permissions service chowns ./data to it
  and closes it to others (works on an older root-owned install with no host sudo);
  files are created 0600 (umask 077)
- python:3.12-slim and caddy:2-alpine pinned by digest; dependencies installed from
  requirements.lock with --require-hashes
- the Fernet key is XPRV_ENCRYPTION_KEY_FILE (./secrets/xprv_encryption.key, a compose
  secret), out of every environment variable and of a ./data backup
- setup.md no longer presents a second way to run the server (B-72)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-18 00:54:23 +02:00
1 parent 40ac490826
commit c1b192f6d1
23 files changed
+1500 -183

No files matched your search

+8 -3
View File
@@ -30,9 +30,14 @@ ELECTRUM_CERT_PIN=
# NEVER true in production.
ALLOW_NO_QUORUM=false
# Fernet key protecting the master xprv at rest. Generate with:
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
XPRV_ENCRYPTION_KEY=
# File holding the Fernet key that protects the master xprv at rest (B-91): in a file, not in
# an environment variable, so `docker inspect` cannot show it and a backup of ./data does not
# carry both. Inside the container docker-compose.yml mounts it as a secret and overrides
# this path. Create it once:
# mkdir -p secrets && chmod 700 secrets
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" > secrets/xprv_encryption.key
# chmod 600 secrets/xprv_encryption.key
XPRV_ENCRYPTION_KEY_FILE=./secrets/xprv_encryption.key
# Random secret for JWT session signing. Generate with:
# python -c "import secrets; print(secrets.token_urlsafe(32))"