Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/python/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "python",
"version": "1.7.1",
"version": "1.8.0",
"name": "Python",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/python",
"description": "Installs the provided version of Python, as well as PIPX, and other common Python utilities. JupyterLab is conditionally installed with the python feature. Note: May require source code compilation.",
Expand Down
175 changes: 167 additions & 8 deletions src/python/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ if [ "${ADJUSTED_ID}" = "rhel" ] && [ "${VERSION_CODENAME-}" = "centos7" ]; then
fi

# To find some devel packages, some rhel need to enable specific extra repos, but not on RedHat ubi images...
INSTALL_CMD_ADDL_REPO=""
INSTALL_CMD_ADDL_REPOS=""
if [ ${ADJUSTED_ID} = "rhel" ] && [ ${ID} != "rhel" ]; then
if [ ${MAJOR_VERSION_ID} = "8" ]; then
INSTALL_CMD_ADDL_REPOS="--enablerepo powertools"
Expand All @@ -93,6 +93,27 @@ else
INSTALL_CMD="${PKG_MGR_CMD} ${INSTALL_CMD_ADDL_REPOS} -y install --noplugins --setopt=install_weak_deps=0"
fi

# Install Time::Piece Perl module required by OpenSSL 3.0.18+ build system on CentOS 7/RHEL 7
install_time_piece() {
echo "(*) Ensuring Time::Piece Perl module is available for OpenSSL 3.0.18+ build..."

# Check if Time::Piece is already available (it's usually in Perl core)
if perl -MTime::Piece -e 'exit 0' 2>/dev/null; then
echo "(*) Time::Piece already available"
return 0
fi

echo "(*) Time::Piece not found, installing perl-Time-Piece package..."

# Install perl-Time-Piece package for CentOS 7/RHEL 7
if ${INSTALL_CMD} perl-Time-Piece; then
echo "(*) perl-Time-Piece installed for OpenSSL 3.0.18+ build"
else
echo "(!) Failed to install perl-Time-Piece package. This will cause OpenSSL 3.0.18+ build to fail"
return 1
fi
}

# Clean up
clean_up() {
case ${ADJUSTED_ID} in
Expand Down Expand Up @@ -478,6 +499,130 @@ install_cpython() {
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}" "${cpython_tgz_url}"
fi
}
# Get system architecture for downloads
get_architecture() {
local architecture=""
case $(uname -m) in
x86_64) architecture="amd64" ;;
aarch64 | armv8*) architecture="arm64" ;;
aarch32 | armv7* | armvhf*) architecture="armhf" ;;
i?86) architecture="386" ;;
*) echo "(!) Architecture $(uname -m) unsupported"; exit 1 ;;
esac
echo ${architecture}
}

# Install cosign with multi-distro support
install_cosign() {

COSIGN_VERSION="latest"
local cosign_url='https://github.com/sigstore/cosign'
local architecture=$(get_architecture)

find_version_from_git_tags COSIGN_VERSION "${cosign_url}"

# Remove 'v' prefix if present for download URL
local version_for_url="${COSIGN_VERSION#v}"

local cosign_filename="/tmp/cosign_${version_for_url}_${architecture}.deb"
local cosign_url="https://github.com/sigstore/cosign/releases/download/v${version_for_url}/cosign_${version_for_url}_${architecture}.deb"

echo "Downloading cosign from: ${cosign_url}"

if curl -L -f --fail-with-body "${cosign_url}" -o "$cosign_filename" 2>/dev/null; then
echo "(*) Successfully downloaded cosign v${COSIGN_VERSION}"
else
echo -e "\n(!) Failed to fetch cosign v${COSIGN_VERSION}..."
# Try previous version
find_prev_version_from_git_tags COSIGN_VERSION "https://github.com/sigstore/cosign"
echo -e "\nAttempting to install previous cosign ${COSIGN_VERSION} version as fallback mechanism"

version_for_url="${COSIGN_VERSION#v}"
cosign_filename="/tmp/cosign_${version_for_url}_${architecture}.deb"
cosign_url="https://github.com/sigstore/cosign/releases/download/v${version_for_url}/cosign_${version_for_url}_${architecture}.deb"

if ! curl -L -f --fail-with-body "${cosign_url}" -o "$cosign_filename" 2>/dev/null; then
echo "(!) Failed to download cosign v${COSIGN_VERSION} as fallback"
return 1
fi
fi

# Install the package
if [ -f "$cosign_filename" ]; then
dpkg -i "$cosign_filename"
rm "$cosign_filename"
echo "Installation of cosign succeeded with ${COSIGN_VERSION}."
else
echo "(!) Failed to install cosign package"
return 1
fi

}

# COSIGN signature verification for python versions >= 3.14
cosign_verification() {
local VERSION="$1"

# Ensure cosign is installed
if ! type cosign > /dev/null 2>&1; then
echo "(*) cosign not found, installing..."
if ! install_cosign; then
echo "(!) Failed to install cosign"
return 1
fi
else
echo "(*) cosign is already available on the system"
fi

echo "(*) Attempting COSIGN verification for Python ${VERSION}..."

# Check if COSIGN signature files exist (these don't exist yet for Python releases)
local cosign_sig_url="${cpython_tgz_url}.sig"
local cosign_cert_url="${cpython_tgz_url}.pem"

# Download COSIGN signature and certificate files with proper error handling
echo "(*) Checking for cosign signature files..."
if ! curl -sSL -f --fail-with-body -o "/tmp/python-src/${cpython_tgz_filename}.sig" "${cosign_sig_url}" 2>/dev/null; then
echo "(!) COSIGN signature file not available for Python ${VERSION}"
echo " Signature URL: ${cosign_sig_url}"
return 1
fi

if ! curl -sSL -f --fail-with-body -o "/tmp/python-src/${cpython_tgz_filename}.pem" "${cosign_cert_url}" 2>/dev/null; then
echo "(!) COSIGN certificate file not available for Python ${VERSION}"
echo " Certificate URL: ${cosign_cert_url}"
return 1
fi

# Perform COSIGN verification
if cosign verify-blob \
--certificate "/tmp/python-src/${cpython_tgz_filename}.pem" \
--signature "/tmp/python-src/${cpython_tgz_filename}.sig" \
--certificate-identity-regexp=".*" \
--certificate-oidc-issuer-regexp=".*" \
"/tmp/python-src/${cpython_tgz_filename}"; then
echo "(*) COSIGN signature verification successful"
return 0
else
echo "(!) COSIGN signature verification failed"
return 1
fi
}

# GPG verification for python versions < 3.14
gpg_verification() {
echo "(*) Using GPG signature verification..."
if [[ ${VERSION_CODENAME} = "centos7" ]] || [[ ${VERSION_CODENAME} = "rhel7" ]]; then
receive_gpg_keys_centos7 PYTHON_SOURCE_GPG_KEYS
else
receive_gpg_keys PYTHON_SOURCE_GPG_KEYS
fi
echo "Downloading ${cpython_tgz_filename}.asc..."
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}.asc" "${cpython_tgz_url}.asc"
gpg --verify "${cpython_tgz_filename}.asc"
echo "(*) GPG signature verification successful"
}


install_from_source() {
VERSION=$1
Expand All @@ -496,6 +641,8 @@ install_from_source() {
case ${VERSION_CODENAME} in
centos7|rhel7)
check_packages perl-IPC-Cmd
# Install Time::Piece Perl module required by OpenSSL 3.0.18+ build system
install_time_piece
install_openssl3
ADDL_CONFIG_ARGS="--with-openssl=${SSL_INSTALL_PATH} --with-openssl-rpath=${SSL_INSTALL_PATH}/lib"
;;
Expand All @@ -507,15 +654,27 @@ install_from_source() {
install_prev_vers_cpython "${VERSION}"
fi
fi;
# Verify signature
if [[ ${VERSION_CODENAME} = "centos7" ]] || [[ ${VERSION_CODENAME} = "rhel7" ]]; then
receive_gpg_keys_centos7 PYTHON_SOURCE_GPG_KEYS

# Discontinuation of PGP signatures for releases of Python 3.14 or future versions
# CPython release artifacts are additionally signed with Sigstore starting with the Python 3.11.0
local major_version=$(echo "$VERSION" | cut -d. -f1)
local minor_version=$(echo "$VERSION" | cut -d. -f2)
echo "(*) Detected Python version: ${major_version}.${minor_version}"
if (( major_version > 3 )) || { (( major_version == 3 )) && (( minor_version >= 14 )); }; then
echo "(*) Python 3.14+ detected. Attempting cosign verification..."
if cosign_verification "$VERSION"; then
echo "(*) COSIGN verification successful."
else
echo "(*) COSIGN verification failed or not available for Python ${VERSION}"
echo "(*) WARNING: Installing Python ${VERSION} without signature verification"
echo "(*) This is expected for newly released versions where cosign signatures are not yet available"
echo "(*) Python 3.14+ discontinued PGP signatures in favor of cosign, but cosign signatures may take time to be published"
fi
else
receive_gpg_keys PYTHON_SOURCE_GPG_KEYS
echo "(*) Python < 3.14 detected. Using GPG signature verification..."
gpg_verification
echo "(*) GPG verification successful."
fi
echo "Downloading ${cpython_tgz_filename}.asc..."
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}.asc" "${cpython_tgz_url}.asc"
gpg --verify "${cpython_tgz_filename}.asc"

# Update min protocol for testing only - https://bugs.python.org/issue41561
if [ -f /etc/pki/tls/openssl.cnf ]; then
Expand Down