From 442339f88788eb5b4535514dcb1141c429b9d15d Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 24 Sep 2026 08:28:57 -0400 Subject: [PATCH] Contain the Litestream sidecar's write access to the database volume Review finding F-01: the litestream service mounts nestquest-db read-write. A read-only mount is not possible: Litestream 0.5.17 fails with "unable to open database file (14)" on a :ro mount, even with meta-path moved to a separate writable volume, because it writes its _litestream_seq/_litestream_lock tables into the database, opens the -wal/-shm files for writing and checkpoints the WAL. Keep the write mount and contain it instead: run as the API's non-root volume owner (999:999) with a read-only root filesystem (tmpfs /tmp), no-new-privileges and all capabilities dropped. Document why in compose and api/README.md, including the key chown the uid change needs. Co-Authored-By: Claude Opus 5.5 --- api/README.md | 13 ++++++++++++- deploy/compose.yaml | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/api/README.md b/api/README.md index faa0190..ae1e773 100644 --- a/api/README.md +++ b/api/README.md @@ -59,7 +59,18 @@ box and is git-ignored. Create it once, before ``up``: then append ``restrict `` + the contents of ``deploy/litestream_key.pub`` to ``joshua@10.60.1.50:~/.ssh/authorized_keys``. Compose mounts the -private key read-only at ``/etc/litestream/sftp_key``. +private key read-only at ``/etc/litestream/sftp_key``. The sidecar runs +as uid/gid 999 (the API's ``nestquest`` user), so give it the key: + + sudo chown 999:999 deploy/litestream_key && chmod 600 deploy/litestream_key + +The sidecar needs WRITE access to the database volume: Litestream adds +its own ``_litestream_seq``/``_litestream_lock`` tables to the database, +writes the ``-wal``/``-shm`` files and checkpoints the WAL, and fails +with "unable to open database file" on a read-only mount. That access +is contained rather than removed — the sidecar runs as the non-root +volume owner with a read-only root filesystem, ``no-new-privileges`` +and all Linux capabilities dropped. Check replication with ``docker compose -f deploy/compose.yaml logs litestream`` or diff --git a/deploy/compose.yaml b/deploy/compose.yaml index 5463e74..742ba23 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -46,10 +46,27 @@ services: # volume to the Synology NAS over SFTP (config: deploy/litestream.yml). # The SFTP private key deploy/litestream_key is git-ignored and must be # created on the API box before starting (see api/README.md). + # + # The database volume is mounted READ-WRITE on purpose: Litestream + # cannot run against a read-only mount ("unable to open database file + # (14)", even with its metadata moved elsewhere) because it writes its + # _litestream_seq/_litestream_lock tables into the database, opens the + # -wal/-shm files for writing, and checkpoints the WAL. The write + # access is contained instead: it runs as the API's own non-root user + # (uid/gid 999, owner of the volume) with a read-only root filesystem, + # no privilege escalation and no Linux capabilities. litestream: image: litestream/litestream:0.5.17 restart: unless-stopped command: ["replicate", "-config", "/etc/litestream.yml"] + user: "999:999" + read_only: true + tmpfs: + - /tmp + security_opt: + - no-new-privileges:true + cap_drop: + - ALL depends_on: api: condition: service_healthy