diff --git a/Dockerfile b/Dockerfile index 08d810a..42c1159 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM python:3.11-slim AS base +FROM python:3.11.16-slim AS base # Install uv (fast Python package manager) from official slim image. -COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ +COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /uvx /usr/local/bin/ # System libs required by WeasyPrint at runtime. RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -13,6 +13,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ shared-mime-info \ && rm -rf /var/lib/apt/lists/* +# Non-root user the app runs as at runtime. +RUN useradd -u 10001 app + WORKDIR /app # Copy lockfile + project metadata first to maximize Docker layer cache. @@ -27,7 +30,10 @@ RUN uv sync --frozen --no-dev --extra server COPY src/ ./src/ # Uploads directory (mounted as a volume in production). -RUN mkdir -p /app/uploads/images /app/uploads/pdfs /app/uploads/logos /app/uploads/reports +RUN mkdir -p /app/uploads/images /app/uploads/pdfs /app/uploads/logos /app/uploads/reports \ + && chown -R app:app /app + +USER app EXPOSE 8000 diff --git a/Dockerfile.frontend b/Dockerfile.frontend index c2e4a78..027eb88 100644 --- a/Dockerfile.frontend +++ b/Dockerfile.frontend @@ -1,13 +1,25 @@ -FROM python:3.11-slim AS base +# TailwindCSS built in its own stage, from the official Node image, instead +# of `curl | bash` against nodesource inside the final image (point 2, C2). +FROM node:20.20.2-bookworm-slim AS tailwind-build + +WORKDIR /app/flask_app +COPY src/frontend/flask_app/ ./ + +# Versione esatta, non `tailwindcss@3`. Con il vincolo aperto ogni ricostruzione +# dell'immagine poteva prendere una minor diversa e produrre un CSS diverso senza +# che nessuno l'avesse validato: per un sistema che produce evidenze per audit +# ISO 9001 / IATF 16949 è lo stesso problema che il punto 12 risolve per le +# librerie del browser. Aggiornarla è una decisione, non un effetto collaterale. +RUN npm install tailwindcss@3.4.19 && \ + npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --minify + +FROM python:3.11.16-slim AS base # uv from the official slim image (fast Python package manager). -COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ +COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /uvx /usr/local/bin/ -# Node.js 20 is needed at build time to compile TailwindCSS. -RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \ - && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ - && apt-get install -y --no-install-recommends nodejs \ - && rm -rf /var/lib/apt/lists/* +# Non-root user the app runs as at runtime. +RUN useradd -u 10001 app WORKDIR /app @@ -16,22 +28,17 @@ COPY pyproject.toml uv.lock ./ COPY .python-version ./ RUN uv sync --frozen --no-dev --extra client -# Copy the Flask app sources. +# Copy the Flask app sources, then overlay the TailwindCSS build produced in +# the node stage above — the final image never installs Node. COPY src/frontend/flask_app/ ./flask_app/ - -# Build TailwindCSS (one-shot; no watcher in production image). -# -# Versione esatta, non `tailwindcss@3`. Con il vincolo aperto ogni ricostruzione -# dell'immagine poteva prendere una minor diversa e produrre un CSS diverso senza -# che nessuno l'avesse validato: per un sistema che produce evidenze per audit -# ISO 9001 / IATF 16949 è lo stesso problema che il punto 12 risolve per le -# librerie del browser. Aggiornarla è una decisione, non un effetto collaterale. -WORKDIR /app/flask_app -RUN npm install tailwindcss@3.4.19 && \ - npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --minify +COPY --from=tailwind-build /app/flask_app/static/css/tailwind.css ./flask_app/static/css/tailwind.css # Compile Flask-Babel translation catalogs. -RUN uv run --project /app pybabel compile -d translations +WORKDIR /app/flask_app +RUN uv run --project /app pybabel compile -d translations && \ + chown -R app:app /app + +USER app EXPOSE 5000 diff --git a/Dockerfile.vision b/Dockerfile.vision index 7e29cb1..7cfffed 100644 --- a/Dockerfile.vision +++ b/Dockerfile.vision @@ -1,4 +1,4 @@ -FROM python:3.13-slim +FROM python:3.13.15-slim # opencv-python (pulled in transitively by vs-core/vs-task/vs-measure/vs-pm2d) # needs libGL and glib at runtime even though it is never imported by name here. @@ -6,7 +6,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libgl1 libglib2.0-0 \ && rm -rf /var/lib/apt/lists/* -COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv +COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /usr/local/bin/uv + +# Non-root user the app runs as at runtime. Same UID as the server's Dockerfile +# so both containers agree on ownership of the shared uploads volume (point 13). +RUN useradd -u 10001 app WORKDIR /app COPY pyproject.toml uv.lock ./ @@ -34,6 +38,9 @@ RUN test -n "$VISION_ENGINE_VERSION" || { \ } ENV VISION_ENGINE_VERSION=${VISION_ENGINE_VERSION} +RUN chown -R app:app /app +USER app + EXPOSE 8100 # Two workers, not four: each holds the vision stack in memory.