The app ran as root, and the production image contained gcc, vim, wget and gnupg. Code execution in the container would have allowed changing the app code and fetching or compiling further tools. - Multi-stage build: the wheels (psycopg2) are built in a build stage. The runtime image only contains libpq5, postgresql-client and wait-for-it (724 MB -> 298 MB). - The app runs as the system user max (uid/gid 10001). The code is owned by root and precompiled, the user has no writable directory. - Log to stdout only; docker collects it. The log file under /app/log was lost on every container recreation anyway. - docker-compose: read-only filesystem (tmpfs for /tmp), no capabilities, no-new-privileges and log rotation (5 x 10 MB). - Add a .dockerignore and set MAX_ORIGIN in docker-compose.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
1.2 KiB
Docker
40 lines
1.2 KiB
Docker
# Build stage: compile the wheels (psycopg2 needs gcc and libpq-dev)
|
|
FROM python:3.15-slim-trixie AS build
|
|
|
|
RUN apt-get update && \
|
|
apt-get install --no-install-recommends -y gcc libc-dev libpq-dev && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /build
|
|
COPY requirements.txt .
|
|
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
|
|
|
|
# Runtime stage: no compiler and no tools besides what db-setup.sh needs
|
|
FROM python:3.15-slim-trixie
|
|
|
|
RUN apt-get update && apt-get upgrade -y && \
|
|
apt-get install --no-install-recommends -y libpq5 postgresql-client wait-for-it && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY --from=build /wheels /wheels
|
|
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
|
|
|
|
RUN groupadd --system --gid 10001 max && \
|
|
useradd --system --uid 10001 --gid max --no-create-home --shell /usr/sbin/nologin max
|
|
|
|
WORKDIR /app
|
|
|
|
ENV PYTHONPATH=/app/
|
|
ENV FLASK_APP=max.app
|
|
|
|
# Owned by root, so the app cannot modify its own code
|
|
COPY max/ ./max
|
|
COPY scripts/prod/ .
|
|
# Precompile, since the app cannot write __pycache__ at runtime
|
|
RUN python -m compileall -q max
|
|
|
|
USER max
|
|
|
|
ENTRYPOINT ["./entrypoint.sh"]
|
|
CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "max.app:app"]
|