junit-team / junit5

✅ The 5th major version of the programmer-friendly testing framework for Java and the JVM
https://junit.org
Eclipse Public License 2.0
6.44k stars 1.5k forks source link

Use GitHub's Artifact Attestations #3128

Open sormuras opened 1 year ago

sormuras commented 1 year ago

Generate verify-able signed attestations for every artifact made with GitHub Actions.

Attestations do appear here:

TODO

vcvitaly commented 1 year ago

I can take a look.

sormuras commented 1 year ago

Note to myself: update the TODO in the initial description

Next: I was waiting for some pull requests in sigstore-java to be merged first. Some of them are!

For example

@vlsi what do you think? Is about the right time to use the Gradle plugin to sign JUnit's artifacts in addition to the current PGP workflow?

vlsi commented 1 year ago

A critical PR is

otherwise the generated bundle is just invalid as it misses signature field.

That would be pretty good, however, you'll need to update sigstore-java every time Fulcio/Rekor certificates change. Automatic fetching of certificates is tracked in

sormuras commented 1 year ago

Thanks for the quick reply, Vladimir!

Updating the status of this issue accordingly.

sormuras commented 1 year ago

Wondering whether we can achieve the same effect (meaning, sign all JAR files) by using some GitHub Action after the build produced them? 🤔

Something similar to: https://github.com/junit-team/junit5/blob/700c2d4f25b48bfc4970f99d3198af5e8c5e3f37/.github/workflows/main.yml#L68

vlsi commented 1 year ago

Of course, you can sign all the jars, poms, and other files via Action.

Here's the way you can generate signatures: https://github.com/sigstore/protobuf-specs/blob/16541696de137c6281d66d075a4924d9bbd181ff/java/scripts/sign_and_bundle_release.sh#L72

vlsi commented 1 year ago

Just in case, the most secure approach would be signing the files within a separate GitHub workflow, so only well-known binaries have access to signing credentials (e.g. OIDC token).

In other words:

1) Prepare release files, and export them via "upload-artifacts" action or something like that 2) Sign them in a separate workflow 3) Collect all the signed files and publish them (e.g. push to Nexus)

I am afraid that sounds like a non-trivial GitHub Actions exercise. However, I consider the above Gradle plugin as a quick win for those who want to start signing without creating complicated workflows.

vlsi commented 1 year ago

One more thing: there's https://github.com/slsa-framework/slsa-github-generator effort to come up with a reusable workflow for creating the attestations.

vlsi commented 1 year ago

Something similar to: https://github.com/junit-team/junit5/blob/700c2d4f25b48bfc4970f99d3198af5e8c5e3f37/.github/workflows/main.yml#L68

The sad thing is that you launch Gradle with all the third-party plugins with Sonatype credentials. In the ideal world, we would like to expose credentials to a tiny tool that pushes code to Central only. That would reduce the attack surface. However, it sounds like it makes all "Gradle release plugins" unnecessary. Frankly speaking, that is slightly disappointing since I like how many tasks are easier to manage in Gradle rather than in bash scripts.

Recently GitHub improved "reusable workflows" part, so it might be that there's a possibility to come up with a reusable workflow that you just copy&paste and get "safe and secure" sign&push to Central.

sormuras commented 6 months ago

With https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/ we can achieve artitfact attestations by calling https://github.com/actions/attest-build-provenance for all our artifacts just before deployment. No build tool plugin is required - updating the issue title accordingly.

marcphilipp commented 6 months ago

@sormuras With #3846 merged, can we consider this done for now?

sormuras commented 6 months ago

I was just about to add these items to the description:

TODO

But those could also go into dedicated issues.

marcphilipp commented 6 months ago

I think adding them to this issue is fine. I was just wondering what's left to do, if anything.

vlsi commented 5 months ago

Delete attestations generated by pull requests

In practice, you don't want allowing id-token: write for pull request builds as they might generate attestations that look like "produced by junit-team".

https://github.com/sigstore-conformance/extremely-dangerous-public-oidc-beacon exists exactly for the reason of avoiding id-token: write for PR builds and still having a valid OIDC token for the test purposes.


At the same time, you probably want to attest the generated pom files as well

sormuras commented 5 months ago

In practice, you don't want allowing id-token: write for pull request builds [...]

Good to learn. Is there another way to guard against that? Like having a dedicated workflow build for attestation that only runs on behalf of the "junit-team"? Doesn't the following line already take care about that?

https://github.com/junit-team/junit5/blob/b3eb972f67e650652ed48e1b554d6f7ffce7d1ff/.github/workflows/main.yml#L77

sormuras commented 5 months ago

At the same time, you probably want to attest the generated pom files as well

We'll start with JAR files for the time being. Otherwise, why stop with pom files and not include hash files, Gradle modules files, etc. Mind that every type is multiplied with 19 ... the number of published packages by the JUnit 5 project.

As soon as Sonatype permits us to drop PGP/GPG for deployments to Maven Central and uses Sigstore-based attestations instead, we should consider generating attests for every and each file.

vlsi commented 5 months ago

include hash files

The attestations attest hash of the file, so creating separate attestations for hash files sounds wasteful, and it adds no value. The same goes for PGP: you don't generate PGP signatures for .sha1 filles. By the way, both PGP and Sigstore are cryptographic signatures, so cross-signing makes no sense.


As soon as Sonatype permits us to drop PGP/GPG for deployments to Maven Central and uses Sigstore-based attestations instead, we should consider generating attests for every and each file

I would suggest you keep publishing both PGP and Sigstore for backward compatibility reasons. There are clients that verify PGP signatures for their dependencies, and removing PGP would be a step backwards.


Gradle modules files

There's a cyclic dependency when signing metadata.


Doesn't the following line already take care about that?

Having if branch==main is good, however, keep in mind you still execute full Gradle build with all its dependencies, and every bit of it has access to id-token. At the same time, if you add id-token: write for the trusted branches only, then you do not need to "delete attestations generated by PRs".

In the ideal world, we execute build without id-token: write privileges, and then execute attestation for the generated files. Then only attestation step would have access to the token. I've raised https://github.com/actions/attest-build-provenance/issues/103 to check the indented usage with respect to security.


As soon as Sonatype permits us to drop PGP/GPG for deployments to Maven Central

As far as I know, Maven Central already supports uploading .sigstore.json files without the corresponding .asc PGP signatures. It does not validate the contents, however, it recognizes .sigstore.json is cryptographic enough to not require PGP.

For instance, see check out .sigstore.json files at https://repo1.maven.org/maven2/dev/sigstore/sigstore-java/0.10.0/