services: scheduler: image: irdb-scheduler:latest build: { context: ./scheduler } environment: INTERNAL_JOB_TOKEN: ${INTERNAL_JOB_TOKEN} # SEC_REVIEW F22: dependencies (curl, tini, ca-certificates) are now # baked into the image at build time with pinned versions, against a # digest-pinned alpine base. The previous `image: alpine:3` + # `apk add` at container start trusted the apk mirror on every # restart and would have given a mirror compromise a foothold in the # container that holds INTERNAL_JOB_TOKEN. read_only: true # busybox crond writes a tiny tempfile when the schedule fires; /run # has to be writable for that. Everything else stays read-only. tmpfs: - /run:mode=0755 - /tmp:mode=1777 # busybox crond calls initgroups() before each exec, which needs # CAP_SETGID even when the target user is the same root it is # already running as — full cap_drop crashes it with # "can't set groups: Operation not permitted". Hardening the # process to non-root would mean shipping a custom cron binary; # not worth the maintenance cost given the container has no # persistent volume, no exposed port, and only INTERNAL_JOB_TOKEN # in env. `no-new-privileges` is still useful: there is no setuid # binary in the image and we want to keep it that way. security_opt: - no-new-privileges:true depends_on: api: condition: service_healthy restart: unless-stopped