Skip to content

Commit 6e50893

Browse files
authored
Update Python Feature Installation: Support Sigstore Signatures for Python 3.14+ and Fix OpenSSL Build on CentOS 7/RHEL 7 (#1508)
* cogin verfiy for python * Fix CentOS 7 Python installation by adding missing perl-Time-Piece dependency * update feature version * fix issues
1 parent eea561a commit 6e50893

File tree

2 files changed

+168
-9
lines changed

2 files changed

+168
-9
lines changed

src/python/devcontainer-feature.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"id": "python",
3-
"version": "1.7.1",
3+
"version": "1.8.0",
44
"name": "Python",
55
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/python",
66
"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.",

src/python/install.sh

Lines changed: 167 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ if [ "${ADJUSTED_ID}" = "rhel" ] && [ "${VERSION_CODENAME-}" = "centos7" ]; then
6969
fi
7070

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

96+
# Install Time::Piece Perl module required by OpenSSL 3.0.18+ build system on CentOS 7/RHEL 7
97+
install_time_piece() {
98+
echo "(*) Ensuring Time::Piece Perl module is available for OpenSSL 3.0.18+ build..."
99+
100+
# Check if Time::Piece is already available (it's usually in Perl core)
101+
if perl -MTime::Piece -e 'exit 0' 2>/dev/null; then
102+
echo "(*) Time::Piece already available"
103+
return 0
104+
fi
105+
106+
echo "(*) Time::Piece not found, installing perl-Time-Piece package..."
107+
108+
# Install perl-Time-Piece package for CentOS 7/RHEL 7
109+
if ${INSTALL_CMD} perl-Time-Piece; then
110+
echo "(*) perl-Time-Piece installed for OpenSSL 3.0.18+ build"
111+
else
112+
echo "(!) Failed to install perl-Time-Piece package. This will cause OpenSSL 3.0.18+ build to fail"
113+
return 1
114+
fi
115+
}
116+
96117
# Clean up
97118
clean_up() {
98119
case ${ADJUSTED_ID} in
@@ -478,6 +499,130 @@ install_cpython() {
478499
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}" "${cpython_tgz_url}"
479500
fi
480501
}
502+
# Get system architecture for downloads
503+
get_architecture() {
504+
local architecture=""
505+
case $(uname -m) in
506+
x86_64) architecture="amd64" ;;
507+
aarch64 | armv8*) architecture="arm64" ;;
508+
aarch32 | armv7* | armvhf*) architecture="armhf" ;;
509+
i?86) architecture="386" ;;
510+
*) echo "(!) Architecture $(uname -m) unsupported"; exit 1 ;;
511+
esac
512+
echo ${architecture}
513+
}
514+
515+
# Install cosign with multi-distro support
516+
install_cosign() {
517+
518+
COSIGN_VERSION="latest"
519+
local cosign_url='https://github.com/sigstore/cosign'
520+
local architecture=$(get_architecture)
521+
522+
find_version_from_git_tags COSIGN_VERSION "${cosign_url}"
523+
524+
# Remove 'v' prefix if present for download URL
525+
local version_for_url="${COSIGN_VERSION#v}"
526+
527+
local cosign_filename="/tmp/cosign_${version_for_url}_${architecture}.deb"
528+
local cosign_url="https://github.com/sigstore/cosign/releases/download/v${version_for_url}/cosign_${version_for_url}_${architecture}.deb"
529+
530+
echo "Downloading cosign from: ${cosign_url}"
531+
532+
if curl -L -f --fail-with-body "${cosign_url}" -o "$cosign_filename" 2>/dev/null; then
533+
echo "(*) Successfully downloaded cosign v${COSIGN_VERSION}"
534+
else
535+
echo -e "\n(!) Failed to fetch cosign v${COSIGN_VERSION}..."
536+
# Try previous version
537+
find_prev_version_from_git_tags COSIGN_VERSION "https://github.com/sigstore/cosign"
538+
echo -e "\nAttempting to install previous cosign ${COSIGN_VERSION} version as fallback mechanism"
539+
540+
version_for_url="${COSIGN_VERSION#v}"
541+
cosign_filename="/tmp/cosign_${version_for_url}_${architecture}.deb"
542+
cosign_url="https://github.com/sigstore/cosign/releases/download/v${version_for_url}/cosign_${version_for_url}_${architecture}.deb"
543+
544+
if ! curl -L -f --fail-with-body "${cosign_url}" -o "$cosign_filename" 2>/dev/null; then
545+
echo "(!) Failed to download cosign v${COSIGN_VERSION} as fallback"
546+
return 1
547+
fi
548+
fi
549+
550+
# Install the package
551+
if [ -f "$cosign_filename" ]; then
552+
dpkg -i "$cosign_filename"
553+
rm "$cosign_filename"
554+
echo "Installation of cosign succeeded with ${COSIGN_VERSION}."
555+
else
556+
echo "(!) Failed to install cosign package"
557+
return 1
558+
fi
559+
560+
}
561+
562+
# COSIGN signature verification for python versions >= 3.14
563+
cosign_verification() {
564+
local VERSION="$1"
565+
566+
# Ensure cosign is installed
567+
if ! type cosign > /dev/null 2>&1; then
568+
echo "(*) cosign not found, installing..."
569+
if ! install_cosign; then
570+
echo "(!) Failed to install cosign"
571+
return 1
572+
fi
573+
else
574+
echo "(*) cosign is already available on the system"
575+
fi
576+
577+
echo "(*) Attempting COSIGN verification for Python ${VERSION}..."
578+
579+
# Check if COSIGN signature files exist (these don't exist yet for Python releases)
580+
local cosign_sig_url="${cpython_tgz_url}.sig"
581+
local cosign_cert_url="${cpython_tgz_url}.pem"
582+
583+
# Download COSIGN signature and certificate files with proper error handling
584+
echo "(*) Checking for cosign signature files..."
585+
if ! curl -sSL -f --fail-with-body -o "/tmp/python-src/${cpython_tgz_filename}.sig" "${cosign_sig_url}" 2>/dev/null; then
586+
echo "(!) COSIGN signature file not available for Python ${VERSION}"
587+
echo " Signature URL: ${cosign_sig_url}"
588+
return 1
589+
fi
590+
591+
if ! curl -sSL -f --fail-with-body -o "/tmp/python-src/${cpython_tgz_filename}.pem" "${cosign_cert_url}" 2>/dev/null; then
592+
echo "(!) COSIGN certificate file not available for Python ${VERSION}"
593+
echo " Certificate URL: ${cosign_cert_url}"
594+
return 1
595+
fi
596+
597+
# Perform COSIGN verification
598+
if cosign verify-blob \
599+
--certificate "/tmp/python-src/${cpython_tgz_filename}.pem" \
600+
--signature "/tmp/python-src/${cpython_tgz_filename}.sig" \
601+
--certificate-identity-regexp=".*" \
602+
--certificate-oidc-issuer-regexp=".*" \
603+
"/tmp/python-src/${cpython_tgz_filename}"; then
604+
echo "(*) COSIGN signature verification successful"
605+
return 0
606+
else
607+
echo "(!) COSIGN signature verification failed"
608+
return 1
609+
fi
610+
}
611+
612+
# GPG verification for python versions < 3.14
613+
gpg_verification() {
614+
echo "(*) Using GPG signature verification..."
615+
if [[ ${VERSION_CODENAME} = "centos7" ]] || [[ ${VERSION_CODENAME} = "rhel7" ]]; then
616+
receive_gpg_keys_centos7 PYTHON_SOURCE_GPG_KEYS
617+
else
618+
receive_gpg_keys PYTHON_SOURCE_GPG_KEYS
619+
fi
620+
echo "Downloading ${cpython_tgz_filename}.asc..."
621+
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}.asc" "${cpython_tgz_url}.asc"
622+
gpg --verify "${cpython_tgz_filename}.asc"
623+
echo "(*) GPG signature verification successful"
624+
}
625+
481626

482627
install_from_source() {
483628
VERSION=$1
@@ -496,6 +641,8 @@ install_from_source() {
496641
case ${VERSION_CODENAME} in
497642
centos7|rhel7)
498643
check_packages perl-IPC-Cmd
644+
# Install Time::Piece Perl module required by OpenSSL 3.0.18+ build system
645+
install_time_piece
499646
install_openssl3
500647
ADDL_CONFIG_ARGS="--with-openssl=${SSL_INSTALL_PATH} --with-openssl-rpath=${SSL_INSTALL_PATH}/lib"
501648
;;
@@ -507,15 +654,27 @@ install_from_source() {
507654
install_prev_vers_cpython "${VERSION}"
508655
fi
509656
fi;
510-
# Verify signature
511-
if [[ ${VERSION_CODENAME} = "centos7" ]] || [[ ${VERSION_CODENAME} = "rhel7" ]]; then
512-
receive_gpg_keys_centos7 PYTHON_SOURCE_GPG_KEYS
657+
658+
# Discontinuation of PGP signatures for releases of Python 3.14 or future versions
659+
# CPython release artifacts are additionally signed with Sigstore starting with the Python 3.11.0
660+
local major_version=$(echo "$VERSION" | cut -d. -f1)
661+
local minor_version=$(echo "$VERSION" | cut -d. -f2)
662+
echo "(*) Detected Python version: ${major_version}.${minor_version}"
663+
if (( major_version > 3 )) || { (( major_version == 3 )) && (( minor_version >= 14 )); }; then
664+
echo "(*) Python 3.14+ detected. Attempting cosign verification..."
665+
if cosign_verification "$VERSION"; then
666+
echo "(*) COSIGN verification successful."
667+
else
668+
echo "(*) COSIGN verification failed or not available for Python ${VERSION}"
669+
echo "(*) WARNING: Installing Python ${VERSION} without signature verification"
670+
echo "(*) This is expected for newly released versions where cosign signatures are not yet available"
671+
echo "(*) Python 3.14+ discontinued PGP signatures in favor of cosign, but cosign signatures may take time to be published"
672+
fi
513673
else
514-
receive_gpg_keys PYTHON_SOURCE_GPG_KEYS
674+
echo "(*) Python < 3.14 detected. Using GPG signature verification..."
675+
gpg_verification
676+
echo "(*) GPG verification successful."
515677
fi
516-
echo "Downloading ${cpython_tgz_filename}.asc..."
517-
curl -sSL -o "/tmp/python-src/${cpython_tgz_filename}.asc" "${cpython_tgz_url}.asc"
518-
gpg --verify "${cpython_tgz_filename}.asc"
519678

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

0 commit comments

Comments
 (0)