Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for uploading attestations in legacy API #15952

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

facutuesca
Copy link
Contributor

Description

Add support for uploading PEP 740 attestations along with distribution files. Part of #15871.
For now only GHA-signed attestations are supported, but the implementation allows easily adding other publishers by implementing the OIDCPublisherMixin.publisher_verification_policy() method in the corresponding subclass.

Implementation

The core logic is in legacy.py: we check an upload request to see if it includes any attestations. If it does, we:

  1. Check if the session is authenticated using Trusted Publishing (and GHA). If not, fail.
  2. Parse the attestations using pypi-attestation-models.
  3. Get the verification policy corresponding to the current Trusted Publisher (for now, always GitHub) .
  4. Verify the attestations using sigstore with the above verification policy, against the uploaded distribution file.

For now we only verify the attestations. Storing them will be implemented in a later PR.

The GHA verification policy (from step 3) is defined in GitHubPublisherMixin.publisher_verification_policy(), and it checks the certificate in the attestation against the following claims:

  • OIDCBuildConfigURI (e.g: https://github.com/org/repo/.github/workflows/workflow.yml@....)
  • OIDCSourceRepositoryURI (e.g: https://github.com/org/repo/)

See here for the definition of each claim.

TODO before merging:

  • Re-add repository-service-tuf to requirements/dev.txt once they release a new version. The current version pins tuf==3.1.0, which conflicts with sigstore who depends on tuf==4.0.0.

cc @woodruffw @di

@facutuesca facutuesca requested a review from a team as a code owner May 13, 2024 16:56
@facutuesca facutuesca marked this pull request as draft May 13, 2024 16:57
Comment on lines +250 to +268
expected_build_configs = [
OIDCBuildConfigURI(f"https://github.com/{self.job_workflow_ref}@{claim}")
for claim in [ref, sha]
if claim is not None
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I might be mistaken, but in practice this should only be ref, not sha. With that being said, we can probably use OIDCSourceRepositoryDigest to enforce that the sha also matches 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trusted publisher claim verification attempts both though:

ref = all_signed_claims.get("ref")
sha = all_signed_claims.get("sha")
if not (ref or sha):
raise InvalidPublisherError("The ref and sha claims are empty")
expected = {f"{ground_truth}@{_ref}" for _ref in [ref, sha] if _ref}
if signed_claim not in expected:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, hmm. Maybe we observed GitHub's IdP doing both at different points...

This is fine for now, then! IMO we could also add the OIDCSourceRepositoryDigest for good measure 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further, the current claims (plus the Digest one you suggested above) are:

  1. OIDCBuildConfigURI (e.g: https://github.com/org/repo/.github/workflows/workflow.yml@$REF/$SHA)
  2. OIDCSourceRepositoryURI (e.g: https://github.com/org/repo/)
  3. OIDCSourceRepositoryDigest (the commit SHA)

I think we could remove the repo URI (2), since it's included in the build config URI (1). WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tying things together: #14335 is where checking both the ref and the sha was introduced.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a PR that adds a comment explaining why we check for both: #15967

@@ -235,6 +241,25 @@ def publisher_url(self, claims=None):
return f"{base}/commit/{sha}"
return base

def publisher_verification_policy(self, claims):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: docstring explaining the exact inner policy being constructed would be good!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 1100 to 1124
except (ValidationError, InvalidAttestationError) as e:
raise _exc_with_message(
HTTPBadRequest,
f"Error while decoding the included attestation: {e}",
)
except VerificationError as e:
raise _exc_with_message(
HTTPBadRequest,
f"Could not verify the uploaded artifact using the included "
f"attestation: {e}",
)
except Exception as e:
raise _exc_with_message(
HTTPBadRequest,
f"Unknown error while trying to verify included attestations: {e}",
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to have some metrics/Sentry handlers for these different cases, to help us debug/triage as this gets rolled out 🙂

(See metrics and sentry_sdk uses in the codebase for some examples)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I added metrics for warehouse.upload.attestations.ok (successful upload), warehouse.upload.attestations.malformed (error decoding/parsing the attestation) and warehouse.upload.attestations.failed_verify (error verifying the attestation).

I also added a Sentry message for unknown/unexpected errors

Comment on lines 1092 to 1094
verification_policy = publisher.publisher_verification_policy(
request.oidc_claims
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be moved out of the loop, to avoid re-building the policy each time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed!

Comment on lines +250 to +268
expected_build_configs = [
OIDCBuildConfigURI(f"https://github.com/{self.job_workflow_ref}@{claim}")
for claim in [ref, sha]
if claim is not None
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, hmm. Maybe we observed GitHub's IdP doing both at different points...

This is fine for now, then! IMO we could also add the OIDCSourceRepositoryDigest for good measure 🙂

@woodruffw woodruffw mentioned this pull request May 13, 2024
16 tasks
@@ -3,4 +3,6 @@ hupper>=1.9
pip-tools>=1.0
pyramid_debugtoolbar>=2.5
pip-api
repository-service-tuf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is now solved with the new releases of RSTUF that support tuf 4.0.0 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new release has another conflict with warehouse. See: #15958 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants