fix: pin the Python patch version and run the app as a non-root user
Two follow-ups to the review of the base image change. 1. Pin the patch version: `python:3.14-slim` is a floating tag, so a rebuild could silently pick up a different base image. It is now `python:3.14.10-slim`, so a rebuild is reproducible. 2. Run the app as an unprivileged user. The app only reads its own source and talks to Mongo over the network — it writes no files — so it does not need to be root. A `beer` system user is created and the image ends with `USER beer`.
This commit is contained in:
+7
-3
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.14-slim AS beer_base_image
|
FROM python:3.14.10-slim AS beer_base_image
|
||||||
|
|
||||||
WORKDIR /beer_data
|
WORKDIR /beer_data
|
||||||
|
|
||||||
@@ -12,8 +12,12 @@ COPY requirements.txt .
|
|||||||
# The Python version matters:
|
# The Python version matters:
|
||||||
# - pymongo 4.19 requires Python >= 3.11, so the base image could not stay
|
# - pymongo 4.19 requires Python >= 3.11, so the base image could not stay
|
||||||
# on 3.10 once the requirements were pinned.
|
# on 3.10 once the requirements were pinned.
|
||||||
# - 3.14 is the current stable release, so the image is not carrying the
|
# - 3.14 is a stable release (first released 2025-10-07, in its bugfix
|
||||||
# CVEs fixed in 3.11, 3.12 and 3.13.
|
# phase until 2030-10), so the image is not carrying the CVEs fixed in
|
||||||
|
# 3.11, 3.12 and 3.13.
|
||||||
|
# - The patch version is pinned rather than the floating `3.14-slim` tag so
|
||||||
|
# a rebuild is reproducible and does not silently pick up a base image
|
||||||
|
# change.
|
||||||
RUN pip install --upgrade pip --no-cache-dir
|
RUN pip install --upgrade pip --no-cache-dir
|
||||||
|
|
||||||
RUN pip --default-timeout=1000 install --no-cache-dir -r requirements.txt
|
RUN pip --default-timeout=1000 install --no-cache-dir -r requirements.txt
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ ADD src /beer_data/src
|
|||||||
# (``import brewing``), so that directory is the working directory.
|
# (``import brewing``), so that directory is the working directory.
|
||||||
WORKDIR /beer_data/src/flask
|
WORKDIR /beer_data/src/flask
|
||||||
|
|
||||||
|
# Run as an unprivileged user. The app only needs to read its own source
|
||||||
|
# and talk to Mongo over the network — it writes no files — so it does not
|
||||||
|
# need to be root.
|
||||||
|
RUN useradd --system --create-home --home-dir /home/beer --shell /usr/sbin/nologin beer \
|
||||||
|
&& chown -R beer:beer /beer_data
|
||||||
|
|
||||||
# The app is served by waitress, not the Flask development server.
|
# The app is served by waitress, not the Flask development server.
|
||||||
# FLASK_DEBUG stays OFF: the old entrypoint ran the Werkzeug debugger in
|
# FLASK_DEBUG stays OFF: the old entrypoint ran the Werkzeug debugger in
|
||||||
# production, which exposes an interactive console on any unhandled error.
|
# production, which exposes an interactive console on any unhandled error.
|
||||||
@@ -21,6 +27,8 @@ ENV PORT=5000
|
|||||||
# session-signing key.
|
# session-signing key.
|
||||||
# ENV SECRET_KEY=...
|
# ENV SECRET_KEY=...
|
||||||
|
|
||||||
|
USER beer
|
||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
ENTRYPOINT ["python", "-m", "waitress", "--listen=0.0.0.0:5000", "--call", "beer_data:create_app"]
|
ENTRYPOINT ["python", "-m", "waitress", "--listen=0.0.0.0:5000", "--call", "beer_data:create_app"]
|
||||||
|
|||||||
Reference in New Issue
Block a user