Repository navigation
[FEATURE] Improve container security and harden your supply chain #109
Description
Summary
- container image is not using multi-layers
- packages (hadolint, trivy) are pulled insecurely thru an install Bash script
- python dependencies are not handled very well
- deps are pinned by version, not pinned by SHA digest
- you're running a full (albeit Slim) Debian version instead of just a runtime for python
Problem Statement
The current setup is susceptible to an easy supply chain attack, the container can be tainted when remote Bash scripts are compromised, and the final container image is bloated when only a python executable is needed.
Proposed Solution
I would like to suggest a Dockerfile like this:
Security could be further improved by relying on hardened images from echo.ai, dhi.io or chainguard and their competitors.
# STAGE 1: Extract Hadolint Binary (Pinned by Digest)
FROM hadolint/hadolint@sha256:7f999c0b784a95777a102c0b64d1f2e915155f86640523e1003666f7f2b988f5 AS hadolint-stage
# STAGE 2: Extract Trivy Binary (Pinned by Digest)
FROM aquasec/trivy@sha256:4d60c4a4521759600e12d46e8c7512d7c07e997455a297924d55b084937a4e6e AS trivy-stage
# STAGE 3: Secure Python Builder (Pinned by Digest)
FROM python@sha256:7b57b98150499e9842f2ed73602f357591c0e3c8868bf030432bc13346cf6e15 AS builder
# Install build-time dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \ # when needed
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir --upgrade pip setuptools wheel
# Supply Chain Protection: Install dependencies with strict hash verification
# Create hashes using: pip-compile --generate-hashes <setup.py or pyproject.toml>
COPY requirements.txt .
RUN pip install --no-cache-dir --require-hashes -r requirements.txt
COPY . .
RUN pip install --no-cache-dir --no-deps .
# STAGE 4: Final Distroless Runtime (Pinned by Digest)
FROM gcr.io/distroless/python3-debian12@sha256:5646cf896dafe95def30420defa8077fc8ee71ef5578e2c018c2572aae0541e5
COPY --from=builder /opt/venv /opt/venv
COPY --from=hadolint-stage /usr/bin/hadolint /usr/local/bin/hadolint
COPY --from=trivy-stage /usr/local/bin/trivy /usr/local/bin/trivy
ENV PATH="/opt/venv/bin:$PATH"
ENV PYTHONUNBUFFERED=1
WORKDIR /github/workspace
ENTRYPOINT ["python", "-m", "docksec"]Alternatives Considered
Looking at the entrypoint script, you may need to have that present and you can't simply run docksec.
- implement the parameter handling cleanly in your python program
- revert the distroless final image to a python Slim image but use a non-root (!) user
# Alternative Final Stage if entrypoint.sh is mandatory:
FROM python:3.12-slim AS final
# Create a dedicated non-root user/group
RUN groupadd -g 10001 appuser && \
useradd -u 10001 -g appuser -s /sbin/nologin -c "App User" appuser
COPY --from=hadolint-stage /usr/bin/hadolint /usr/local/bin/hadolint
COPY --from=trivy-stage /usr/local/bin/trivy /usr/local/bin/trivy
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh && chown appuser:appuser /entrypoint.sh
# Drop privileges completely
USER appuser
ENTRYPOINT ["/entrypoint.sh"]Even better for python, would be to switch using uv for dependency mgmt:
# STAGE 1: Extract Binaries (Pinned)
FROM hadolint/hadolint@sha256:7f999c0b784a95777a102c0b64d1f2e915155f86640523e1003666f7f2b988f5 AS hadolint
FROM aquasec/trivy@sha256:4d60c4a4521759600e12d46e8c7512d7c07e997455a297924d55b084937a4e6e AS trivy
FROM ghcr.io/astral-sh/uv:0.5@sha256:4624738479e0f6f059e09d57a22ef45b78f4477b949f91a5ec407a51357f8976 AS uv
# STAGE 2: Builder
FROM python@sha256:7b57b98150499e9842f2ed73602f357591c0e3c8868bf030432bc13346cf6e15 AS builder
# Copy uv from its official image
COPY --from=uv /uv /bin/uv
WORKDIR /app
# Enable bytecode compilation and optimization
ENV UV_COMPILE_BYTECODE=1
# Prevent uv from using the internet for deps; ensure we rely on the lockfile
ENV UV_FROZEN=1
COPY pyproject.toml uv.lock ./
# Install dependencies into /opt/venv
RUN uv venv /opt/venv && \
. /opt/venv/bin/activate && \
uv sync --frozen --no-dev
# Copy source code and install
COPY . .
RUN . /opt/venv/bin/activate && uv pip install --no-deps .
# STAGE 3: Final Distroless Runtime
FROM gcr.io/distroless/python3-debian12@sha256:5646cf896dafe95def30420defa8077fc8ee71ef5578e2c018c2572aae0541e5
COPY --from=builder /opt/venv /opt/venv
COPY --from=hadolint /usr/bin/hadolint /usr/local/bin/hadolint
COPY --from=trivy /usr/local/bin/trivy /usr/local/bin/trivy
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /github/workspace
ENTRYPOINT ["python", "-m", "docksec"]Use Case
Who would benefit from this feature?
- Individual developers
- DevOps teams
- Security teams
- CI/CD pipelines
- Enterprise users
- Other:
Acceptance Criteria
What does done look like?
- docksec parameters still work
- final image is smaller and more secure (CVE)
- implement
uvand add a 5-day back-off for newer updates
[tool.uv]
# Ignore any package version published within the last 5 days
exclude-newer = "5 days"
I'm more than happy to create a PR but I'm not a python developer, mostly a security engineer 😊 but I'm able to lend a hand if you get stuck on the security aspects when the python part works .
Activity
pfarikrispy commentedon Jul 28, 2026
ping
jyotish6699 commentedon Oct 15, 2026
I'd like to contribute to this issue. Since it covers several changes to the Docker build and dependency management, would it be okay to start with one part, such as pinning the image digests, and then handle the remaining changes separately if needed?
pfarikrispy commentedon Oct 15, 2026
ashhadali10 commentedon Oct 18, 2026
Hi @pfarikrispy
I had like to contribute to this issue from the container security side.
Since image digest pinning is already being taken up would it be okay if I investigate non root container hardening and the related smoke tests as a separate, focused contribution?
I wll first review the existing Dockerfile, entrypoint, workspace permissions, and current tests. I'll then propose a minimal change and verify that the Docker image builds and the existing CLI parameters continue to work.
Please let me know if this scope is still available or if you hd prefer another part of the issue.
Thanks
pfarikrispy commentedon Oct 18, 2026
ashhadali10 commentedon Oct 21, 2026
Hi @pfarikrispy
Friendly ping PR #201 is ready for review when you have a moment. The CI
workflows are still waiting for maintainer approval, so the checks haven't
run yet.
One-line summary of what this does: Drops privileges to a non-root
docksec user (UID/GID 10001) for standalone
docker run, while keepingroot for GitHub Docker container actions (required for
GITHUB_WORKSPACE),fixes the missing
dockerCLI (Debian trixie no longer shipsdocker.io),matches the container
dockergroup to the host socket GID, and adds smoketests for all four modes.
Locally verified: pytest tests/ → 546 passed; bash tests/smoke-test-nonroot.sh
→ all 4 tests pass. Happy to iterate on any feedback.
Thanks