Three-stage build. A python:3.12-slim-bookworm stage installs Flask and stages the interpreter's link libraries; the runtime is gcr.io/distroless/cc-debian12 with /usr/local copied over, so we ship a current Python without the shell, apt, perl and tar that make up most of a slim image's post-exploitation toolkit. Bookworm on the build side so glibc matches the debian12 runtime. We deliberately do not use gcr.io/distroless/python3-debian12 — its pinned interpreter carries a larger CVE count than the slim image it would replace. The container now runs as nonroot (65532), so /data is created and chowned in the build stage: registry.db, databases/, uploads/ and inbox/ are written there at startup and there is no shell left to fix ownership at runtime. CMD becomes an exec-form ENTRYPOINT for the same reason. Note for deploys: docker-compose.yml bind-mounts ./data:/data, and a host-owned directory will need to be writable by uid 65532.
49 lines
2.0 KiB
Docker
49 lines
2.0 KiB
Docker
# ============================================
|
|
# STAGE 1: Build the Python deps on bookworm-slim
|
|
# ============================================
|
|
# Bookworm so glibc matches the distroless/debian12 runtime below. Flask is pure
|
|
# Python, so no apt build tools are needed here.
|
|
FROM python:3.12-slim-bookworm AS pybuild
|
|
WORKDIR /app
|
|
|
|
# Install deps first so this layer caches when only the code changes.
|
|
COPY requirements.txt .
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
# Stage the shared libs the Python interpreter links against into an arch-neutral
|
|
# dir (the glob matches whatever multiarch triplet — x86_64 in CI, aarch64 on an
|
|
# arm dev box), plus the writable data dir owned by the distroless nonroot uid.
|
|
RUN set -eux; \
|
|
mkdir -p /rootfs/pylib /rootfs/data; \
|
|
cp -aL /usr/lib/*-linux-gnu/*.so* /rootfs/pylib/ 2>/dev/null || true; \
|
|
chown -R 65532:65532 /rootfs/data
|
|
|
|
# ============================================
|
|
# STAGE 2: Distroless runtime (no shell / apt / perl / tar; runs as nonroot)
|
|
# ============================================
|
|
# distroless/cc carries glibc + libgcc + libstdc++; we bring our own Python 3.12
|
|
# and its link libs. Note we do NOT use distroless/python3-debian12 — it ships a
|
|
# far larger CVE surface than the slim image it would replace; copying
|
|
# /usr/local out of the pybuild stage gives us a current interpreter instead.
|
|
FROM gcr.io/distroless/cc-debian12
|
|
WORKDIR /app
|
|
COPY --from=pybuild /usr/local /usr/local
|
|
COPY --from=pybuild /rootfs/pylib /opt/pylib
|
|
# registry.db, databases/, uploads/ and inbox/ are all created under /data at
|
|
# startup. Distroless has no shell, so ownership has to be set at COPY time.
|
|
COPY --from=pybuild --chown=65532:65532 /rootfs/data /data
|
|
|
|
ENV LD_LIBRARY_PATH=/usr/local/lib:/opt/pylib \
|
|
PATH=/usr/local/bin:/usr/bin:/bin \
|
|
AHDX_DATA=/data
|
|
|
|
COPY . .
|
|
|
|
# All state lives here, mounted as a volume from the host.
|
|
VOLUME ["/data"]
|
|
EXPOSE 8080
|
|
|
|
USER nonroot
|
|
# Exec form — distroless has no shell to interpret a string CMD.
|
|
ENTRYPOINT ["python3", "app.py"]
|