From 429a1642cccf0e6426e36547e9e2df6e19221afe Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:51:34 +0100 Subject: [PATCH] Switch from GPG to Sigstore for Python source verification The build scripts used to download and compile the Python source archives (for upload to S3, where they are then consumed by the buildpack during customer builds) currently use GPG to verify the Python source archive downloads. However, use of PGP signatures for Python artifact verification was deprecated previously in PEP 761, in favour of Sigstore: https://peps.python.org/pep-0761/ https://www.python.org/downloads/metadata/sigstore/ Until now the PGP signatures have still been available for all stable releases, however, as of Python 3.14 (due to be released this week), Sigstore will be the only supported verification mechanism: https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-no-more-pgp As such, we must now switch over to Sigstore. We use the `cosign` CLI for verification since it's a standalone binary available via a Docker image, rather than the Python `sigstore` CLI which requires a Python environment (and so pip, venv etc, and more setup to ensure it stays isolated from the Python we're trying to build). See: - https://www.python.org/downloads/metadata/sigstore/ - https://docs.sigstore.dev/cosign/system_config/installation/#container-images - https://docs.sigstore.dev/cosign/verifying/verify/ - https://github.com/sigstore/cosign/blob/main/doc/cosign_verify-blob.md GUS-W-18244071. --- .github/dependabot.yml | 8 ++++++++ builds/Dockerfile | 2 ++ builds/build_python_runtime.sh | 31 +++++++++++++++---------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ee3475f73..fa1d4813e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,14 @@ updates: update-types: - "minor" - "patch" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "monthly" + labels: + - "dependencies" + - "docker" + - "skip changelog" - package-ecosystem: "github-actions" directory: "/" schedule: diff --git a/builds/Dockerfile b/builds/Dockerfile index cc16e9b25..8e83a738a 100644 --- a/builds/Dockerfile +++ b/builds/Dockerfile @@ -1,4 +1,5 @@ ARG STACK_VERSION="24" +FROM ghcr.io/sigstore/cosign/cosign:v2.6.1@sha256:68839b7f13dac5a6744a5d8818e984dd39183374e37855c19e14d623d9bc9037 AS cosign FROM heroku/heroku:${STACK_VERSION}-build ARG STACK_VERSION @@ -13,6 +14,7 @@ RUN apt-get update --error-on=any \ libreadline-dev \ libsqlite3-dev \ && rm -rf /var/lib/apt/lists/* +COPY --from=cosign /ko-app/cosign /usr/local/bin/cosign WORKDIR /tmp COPY build_python_runtime.sh python-3.13-ubuntu-22.04-libexpat-workaround.patch . diff --git a/builds/build_python_runtime.sh b/builds/build_python_runtime.sh index 7def990af..18a8dc5ea 100755 --- a/builds/build_python_runtime.sh +++ b/builds/build_python_runtime.sh @@ -43,23 +43,19 @@ if [[ " ${SUPPORTED_PYTHON_VERSIONS[*]} " != *" ${PYTHON_MAJOR_VERSION} "* ]]; t abort "Python ${PYTHON_MAJOR_VERSION} isn't supported on ${STACK}!" fi -# The release keys can be found on https://www.python.org/downloads/ -> "OpenPGP Public Keys". +# Sigstore identities taken from: https://www.python.org/downloads/metadata/sigstore/ case "${PYTHON_MAJOR_VERSION}" in - 3.13) - # https://github.com/Yhg1s.gpg - GPG_KEY_FINGERPRINT='7169605F62C751356D054A26A821E680E5FA6305' - ;; - 3.12) - # https://github.com/Yhg1s.gpg - GPG_KEY_FINGERPRINT='7169605F62C751356D054A26A821E680E5FA6305' + 3.12 | 3.13) + SIGSTORE_IDENTITY='thomas@python.org' + SIGSTORE_ISSUER='https://accounts.google.com' ;; 3.10 | 3.11) - # https://keybase.io/pablogsal/ - GPG_KEY_FINGERPRINT='A035C8C19219BA821ECEA86B64E628F8D684696D' + SIGSTORE_IDENTITY='pablogsal@python.org' + SIGSTORE_ISSUER='https://accounts.google.com' ;; 3.9) - # https://keybase.io/ambv/ - GPG_KEY_FINGERPRINT='E3FF2839C048B25C084DEBE9B26995E310250568' + SIGSTORE_IDENTITY='lukasz@langa.pl' + SIGSTORE_ISSUER='https://github.com/login/oauth' ;; *) abort "Unsupported Python version '${PYTHON_MAJOR_VERSION}'!" @@ -69,17 +65,20 @@ esac echo "Building Python ${PYTHON_VERSION} for ${STACK} (${ARCH})..." SOURCE_URL="https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz" -SIGNATURE_URL="${SOURCE_URL}.asc" +SIGSTORE_BUNDLE_URL="${SOURCE_URL}.sigstore" set -o xtrace mkdir -p "${SRC_DIR}" "${INSTALL_DIR}" "${UPLOAD_DIR}" curl --fail --retry 5 --retry-connrefused --connect-timeout 3 --max-time 30 -o python.tgz "${SOURCE_URL}" -curl --fail --retry 5 --retry-connrefused --connect-timeout 3 --max-time 30 -o python.tgz.asc "${SIGNATURE_URL}" +curl --fail --retry 5 --retry-connrefused --connect-timeout 3 --max-time 30 -o python.tgz.sigstore "${SIGSTORE_BUNDLE_URL}" -gpg --batch --verbose --recv-keys "${GPG_KEY_FINGERPRINT}" -gpg --batch --verify python.tgz.asc python.tgz +cosign verify-blob \ + --bundle python.tgz.sigstore \ + --certificate-identity "${SIGSTORE_IDENTITY}" \ + --certificate-oidc-issuer "${SIGSTORE_ISSUER}" \ + python.tgz tar --extract --file python.tgz --strip-components=1 --directory "${SRC_DIR}" cd "${SRC_DIR}"