Create every SQLite file 0600 (closes #255)
All checks were successful
check / check (push) Successful in 3m24s
All checks were successful
check / check (push) Successful in 3m24s
webhooker.db holds target configuration in plaintext — bearer tokens, API keys, Slack webhook URLs — and the session encryption key, and it was created 0644. The 0750 data directory was therefore the only barrier, and a Docker bind mount supplies that directory at 0755, which removes it: every local user on the host could read every stored credential. The mode is settled in OpenSQLite, the single open path all three tiers share, so the main database, the per-webhook event databases and the archive databases are covered in one place. 0644 comes from SQLite itself: robust_open substitutes SQLITE_DEFAULT_FILE_PERMISSIONS whenever it is handed mode 0, and findCreateFileMode yields 0 for a main database opened by URI with no `modeof` parameter. A chmod after opening would leave a window in which the credentials are on disk world-readable, so OpenSQLite creates the file itself at 0600 before the driver sees the path. That also settles the WAL sidecars, which carry the same rows and would otherwise leave the fix worthless. SQLite derives both from the main database file — `-wal` through findCreateFileMode, which stats the path with the suffix stripped, and `-shm` in unixOpenSharedMemory from an fstat of the open database descriptor — so a main file at 0600 produces sidecars at 0600. Verified by stat rather than by reading the driver: a pre-change build leaves webhooker.db, -wal and -shm all 644; this one leaves all three 600, in a 0755 bind mount, for all three tiers, with delivery and restart working. Existing files are chmodded on open, so a directory an earlier build left 0644 — including a developer's scratch directory — is fixed without any migration machinery. This is not encryption at rest. An unattended process needs a key it can read without a human, so the key lands beside the data and an attacker who can read the database can read it too. The data directory stays 0750: the group bit may matter to a deployment, and with the files at 0600 the directory is no longer the barrier. README: the Docker section now states the DATA_DIR ownership requirement where the bind mount is documented. A `-v` source path Docker creates is root:root, and the container runs as UID 1000, so it fails to start on the lock file; the chown that fixes it appeared only under Restore.
This commit is contained in:
43
README.md
43
README.md
@@ -666,6 +666,44 @@ databases written by `database` targets (`archive-{uuid}.db`). Mount
|
||||
this as a persistent volume to preserve data across container
|
||||
restarts.
|
||||
|
||||
**The bind-mounted directory must be owned by UID 1000, or the
|
||||
container does not start.** Docker creates a `-v` source path that
|
||||
does not exist yet as `root:root`, and the process runs as UID 1000,
|
||||
so it cannot take its `DATA_DIR` lock:
|
||||
|
||||
```
|
||||
webhooker: locking data directory /var/lib/webhooker: open
|
||||
/var/lib/webhooker/webhooker.lock: permission denied
|
||||
```
|
||||
|
||||
It exits non-zero at that point, before opening any database. Create
|
||||
the directory ahead of the first `docker run`:
|
||||
|
||||
```bash
|
||||
mkdir -p /path/to/data
|
||||
chown 1000:1000 /path/to/data
|
||||
chmod 750 /path/to/data
|
||||
```
|
||||
|
||||
The same `chown` is what a restore needs — see step 4 of
|
||||
[Restore](#restore). A **named volume** does not have this problem:
|
||||
Docker copies the image's ownership onto a volume it initializes, and
|
||||
the image creates `/var/lib/webhooker` owned by `webhooker`.
|
||||
|
||||
**The file modes are not yours to set, and do not depend on the
|
||||
directory.** `webhooker.db` holds target configuration in plaintext —
|
||||
bearer tokens, API keys, Slack webhook URLs — along with the session
|
||||
encryption key, so webhooker creates every SQLite file it owns `0600`:
|
||||
each database and both of its `-wal` and `-shm` sidecars, across all
|
||||
three tiers. Files an earlier build left `0644` are tightened when
|
||||
they are opened. A `DATA_DIR` webhooker creates itself is `0750`, but
|
||||
a bind mount supplies its own directory and Docker's default for one
|
||||
it creates is `0755`; the `0600` files hold there regardless. The
|
||||
`chmod 750` above is defence in depth — it stops other local users
|
||||
listing the directory and learning your webhook UUIDs from the
|
||||
`events-{uuid}.db` filenames — not the barrier protecting the
|
||||
credentials.
|
||||
|
||||
## Deployment behind a reverse proxy
|
||||
|
||||
webhooker terminates no TLS of its own. It serves plaintext HTTP and
|
||||
@@ -1622,6 +1660,11 @@ webhooker uses **separate SQLite database files**: a main application
|
||||
database for configuration data and per-webhook databases for event
|
||||
storage. All database files live in the `DATA_DIR` directory.
|
||||
|
||||
Every one of them is created `0600`, and so is each `-wal` and `-shm`
|
||||
sidecar. See
|
||||
[Running with Docker](#running-with-docker) for what that does and
|
||||
does not protect.
|
||||
|
||||
**Main Application Database** (`{DATA_DIR}/webhooker.db`) — stores
|
||||
configuration and application state:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user