pypi/pypi-attestations

Support Google Cloud publishers

Closed this issue · 6 comments

di commented

Currently this library provides GitHubPublisher and GitLabPublisher. Additionally, when verifying, --repository is required to be either a GitHub or GitLab publisher.

Since PyPI supports a Google Cloud Trusted Publisher, this library should provide a GoogleCloudPublisher as well, to enable PyPI to support Google Cloud publishers for publish provenance.

Full end-to-end workflow:

$ cat setup.py
from setuptools import setup

setup(
    name="gcb-attestation-test",
    version="0.0.0",
)

$ python -m build --sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - setuptools >= 40.8.0
* Getting build dependencies for sdist...
running egg_info
creating src/gcb_attestation_test.egg-info
writing src/gcb_attestation_test.egg-info/PKG-INFO
writing dependency_links to src/gcb_attestation_test.egg-info/dependency_links.txt
writing top-level names to src/gcb_attestation_test.egg-info/top_level.txt
writing manifest file 'src/gcb_attestation_test.egg-info/SOURCES.txt'
reading manifest file 'src/gcb_attestation_test.egg-info/SOURCES.txt'
writing manifest file 'src/gcb_attestation_test.egg-info/SOURCES.txt'
* Building sdist...
running sdist
running egg_info
writing src/gcb_attestation_test.egg-info/PKG-INFO
writing dependency_links to src/gcb_attestation_test.egg-info/dependency_links.txt
writing top-level names to src/gcb_attestation_test.egg-info/top_level.txt
reading manifest file 'src/gcb_attestation_test.egg-info/SOURCES.txt'
writing manifest file 'src/gcb_attestation_test.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md

running check
creating gcb_attestation_test-0.0.0
creating gcb_attestation_test-0.0.0/src
creating gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
copying files to gcb_attestation_test-0.0.0...
copying setup.py -> gcb_attestation_test-0.0.0
copying src/__init__.py -> gcb_attestation_test-0.0.0/src
copying src/gcb_attestation_test.egg-info/PKG-INFO -> gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
copying src/gcb_attestation_test.egg-info/SOURCES.txt -> gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
copying src/gcb_attestation_test.egg-info/dependency_links.txt -> gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
copying src/gcb_attestation_test.egg-info/top_level.txt -> gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
copying src/gcb_attestation_test.egg-info/SOURCES.txt -> gcb_attestation_test-0.0.0/src/gcb_attestation_test.egg-info
Writing gcb_attestation_test-0.0.0/setup.cfg
Creating tar archive
removing 'gcb_attestation_test-0.0.0' (and everything under it)
Successfully built gcb_attestation_test-0.0.0.tar.gz

$ python -m pypi_attestations sign dist/gcb_attestation_test-0.0.0.tar.gz

$ python -m pypi_attestations inspect dist/gcb_attestation_test-0.0.0.tar.gz.publish.attestation
Warning: The information displayed below are not verified, they are only displayed. Use the verify command to verify them.
File: dist/gcb_attestation_test-0.0.0.tar.gz.publish.attestation
Version: 1
Statement:
	Type: https://in-toto.io/Statement/v1
	Subject:
		gcb_attestation_test-0.0.0.tar.gz (digest: 461317362419124b6012e855423a9078d6de8aed3e74fa78cc74d669b23dc6cf)
	Predicate type: https://docs.pypi.org/attestations/publish/v1
	Predicate: None
Certificate:
	Subjects (suitable for `--identity`): ['REDACTED@developer.gserviceaccount.com']
	Issuer: CN=sigstore-intermediate,O=sigstore.dev
	Validity: 2025-04-21 15:12:27+00:00
Transparency Log (1 entries):
	Log Index: 200170367

$ python -m pypi_attestations verify attestation --identity REDACTED@developer.gserviceaccount.com dist/gcb_attestation_test-0.0.0.tar.gz
OK: dist/gcb_attestation_test-0.0.0.tar.gz.publish.attestation

$ twine upload --attestations dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Uploading gcb_attestation_test-0.0.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.9/7.9 kB • 00:00 • ?
WARNING  Error during upload. Retry with the --verbose option for more details.
ERROR    HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/
         Invalid attestations supplied during upload: Attestations are not currently supported with Google publishers

$ python -m pypi_attestations verify pypi gcb_attestation_test-0.0.0.tar.gz
usage: pypi-attestations verify pypi [-h] --repository REPOSITORY [--staging] [--offline] [--provenance-file PROVENANCE_FILE] PYPI_FILE
pypi-attestations verify pypi: error: the following arguments are required: --repository

The Google Trusted Publishers take two claims: an email and a subject (which then Google sends as claims in their OIDC token). Here, I see that the email is present in the sigstore certificate, but I don't see the subject. Will it always be absent?

Here, I see that the email is present in the sigstore certificate, but I don't see the subject. Will it always be absent?

I believe so, but I'll confirm -- my understanding is that Fulcio emits very few claims when used with an "email" signing identity, and that email is really the only guaranteed one.

Yep, the only things that Fulcio embeds with an email identity is the email claim itself, plus the issuer, both of which we check:

https://github.com/sigstore/fulcio/blob/a99e844583d9c9b6a861fae623f93c8d39a26b33/pkg/identity/email/principal.go#L69-L81

di commented

Thanks @woodruffw! Can we release this?

Yep, I'll kick one off in a moment!

Cut with v0.0.24 🙂