From 378822682c9757ad42af980d46789bd18755cff0 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Thu, 4 Jun 2026 18:20:38 +0200 Subject: [PATCH] feat(06-04): multi-stage Dockerfile with appuser + docker-compose runtime hardening (D-07/D-08/D-09) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend/Dockerfile: two-stage build (builder/runtime); runtime stage creates appuser uid=1000, copies installed packages from discardable builder stage, runs as non-root USER appuser; no --reload baked in CMD - docker-compose.yml: backend + celery-worker get read_only:true, tmpfs:/tmp:mode=1777, cap_drop:ALL, security_opt:no-new-privileges:true; celery-beat intentionally left unhardened (Pitfall 7 — writes celerybeat-schedule) Co-Authored-By: Claude Sonnet 4.6 --- backend/Dockerfile | 31 ++++++++++++++++++++++++------- docker-compose.yml | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 4270366..8aadd2b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,17 +1,34 @@ -FROM python:3.12-slim +# Stage 1: builder — compiles Python packages that need gcc +FROM python:3.12-slim AS builder -WORKDIR /app +WORKDIR /build -# System deps for PyMuPDF + OCR -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir --prefix=/install -r requirements.txt + +# Stage 2: runtime — lean image with only what the app needs at runtime +FROM python:3.12-slim AS runtime + +RUN apt-get update && apt-get install -y --no-install-recommends \ tesseract-ocr \ libgl1 \ libglib2.0-0 \ && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +COPY --from=builder /install /usr/local -COPY . . +RUN groupadd --gid 1000 appgroup \ + && useradd --uid 1000 --gid appgroup --shell /bin/sh --no-create-home appuser + +WORKDIR /app +COPY --chown=appuser:appgroup . . + +USER appuser EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml index 630e774..ad09851 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,6 +81,13 @@ services: condition: service_healthy redis: condition: service_healthy + read_only: true + tmpfs: + - "/tmp:mode=1777" + cap_drop: + - ALL + security_opt: + - "no-new-privileges:true" celery-worker: build: ./backend @@ -107,7 +114,15 @@ services: condition: service_healthy redis: condition: service_healthy + read_only: true + tmpfs: + - "/tmp:mode=1777" + cap_drop: + - ALL + security_opt: + - "no-new-privileges:true" + # celery-beat: NOT hardened — writes celerybeat-schedule to working directory (Phase 6 Pitfall 7). celery-beat: build: ./backend environment: