You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2026/10/20 09:14:19 UTC
(airflow) 08/20: Keep a second hardened Python out of the trixie images
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch hardened-python-base-images
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit a81cba26587b64629c6c71ab5f009f246ce52fb8
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Oct 10 14:04:03 2026 +0100
Keep a second hardened Python out of the trixie images
On trixie, libenchant-2-dev's build dependencies need python3, which Docker's package repository satisfies with its own python-3.13 - a second interpreter in the 3.12 image that broke building C extensions. pyenchant only needs the runtime library, which it finds through the ldconfig cache, and the guard now fails early on any hardened Python other than the image's own.
Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_014np1fVAxHmGaBFt3qebJ8x
---
Dockerfile | 19 +++++++++++++++++++
Dockerfile.ci | 26 ++++++++++++++++++++++----
scripts/docker/install_os_dependencies.sh | 19 +++++++++++++++++++
3 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 3360811687e..f3d576580da 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -131,6 +131,8 @@ if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
# The Debian 12 hardened images ship Python under /opt/python, the Debian 13 ones install it as
# Debian packages under /usr - so ask the base image's Python where it lives.
PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; print(sys.base_prefix)')}
+ # Read before apt runs: a package depending on python3 can pull another Python version in.
+ BASE_PYTHON_MAJOR_MINOR=$(python3 -c 'import sys; print(f"{sys.version_info[0]}.{sys.version_info[1]}")')
elif [[ "${AIRFLOW_IMAGE_FLAVOR}" == "legacy" ]]; then
PYTHON_HOME=${PYTHON_HOME:-/usr/python}
else
@@ -436,6 +438,23 @@ function check_no_system_python() {
# /usr/python/lib/python3.11/lib-dynload/_ssl.cpython-311-aarch64-linux-gnu.so: undefined symbol: _PyModule_Add
# Debian names its Python libraries "libpython3.13". The trixie hardened images register their own
# Python with dpkg as "libpython-3.13", which is the Python we want, so it must not match.
+ # Debian 13 hardened images package their Python as "python-3.13" and serve other versions from
+ # their apt repository, so a package depending on python3 can also pull in a hardened Python of a
+ # different version than the image's own.
+ local other_hardened_python=""
+ if [[ -n "${BASE_PYTHON_MAJOR_MINOR=}" ]]; then
+ other_hardened_python=$(dpkg -l | awk '/^ii (lib)?python-3\.[0-9]+/ {print $2}' \
+ | grep -v -E "python-${BASE_PYTHON_MAJOR_MINOR//./\\.}(-|:|$)" || true)
+ fi
+ if [[ -n "${other_hardened_python}" ]]; then
+ echo
+ echo "ERROR! A Python other than the image's ${BASE_PYTHON_MAJOR_MINOR} was installed: ${other_hardened_python//$'\n'/ }"
+ echo
+ apt-get install -yqq aptitude >/dev/null
+ aptitude why "$(echo "${other_hardened_python}" | head -1 | cut -d: -f1)"
+ echo
+ exit 1
+ fi
if dpkg -l | grep -E '^ii libpython3\.[0-9]+' >/dev/null; then
echo
echo "ERROR! System python is installed by one of the previous steps"
diff --git a/Dockerfile.ci b/Dockerfile.ci
index a5acf8ffc24..8681188656c 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -67,6 +67,8 @@ if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
# The Debian 12 hardened images ship Python under /opt/python, the Debian 13 ones install it as
# Debian packages under /usr - so ask the base image's Python where it lives.
PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; print(sys.base_prefix)')}
+ # Read before apt runs: a package depending on python3 can pull another Python version in.
+ BASE_PYTHON_MAJOR_MINOR=$(python3 -c 'import sys; print(f"{sys.version_info[0]}.{sys.version_info[1]}")')
elif [[ "${AIRFLOW_IMAGE_FLAVOR}" == "legacy" ]]; then
PYTHON_HOME=${PYTHON_HOME:-/usr/python}
else
@@ -372,6 +374,23 @@ function check_no_system_python() {
# /usr/python/lib/python3.11/lib-dynload/_ssl.cpython-311-aarch64-linux-gnu.so: undefined symbol: _PyModule_Add
# Debian names its Python libraries "libpython3.13". The trixie hardened images register their own
# Python with dpkg as "libpython-3.13", which is the Python we want, so it must not match.
+ # Debian 13 hardened images package their Python as "python-3.13" and serve other versions from
+ # their apt repository, so a package depending on python3 can also pull in a hardened Python of a
+ # different version than the image's own.
+ local other_hardened_python=""
+ if [[ -n "${BASE_PYTHON_MAJOR_MINOR=}" ]]; then
+ other_hardened_python=$(dpkg -l | awk '/^ii (lib)?python-3\.[0-9]+/ {print $2}' \
+ | grep -v -E "python-${BASE_PYTHON_MAJOR_MINOR//./\\.}(-|:|$)" || true)
+ fi
+ if [[ -n "${other_hardened_python}" ]]; then
+ echo
+ echo "ERROR! A Python other than the image's ${BASE_PYTHON_MAJOR_MINOR} was installed: ${other_hardened_python//$'\n'/ }"
+ echo
+ apt-get install -yqq aptitude >/dev/null
+ aptitude why "$(echo "${other_hardened_python}" | head -1 | cut -d: -f1)"
+ echo
+ exit 1
+ fi
if dpkg -l | grep -E '^ii libpython3\.[0-9]+' >/dev/null; then
echo
echo "ERROR! System python is installed by one of the previous steps"
@@ -1874,11 +1893,10 @@ RUN echo "Base image version: ${BASE_IMAGE}"
ARG DEV_APT_COMMAND=""
ARG ADDITIONAL_DEV_APT_COMMAND=""
ARG ADDITIONAL_DEV_ENV_VARS=""
-# libenchant-2-dev is needed on top of libenchant-2-2: pyenchant locates the library via
-# ctypes.util.find_library(), which without ldconfig in the hardened base falls back to
-# linking with gcc and so needs the unversioned libenchant-2.so symlink the -dev package ships.
+# No libenchant-2-dev: pyenchant finds libenchant-2-2 through the ldconfig cache, and on trixie the
+# -dev package's build dependencies pull in a second Python from the hardened images' repository.
ARG ADDITIONAL_DEV_APT_DEPS="bash-completion dumb-init git gdb graphviz krb5-user \
- less libenchant-2-2 libenchant-2-dev libgeos-dev libpq-dev net-tools \
+ less libenchant-2-2 libgeos-dev libpq-dev net-tools \
netcat-openbsd openssh-server postgresql-client rsync tmux \
unzip vim xxd"
diff --git a/scripts/docker/install_os_dependencies.sh b/scripts/docker/install_os_dependencies.sh
index b1083387b6b..99c1c07b396 100644
--- a/scripts/docker/install_os_dependencies.sh
+++ b/scripts/docker/install_os_dependencies.sh
@@ -42,6 +42,8 @@ if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
# The Debian 12 hardened images ship Python under /opt/python, the Debian 13 ones install it as
# Debian packages under /usr - so ask the base image's Python where it lives.
PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; print(sys.base_prefix)')}
+ # Read before apt runs: a package depending on python3 can pull another Python version in.
+ BASE_PYTHON_MAJOR_MINOR=$(python3 -c 'import sys; print(f"{sys.version_info[0]}.{sys.version_info[1]}")')
elif [[ "${AIRFLOW_IMAGE_FLAVOR}" == "legacy" ]]; then
PYTHON_HOME=${PYTHON_HOME:-/usr/python}
else
@@ -347,6 +349,23 @@ function check_no_system_python() {
# /usr/python/lib/python3.11/lib-dynload/_ssl.cpython-311-aarch64-linux-gnu.so: undefined symbol: _PyModule_Add
# Debian names its Python libraries "libpython3.13". The trixie hardened images register their own
# Python with dpkg as "libpython-3.13", which is the Python we want, so it must not match.
+ # Debian 13 hardened images package their Python as "python-3.13" and serve other versions from
+ # their apt repository, so a package depending on python3 can also pull in a hardened Python of a
+ # different version than the image's own.
+ local other_hardened_python=""
+ if [[ -n "${BASE_PYTHON_MAJOR_MINOR=}" ]]; then
+ other_hardened_python=$(dpkg -l | awk '/^ii (lib)?python-3\.[0-9]+/ {print $2}' \
+ | grep -v -E "python-${BASE_PYTHON_MAJOR_MINOR//./\\.}(-|:|$)" || true)
+ fi
+ if [[ -n "${other_hardened_python}" ]]; then
+ echo
+ echo "ERROR! A Python other than the image's ${BASE_PYTHON_MAJOR_MINOR} was installed: ${other_hardened_python//$'\n'/ }"
+ echo
+ apt-get install -yqq aptitude >/dev/null
+ aptitude why "$(echo "${other_hardened_python}" | head -1 | cut -d: -f1)"
+ echo
+ exit 1
+ fi
if dpkg -l | grep -E '^ii libpython3\.[0-9]+' >/dev/null; then
echo
echo "ERROR! System python is installed by one of the previous steps"