Security
Operator notes for running Frachtschiff on a single host. Frachtschiff is a homelab product by default - the Docker host is the trust boundary.
Host trust model
Anyone with:
- membership in the host
dockergroup (or root), or - read access to
deploy/.env, drive bind mounts, or Docker secret files
can obtain application secrets and data. Treat the host accordingly.
With default Compose, secrets from deploy/.env are passed as environment
variables - visible via docker inspect and process environ. Never commit
deploy/.env.
Secret delivery
| Mode | How |
|---|---|
| Default | Values in deploy/.env → Compose environment: |
| Hardened (optional) |
Files as Docker secrets → *_FILE paths (see
deploy/examples/docker-compose.secrets.yml)
|
File-backed secrets remove values from container env listings but do not protect a fully compromised host.
Secret rotation
-
JWT_SECRET- invalidates web JWTs; TOTP ciphertext may need admin reset SHARE_LINK_SIGNING_SECRET- existing share links stop working-
ADMIN_PASSWORD- bootstrap only on empty DB; use in-app flows to change an existing admin -
POSTGRES_PASSWORD/DATABASE_URL- must stay in sync; Postgres applies the password only on first volume init
Container privileges and SMART
The API runs as root inside the container for drive I/O and optional SMART. That is intentional and is not multi-tenant isolation.
- Compose always grants
SYS_RAWIOand mounts/dev/diskread-only. -
When
SMART_DEVICESpoints at real block devices, override generation may setprivileged: trueon the API. -
Leave
SMART_DEVICESempty to avoid privileged mode. DisablingSMART_CHECK_ENABLEDalone does not remove privileged mode if devices are still listed.
TLS and reverse proxy
Terminate TLS at your edge proxy. Strip or overwrite client-controlled forwarding headers
(e.g. inbound X-Forwarded-For / True-Client-IP). Keep
X-Forwarded-Proto so Secure cookies and refresh origin checks work.
Example Caddy / Traefik snippets: upstream deploy/examples/. Large tus uploads
need long proxy timeouts (often up to an hour).
Database transport
Default stack uses sslmode=disable to Postgres on the internal Compose
network - acceptable for that layout. If DATABASE_URL points at a remote
host, enable TLS (require or verify-full).
Auth surfaces (do not mix)
| Client | Auth |
|---|---|
| Web UI | HttpOnly cookies + CSRF on mutating requests |
| Sync client | Bearer device token (no CSRF) |
| External API | Bearer API key (no CSRF) |
| Share links | Separate share session tokens |
Optional TOTP for web login. Device tokens and API keys are issued after a full web sign-in and are not subject to 2FA on each request.
Encrypted archives
Password-protected ZIP/7z uses host 7z; passwords go on stdin (not argv).
Still assumes a trusted host - not a multi-tenant isolation boundary.
Upstream detail: SECURITY-DEPLOY.md · SECURITY-AUTH.md.