2

No Alpine, Ubuntu, Debian, CentOS, yum, apt, apk, pip, etc. - just the bare minimum necessary to run a python program. Of course, it will probably have requirements that need to be installed also, from a requirements.txt file.

Just to be clear, a multistage build would be fine, as long as the final image contains the python program to run, the python interpreter and all its required libraries, etc., and the additional python requirements.

1
  • 3
    Is there an actual purpose to these requirements? You need some form of minimal base with a libc, python interpreter, whatever requirements that drags along, etc. For me the real question is: Why wouldn't you use an existing base? For instance alpine is absolutely tiny! Unless you have an actual reason, it seems like you are only making things difficult for yourself and/or your colleagues. Commented Jan 24, 2021 at 17:54

3 Answers 3

1

Actually it is possible (at least for GoLang static binaries) to have only the application binary and from security perspective it's quite reasonable. If you wish to do the same with python , you will need to compile the python code into a binary, but keep in mind that the glibc has only backward compatibility. Thus , if you decide to compile your python code with cpython or pyinstaller - you need to know the container host OS version and compile on it (compiling on latest Fedora, won't work on RHEL 6 ;) ).

Once you have the python code into binary, you can use a Dockerfile to copy your binary and start it inside the container. Then building the container is as usual.

Here is an example how to build your minimal container via buildah.

4
  • Thanks @hunter86_gb! I was afraid compiling python would be my only option, not because I can't compile it (with the restrictions you mention), but because the developers use that as an argument against making secure, minimal container images. (Alpine, for example, uses musl, which introduces bugs, and includes all sorts of nasty tools for hacking.) I am considering buildah as it seems a much more reasonable and flexible approach to container image building. Commented Jan 24, 2021 at 18:16
  • Actually , it is more secure to have it as binary. Imagine that someone can reach the container and edit the python code ... With a binary , you just need to publish your new version in the repository and then the rolling upgrade is quite easy. Commented Jan 24, 2021 at 18:20
  • Oh, I misunderstood I think - I thought you meant compiling the Python interpreter. Definitely compiling the Python code as you say would be more secure - I hadn't thought of that aspect of it yet. Another reason not to have a distribution with an editor in it for manipulating raw Python code! Commented Jan 24, 2021 at 18:29
  • Also, compiling the python code will allow you to avoid adding any python interpreter. Commented Jan 25, 2021 at 12:50
2

The closest you'll get with Python is Google's distroless project that builds docker images with the minimum necessary to run a specific interpreter. That said, I'd question the goal a bit because yes, attackers may not have a shell or package installer, but they'll still have a full interpreter (python in this case) to use for their exploit.

Realize that without a shell, you won't be able to use the string syntax for RUN, and that python won't be able to shell out to the host, so some things may also break with this approach.

If you switch to a compiled language that can package the result in a single static binary (C, C++, and Go, among others), you'll have a much more secure environment since the only tool inside the container for the attackers to use is the application itself. Even better if you can run that binary with the root filesystem in the container set to read only, and any volumes mounted set to noexec, eliminating the ability of attackers to push their own binaries to run inside the container.

2
  • Very good points. I think I'll be looking at cpython and pyinstaller to create a “compiled” version of the python program. By definition these python programs won't be able to run a shell, of course (but can they fork/exec?). Compiled languages would be all around better, but the programs to be run in containers already exist in Python. I will definitely try your suggestion to make the root file system R/O, and mount with noexec! Commented Jan 24, 2021 at 19:56
  • fork() and exec() are quite fundamental system calls; anything remotely Unix-like will continue to support the system call API unless you separately take extreme measures to also modify the underlying OS. Commented Apr 8, 2021 at 5:04
0

I wanted to create a very small image for my Python applications, but it's difficult to do it from scratch because you cannot build a statically linked Python application so you need to include required libraries (e. g. glibc, libffi, etc.).

I looked at distroless project but then decided to build my own image based off busybox images. Busybox is very small but it's convenient to have some tools like sh, ls, df, etc. inside your image.

Here's the result: Winand/python-base-images. I copy Python installation from Python's official image, clean it, then copy certificates and some essential libraries.

The size is around 35.0MB for glibc version (python:3.12-slim is 123.6MB). and 31.3MB for musl version (python:3.12-alpine is 48.3MB).

Also I remove pip, so you need to mount uv tool to be able to install packages. This example uses pyproject.toml and uv.lock file but it can also install packages from requirements.txt.

FROM ghcr.io/astral-sh/uv:0.5.14 AS uv COPY pyproject.toml uv.lock / FROM python:3.12-musl # https://docs.astral.sh/uv/concepts/python-versions/#disabling-automatic-python-downloads ENV UV_PYTHON_DOWNLOADS=never # for uv sync https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path ENV UV_PROJECT_ENVIRONMENT=/usr/local USER root RUN --mount=from=uv,source=/uv,target=/bin/uv \ --mount=from=uv,source=/pyproject.toml,target=/pyproject.toml \ --mount=from=uv,source=/uv.lock,target=/uv.lock \ # Install dependencies from an existing uv.lock uv sync --frozen --no-cache --no-dev --no-install-project USER app COPY --chown=app:app ./application /application 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.