diff --git a/Dockerfile b/Dockerfile index d3b4de5..dc145e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,48 @@ -FROM python:3.12-slim - +# ============================================ +# 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. -ENV AHDX_DATA=/data VOLUME ["/data"] EXPOSE 8080 -CMD ["python", "app.py"] +USER nonroot +# Exec form — distroless has no shell to interpret a string CMD. +ENTRYPOINT ["python3", "app.py"]