Files
scout-website/docker-compose.yml
T
thethreemagi 985253422d 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.
2026-09-01 13:38:47 -04:00

57 lines
1.9 KiB
YAML

# scout-website — greenlanescouts73.org (Pack & Troop 73)
#
# Deployed as a Portainer Repository stack pointed at this repo
# (ops/portainer-stacks/00-the-rule.md in claude-workspace applies):
# - no relative bind mounts: `.` resolves to Portainer's clone, not /srv
# - no env_file: NTFY_URL comes from the Portainer stack env
# Images live OUTSIDE the repo at /srv/scout-website-assets/img (bind-mounted
# read-only) so photos never bloat clones. Documents work the same way, from
# /srv/scout-website-assets/docs mounted at /docs - NOT under /app/static,
# because they are served through the app so login can gate them later.
# The calendar comes from the scout-calendar feed (EVENTS_FEED_URL, set in
# the Portainer stack env); /data/events-cache.json is the stale fallback.
name: scout-website
services:
scout-website:
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
options:
max-size: "10m"
max-file: "5"
ports:
# localhost only: NPM reaches this as scout-website:8000 over the docker
# network, so publishing on 0.0.0.0 only bought a LAN path around the proxy.
- "127.0.0.1:8132:8000"
environment:
- NTFY_URL=${NTFY_URL}
- SHEET_ID=${SHEET_ID}
- NTFY_BASE=${NTFY_BASE}
- NTFY_TOPIC=${NTFY_TOPIC}
- NTFY_TOKEN=${NTFY_TOKEN}
- ADMIN_TOKEN=${ADMIN_TOKEN}
- EVENTS_FEED_URL=${EVENTS_FEED_URL}
volumes:
- /srv/scout-website/data:/data
- /srv/scout-website/secrets/service_account.json:/app/service_account.json:ro
- /srv/scout-website-assets/img:/app/static/img:ro
- /srv/scout-website-assets/docs:/docs:ro
networks:
- default
- npm
networks:
npm:
external: true
name: arrstack_arr_net