harden: run unprivileged, read-only rootfs, no capabilities

The app is the only process on this box accepting unauthenticated input from
the internet and it was running as root in a writable container. Now uid 10001,
read_only with a tmpfs /tmp, cap_drop ALL and no-new-privileges. Host-side
/srv/scout-website/data and the service account key are chowned to 10001.
Verified on a throwaway container: every route, the admin API, spam rejection,
and a real submission writing to both the jsonl and SQLite.
This commit is contained in:
2026-09-01 13:38:47 -04:00
parent 2d7784bcbd
commit 985253422d
2 changed files with 11 additions and 1 deletions
+4 -1
View File
@@ -2,5 +2,8 @@ FROM python:3.12-slim
RUN pip install --no-cache-dir fastapi "uvicorn[standard]" python-multipart gspread
WORKDIR /app
COPY . /app
RUN mkdir -p /app/static/img
RUN mkdir -p /app/static/img && useradd -r -u 10001 app
# Unprivileged: nothing here needs root, and the process is the one thing on
# this box that takes unauthenticated input from the internet.
USER 10001
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
+7
View File
@@ -17,6 +17,13 @@ services:
build:
context: ./app
container_name: scout-website
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
restart: unless-stopped
logging:
driver: json-file