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: