slsa-framework / slsa-github-generator

Language-agnostic SLSA provenance generation for Github Actions
Apache License 2.0
433 stars 128 forks source link

Examples on using generic provenance for containers #332

Closed ianlewis closed 2 years ago

ianlewis commented 2 years ago
ianlewis commented 2 years ago

This should have a full example with policy verification using OPA and/or Kyverno.

chipzoller commented 2 years ago

If you can provide some suggestions on what such policies should check for, I can create and contribute them from Kyverno's side.

ianlewis commented 2 years ago

@chipzoller I think that, for starters, we need an example policy that verifies the invocation.configSource.uri is a URI for the user's repo with the expected branch.

Some other nice to haves would be:

ianlewis commented 2 years ago

I'm working on figuring out how verification will work when using the generic workflow w/ containers. Based on the Kyverno docs it seems like Kyverno supports a verifyImage rule that can verify image signatures or image provenance and that it verifies it in whatever format cosign attest saves it in.

I'm guessing that a user's GH Actions workflow would look something like the following:

  1. Build the docker image
  2. Push it to the registry
  3. Generate the provenance by passing the sha256 RepoDigest to the generic workflow
  4. Upload the provenance to the registry using cosign upload blob

...but I need to check what the cosign attest API does and if I can mimic it with the provenance generated by the generic workflow and cosign upload blob.

chipzoller commented 2 years ago

Take a look at the work I've already done here. The first of three attestations happens here. When using cosign attest on an image, as you've noted in another issue, it'll set the subject to something like

  "subject": [
    {
      "name": "ghcr.io/chipzoller/zulu",
      "digest": {
        "sha256": "5365f368065f3e93629b71b404d75c03154be84ed9853a2ea6739d6994a1993e"
      }
    }
  ],

You therefore want all attestations to have the same subject, and that's why I'm stripping just the raw predicate out of the in-toto attested form here. If that isn't done, the subjects will be different on account of the generic provenance generator producing a different subject. You can see here where I even customize the input name (for experimentation) knowing that it'll get stripped and replaced in the later step.

So, ideally, whenever the provenance generator for container images is made available, it should produce either the raw predicate (unattested form) allowing to be used as an input to cosign attest --type slsaprovenance, and/or in in-toto attested form the subject should be in alignment with what cosign would itself set if it handled the attestation.

chipzoller commented 2 years ago

Sample Kyverno Policies with Keyless Verification

All the below Kyverno policies are set to enforce mode under spec.validationFailureAction meaning if the validation does not pass, it will block the creation or update of the Pod. Also, by default, these policies will work similarly for Pod controllers (Deployments, DaemonSets, StatefulSets, Jobs, and CronJobs) as they do for "bare" Pods. No other work is required. These policies will also resolve mutable tags to immutable digest format. Please see comment blocks for portions of the policies which need user customization.

  1. Example policy that verifies the invocation.configSource.uri is a URI for the user's repo with the expected branch.

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: check-slsa-attestations
    spec:
    validationFailureAction: enforce
    webhookTimeoutSeconds: 30
    rules:
    - name: check-uri-keyless
      match:
        any:
        - resources:
            kinds:
              - Pod
      verifyImages:
      - imageReferences:
        # Replace with your image. Wildcard values are supported.
        - "myreg.org/path/repo:*"
        attestors:
        - entries:
          - keyless:
            # In the case of GitHub Actions, the subject will be set to your Actions workflow responsible
            # for kicking off the process. Note that this will not be set to the external action
            # responsible for the provenance generation.
              subject: "https://github.com/myname/myrepo/.github/workflows/my-workflow.yaml@refs/heads/mybranch"
              issuer: "https://token.actions.githubusercontent.com"
        attestations:
        - predicateType: https://slsa.dev/provenance/v0.2
          conditions:
          - all:
            # The invocation.configSource.uri in the attestation will be set to the repository
            # and branch where your workflow, in the case of GitHub Actions, is located.
            # Replace with your values.
            - key: "{{ invocation.configSource.uri }}"
              operator: Equals
              value: "git+https://github.com/myname/myrepo@refs/heads/mybranch"
  2. Check that invocation.configSource.entryPoint is the user's expected workflow.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-slsa-attestations
spec:
  validationFailureAction: enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: check-entrypoint-keyless
      match:
        any:
        - resources:
            kinds:
              - Pod
      verifyImages:
      - imageReferences:
        # Replace with your image. Wildcard values are supported.
        - "myreg.org/path/repo:*"
        attestors:
        - entries:
          - keyless:
            # In the case of GitHub Actions, the subject will be set to your Actions workflow responsible
            # for kicking off the process. Note that this will not be set to the external action
            # responsible for the provenance generation.
              subject: "https://github.com/myname/myrepo/.github/workflows/my-workflow.yaml@refs/heads/mybranch"
              issuer: "https://token.actions.githubusercontent.com"
        attestations:
        - predicateType: https://slsa.dev/provenance/v0.2
          conditions:
          - all:
            # The invocation.configSource.entryPoint in the attestation will be set to the full
            # path to your workflow. With GitHub Actions, this will begin with .github/
            # Replace with your values.
            - key: "{{ invocation.configSource.entryPoint }}"
              operator: Equals
              value: ".github/workflows/my-workflow.yaml"
  1. Check that the builder.id is a URI for the generic workflow.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-slsa-attestations
spec:
  validationFailureAction: enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: check-builder-id-keyless
      match:
        any:
        - resources:
            kinds:
              - Pod
      verifyImages:
      - imageReferences:
        # Replace with your image. Wildcard values are supported.
        - "myreg.org/path/repo:*"
        attestors:
        - entries:
          - keyless:
            # In the case of GitHub Actions, the subject will be set to your Actions workflow responsible
            # for kicking off the process. Note that this will not be set to the external action
            # responsible for the provenance generation.
              subject: "https://github.com/myname/myrepo/.github/workflows/my-workflow.yaml@refs/heads/mybranch"
              issuer: "https://token.actions.githubusercontent.com"
        attestations:
        - predicateType: https://slsa.dev/provenance/v0.2
          conditions:
          - all:
            # This expression uses a regex pattern to ensure the builder.id in the attestation is equal to the official
            # SLSA provenance generator workflow and uses a tagged release in semver format. If using a specific SLSA
            # provenance generation workflow, you may need to adjust the first input as necessary.
            - key: "{{ regex_match('^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v[0-9].[0-9].[0-9]$','{{ builder.id}}') }}"
              operator: Equals
              value: true
  1. All-in-one policy with a single rule which combines the three previous checks.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-slsa-attestations
spec:
  validationFailureAction: enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: check-all-keyless
      match:
        any:
        - resources:
            kinds:
              - Pod
      verifyImages:
      - imageReferences:
        # Replace with your image. Wildcard values are supported.
        - "myreg.org/path/repo:*"
        attestors:
        - entries:
          - keyless:
            # In the case of GitHub Actions, the subject will be set to your Actions workflow responsible
            # for kicking off the process. Note that this will not be set to the external action
            # responsible for the provenance generation.
              subject: "https://github.com/myname/myrepo/.github/workflows/my-workflow.yaml@refs/heads/mybranch"
              issuer: "https://token.actions.githubusercontent.com"
        attestations:
        - predicateType: https://slsa.dev/provenance/v0.2
          conditions:
          - all:
            - key: "{{ invocation.configSource.uri }}"
              operator: Equals
              value: "git+https://github.com/myname/myrepo@refs/heads/mybranch"
            - key: "{{ invocation.configSource.entryPoint }}"
              operator: Equals
              value: ".github/workflows/my-workflow.yaml"
            - key: "{{ regex_match('^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v[0-9].[0-9].[0-9]$','{{ builder.id}}') }}"
              operator: Equals
              value: true

Keyed Verification

For any of the above policies, if a user wishes to verify them with key-based signing as opposed to keyless, they can simply substitute the attestors[].entries[].keyless block with the keys[] block below. There are no modifications needed to the attestations object.

          - keys:
              publicKeys: |
                -----BEGIN PUBLIC KEY-----
                MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2kCXNpRME7TtCbzLNyRhdZY5JvmC
                sAeNYYYg5LYpLdYjqtdfly54yEevhBGQUy0vn/tbSOptTnUNcNgnNaBlPg==
                -----END PUBLIC KEY-----
ianlewis commented 2 years ago

@chipzoller Thanks! Those policies look good! I totally forgot about checking the subject from the cert. It's good you added that. Could you create these as all just one ClusterPolicy with multiple rules rather than three different objects?

As for the subjects, since they are an input into the generic workflow we should be able to set that to the correct value I would think. Does the base64-subjects you create here not work? Off hand, I think you need to strip the part to the left of the '@' from $IMAGE before you put it in the base64-subjects. i.e. echo $IMAGE | cut -d '@' -f1

# gcr.io/my-project/app@sha256:6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e
# becomes
# 6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e gcr.io/my-project/app
BASE64_DIGEST=$(echo "$(echo -n $IMAGE | cut -d ':' -f2)  $(echo $IMAGE | cut -d '@' -f1)" | base64 -w0)

Once you do that I think you should be able to use the attestation returned from the generic workflow as is. So you wouldn't need to strip the predicate out or run cosign attest which would sign it over again. Rather you should be able to just use cosign upload blob to upload the attestation file as is. I still haven't tested it yet but I think it should work. I'm just not totally sure yet if there is anything special cosign does when uploading attestations (vs. sbom etc.). It might need a specific <blob ref> name or something.

ianlewis commented 2 years ago

@chipzoller BTW, it's still buggy and not working yet but the workflow I had been using for testing is here. This is kind of what I was thinking in terms of an example user workflow. https://github.com/ianlewis/actions-test/blob/main/.github/workflows/generic-container.yml

chipzoller commented 2 years ago

Could you create these as all just one ClusterPolicy with multiple rules rather than three different objects?

I can, or this can just be one rule with multiple of those expressions falling under the attestations[].conditions[].all[] block. If the intent is that all of these belong together, then one rule would do it. EDIT: See policy number four above for a single ClusterPolicy with single rule having all three checks.

As for the subjects, since they are an input into the generic workflow we should be able to set that to the correct value I would think. Does the base64-subjects you create here not work?

It does work, but it just sets the subject as the following:

"subject": [
    {
      "name": "ghcr.io/chipzoller/zulu@sha256:5365f368065f3e93629b71b404d75c03154be84ed9853a2ea6739d6994a1993e",
      "digest": {
        "sha256": "5365f368065f3e93629b71b404d75c03154be84ed9853a2ea6739d6994a1993e"
      }
    }
  ]

I know this can be adapted by emulate what cosign attest places there, I just hadn't messed with it. I can test this out in a bit (without stripping out the predicate after the attestation is created). It'd be nice if the container generator workflow would produce the subject like this so it's consistent with what cosign attest uses.

Your workflow is basically the same as mine just minus the blob upload part. It would be good to provide an input to the finished container workflow which allows users to control the name of the attestation file that gets uploaded.

ianlewis commented 2 years ago

It does work, but it just sets the subject as the following:

Yeah, I'll need to check what Kyverno does when validating. It might use the name & digest from the subject, though I'm not sure if that's the right practice. slsa-verifier hashes the binary and uses the hash to look up the cert via the rekor index instead. I'm not sure what's better and for what reasons tbh. @asraa and @laurentsimon might know more. I think in the case of containers the image name might matter, whereas it doesn't really matter in the case of binary executable files.

In any case, at least for our examples in the docs I think we would want to emulate what cosign attest does.

Your workflow is basically the same as mine just minus the blob upload part. It would be good to provide an input to the finished container workflow which allows users to control the name of the attestation file that gets uploaded.

I'm not sure that the file name really makes a difference since the container registry isn't really a file server. I think it's more like a blob store with labels and we just need to make sure that it's uploaded with the same tags/labels but I'm not a container registry protocol expert. BTW, some folks kindly pointed out that cosign attach attestation is the command we need to use. That's what we'll likely use as it will (hopefully) ensure compatibility with cosign attest.

laurentsimon commented 2 years ago

Note that for keyless, you need to verify that the certificate contains the trusted builder (see image https://github.com/slsa-framework/slsa-github-generator/blob/main/SPECIFICATIONS.md#detailed-steps).

subject: "https://github.com/myname/myrepo/.github/workflows/my-workflow.yaml@refs/heads/mybranch" should be replaced by the trusted builder. Once we have verified that the OIDC provider is GitHub (as in the example) and the workflow is the trusted builder, we can trust the content of the provenance and use keys to match on provenance fields.

- key: "{{ regex_match('^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v[0-9].[0-9].[0-9]$','{{ builder.id}}') }}" is not enough, because it looks up the content of the provenance without verification of the "trusted builder"'s field in the certificate.

Please let me know if that's not making sense.

cosign attach attestation

yes we will use this either via API or via CLI (like we do in the ko PoC)

verification

most of the cosign uses with containers currently do not query rekor and rely on a bundle that contains a signature from rekor. I think it's up to to the verification engine to do this, and our provenance need not change. We could ask the maintainers of Kyverno to add an option if it does not exist.

/cc @asraa

chipzoller commented 2 years ago

But it does because it will only proceed to verify the provenance if the attestors are verified. And as I stated in the comment for these policies, it is expected that the subject is replaced by the user's "parent" (trusted) builder workflow.

laurentsimon commented 2 years ago

gotcha, sorry I missed the comment then. I only saw

# In the case of GitHub Actions, the subject will be set to your Actions workflow responsible
# for kicking off the process. Note that this will not be set to the external action
# responsible for the provenance generation.

Can you link to the comment?

chipzoller commented 2 years ago

That is the comment I meant. The subject gets set to the workflow the user has as the parent workflow making the call to the SLSA generator workflow. It doesn't get set to the SLSA workflow. Or am I misunderstanding your comment?

laurentsimon commented 2 years ago

Verifying the user workflow does not prove that the trusted builder was called. We need to verify the certificate field that attests to the trusted builder instead. I was assuming it was "subject", but it may be "Subject Alternative Name"'s URI then (given the image in https://github.com/slsa-framework/slsa-github-generator/blob/main/SPECIFICATIONS.md#detailed-steps).

You can see it used in our verifier in https://github.com/slsa-framework/slsa-verifier/blob/main/pkg/provenance.go#L496

chipzoller commented 2 years ago

Ok, I see what you mean. Yes, this is a SAN field.

$ COSIGN_EXPERIMENTAL=1 cosign verify ghcr.io/chipzoller/zulu:latest | jq -r .[].optional.Bundle.Payload.body | base64 --decode | jq -r .spec.signature.publicKey.content | base64 --decode | step certificate inspect -

Verification for ghcr.io/chipzoller/zulu:latest --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - Any certificates were verified against the Fulcio roots.
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 522184968399268816520144771357179810204483628558 (0x5b77957518196e2902a8b833417f7c595f90ae0e)
    Signature Algorithm: ECDSA-SHA384
        Issuer: O=sigstore.dev,CN=sigstore-intermediate
        Validity
            Not Before: Jun 21 14:00:16 2022 UTC
            Not After : Jun 21 14:10:16 2022 UTC
        Subject:
        Subject Public Key Info:
            Public Key Algorithm: ECDSA
                Public-Key: (256 bit)
                X:
                    18:0b:78:5d:ca:df:8e:01:e6:d3:fc:9d:bb:34:54:
                    a8:4a:be:d8:c8:b8:c4:bf:7e:4b:8f:2a:89:ec:76:
                    77:f3
                Y:
                    55:b1:ff:a7:66:1d:2f:9f:d4:91:23:00:a8:85:0d:
                    9a:af:d1:b0:b3:99:05:62:43:c7:35:15:e1:35:89:
                    a7:19
                Curve: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                Code Signing
            X509v3 Subject Key Identifier:
                C5:66:26:3F:34:96:BF:B6:45:8D:E6:49:A2:80:94:81:6A:3E:12:79
            X509v3 Authority Key Identifier:
                keyid:DF:D3:E9:CF:56:24:11:96:F9:A8:D8:E9:28:55:A2:C6:2E:18:64:3F
            X509v3 Subject Alternative Name: critical
                URI:https://github.com/chipzoller/zulu/.github/workflows/slsa-generic-keyless.yaml@refs/heads/main
            1.3.6.1.4.1.57264.1.1:
                https://token.actions.githubusercontent.com
            1.3.6.1.4.1.57264.1.2:
                push
            1.3.6.1.4.1.57264.1.3:
                836faeca5cf505bd6d9e35a6564847bd53aafd51
            1.3.6.1.4.1.57264.1.4:
                slsa-generic-keyless
            1.3.6.1.4.1.57264.1.5:
                chipzoller/zulu
            1.3.6.1.4.1.57264.1.6:
                refs/heads/main
            RFC6962 Certificate Transparency SCT:
                SCT [0]:
                    Version: V1 (0x0)
                    LogID: CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=
                    Timestamp: Jun 21 14:00:16.286 2022 UTC
                    Signature Algorithm: SHA256-ECDSA
                      30:46:02:21:00:a4:2b:e3:17:31:1e:aa:51:3c:93:00:0b:09:
                      36:a7:c5:c7:8d:99:f5:7e:14:40:de:19:10:bb:cf:2e:53:88:
                      0d:02:21:00:95:93:89:9f:7a:59:d0:cf:a5:eb:12:c4:8d:e8:
                      98:53:e0:c9:a4:a8:0c:b3:90:1f:70:29:74:37:45:68:c4:d3
    Signature Algorithm: ECDSA-SHA384
         30:66:02:31:00:98:03:86:aa:a1:a1:79:f1:b5:0a:96:c8:c2:
         8c:3f:fc:e7:0f:3a:dc:0a:33:df:24:a6:90:0f:dc:d5:8f:19:
         d0:0f:e0:8d:29:c7:e0:be:4b:3f:11:b7:18:12:06:ee:5f:02:
         31:00:e4:89:6d:6f:9e:92:bc:a9:15:8c:a1:3e:43:af:c5:e5:
         aa:0a:9a:ed:17:d2:1d:d7:ea:9b:2c:a9:33:01:96:80:6e:ba:
         16:03:37:60:e9:03:27:08:af:86:6b:4e:f2:bd
ianlewis commented 2 years ago

Yeah in @chipzoller 's example he's stripping out the predicate and resigning the provenance which I assume updates the subject in the key because in his case cosign is being executed in the context of the user workflow.

ianlewis commented 2 years ago

BTW, This example for the generic workflow works now. I think I'm going to use this as an example workflow for building and generating provenance for containers using the generic reusable workflow. https://github.com/ianlewis/actions-test/blob/main/.github/workflows/generic-container.yml

I'm planning on testing how the attestation is verified by Kyverno on Monday but I think it should work as advertised.

chipzoller commented 2 years ago

I just tested by modifying my workflow, and I'm not seeing the provenance returned with a cosign verify-attestations command. cosign tree shows there are three attestations present, but the only ones returned by cosign verify-attestations are those created in my workflow from the cosign attest command.

Decoded attestation provided below from the prov. generator after modification:

Click to expand ```json { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "ghcr.io/chipzoller/zulu", "digest": { "sha256": "76300503a3cb5bfd1bdab04114ba68c26da449a33ed5d4f7afab374cb36cd0a6" } } ], "predicate": { "builder": { "id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.1.0" }, "buildType": "https://github.com/slsa-framework/slsa-github-generator@v1", "invocation": { "configSource": { "uri": "git+https://github.com/chipzoller/zulu@refs/heads/main", "digest": { "sha1": "2342d65d70ab3af2ac3159e6fae8078534d6717b" }, "entryPoint": ".github/workflows/slsa-generic-keyless.yaml" }, "parameters": {}, "environment": { "github_actor": "chipzoller", "github_actor_id": "29008123", "github_base_ref": "", "github_event_name": "push", "github_event_payload": { "after": "2342d65d70ab3af2ac3159e6fae8078534d6717b", "base_ref": null, "before": "1c3a746af484ec5c61a3c4e156f757caa0b5b639", "commits": [ { "author": { "email": "chipzoller@gmail.com", "name": "Chip Zoller", "username": "chipzoller" }, "committer": { "email": "chipzoller@gmail.com", "name": "Chip Zoller", "username": "chipzoller" }, "distinct": true, "id": "2342d65d70ab3af2ac3159e6fae8078534d6717b", "message": "attach\n\nSigned-off-by: Chip Zoller ", "timestamp": "2022-06-24T12:27:31-04:00", "tree_id": "dc793f43756c68f878e1ee0e4d99158fa9b4292b", "url": "https://github.com/chipzoller/zulu/commit/2342d65d70ab3af2ac3159e6fae8078534d6717b" } ], "compare": "https://github.com/chipzoller/zulu/compare/1c3a746af484...2342d65d70ab", "created": false, "deleted": false, "forced": false, "head_commit": { "author": { "email": "chipzoller@gmail.com", "name": "Chip Zoller", "username": "chipzoller" }, "committer": { "email": "chipzoller@gmail.com", "name": "Chip Zoller", "username": "chipzoller" }, "distinct": true, "id": "2342d65d70ab3af2ac3159e6fae8078534d6717b", "message": "attach\n\nSigned-off-by: Chip Zoller ", "timestamp": "2022-06-24T12:27:31-04:00", "tree_id": "dc793f43756c68f878e1ee0e4d99158fa9b4292b", "url": "https://github.com/chipzoller/zulu/commit/2342d65d70ab3af2ac3159e6fae8078534d6717b" }, "pusher": { "email": "chipzoller@gmail.com", "name": "chipzoller" }, "ref": "refs/heads/main", "repository": { "allow_forking": true, "archive_url": "https://api.github.com/repos/chipzoller/zulu/{archive_format}{/ref}", "archived": false, "assignees_url": "https://api.github.com/repos/chipzoller/zulu/assignees{/user}", "blobs_url": "https://api.github.com/repos/chipzoller/zulu/git/blobs{/sha}", "branches_url": "https://api.github.com/repos/chipzoller/zulu/branches{/branch}", "clone_url": "https://github.com/chipzoller/zulu.git", "collaborators_url": "https://api.github.com/repos/chipzoller/zulu/collaborators{/collaborator}", "comments_url": "https://api.github.com/repos/chipzoller/zulu/comments{/number}", "commits_url": "https://api.github.com/repos/chipzoller/zulu/commits{/sha}", "compare_url": "https://api.github.com/repos/chipzoller/zulu/compare/{base}...{head}", "contents_url": "https://api.github.com/repos/chipzoller/zulu/contents/{+path}", "contributors_url": "https://api.github.com/repos/chipzoller/zulu/contributors", "created_at": 1655150913, "default_branch": "main", "deployments_url": "https://api.github.com/repos/chipzoller/zulu/deployments", "description": "Experimental SLSA provenance generation", "disabled": false, "downloads_url": "https://api.github.com/repos/chipzoller/zulu/downloads", "events_url": "https://api.github.com/repos/chipzoller/zulu/events", "fork": false, "forks": 1, "forks_count": 1, "forks_url": "https://api.github.com/repos/chipzoller/zulu/forks", "full_name": "chipzoller/zulu", "git_commits_url": "https://api.github.com/repos/chipzoller/zulu/git/commits{/sha}", "git_refs_url": "https://api.github.com/repos/chipzoller/zulu/git/refs{/sha}", "git_tags_url": "https://api.github.com/repos/chipzoller/zulu/git/tags{/sha}", "git_url": "git://github.com/chipzoller/zulu.git", "has_downloads": true, "has_issues": true, "has_pages": false, "has_projects": true, "has_wiki": true, "homepage": "", "hooks_url": "https://api.github.com/repos/chipzoller/zulu/hooks", "html_url": "https://github.com/chipzoller/zulu", "id": 503099854, "is_template": false, "issue_comment_url": "https://api.github.com/repos/chipzoller/zulu/issues/comments{/number}", "issue_events_url": "https://api.github.com/repos/chipzoller/zulu/issues/events{/number}", "issues_url": "https://api.github.com/repos/chipzoller/zulu/issues{/number}", "keys_url": "https://api.github.com/repos/chipzoller/zulu/keys{/key_id}", "labels_url": "https://api.github.com/repos/chipzoller/zulu/labels{/name}", "language": "Go", "languages_url": "https://api.github.com/repos/chipzoller/zulu/languages", "license": null, "master_branch": "main", "merges_url": "https://api.github.com/repos/chipzoller/zulu/merges", "milestones_url": "https://api.github.com/repos/chipzoller/zulu/milestones{/number}", "mirror_url": null, "name": "zulu", "node_id": "R_kgDOHfyxzg", "notifications_url": "https://api.github.com/repos/chipzoller/zulu/notifications{?since,all,participating}", "open_issues": 0, "open_issues_count": 0, "owner": { "avatar_url": "https://avatars.githubusercontent.com/u/29008123?v=4", "email": "chipzoller@gmail.com", "events_url": "https://api.github.com/users/chipzoller/events{/privacy}", "followers_url": "https://api.github.com/users/chipzoller/followers", "following_url": "https://api.github.com/users/chipzoller/following{/other_user}", "gists_url": "https://api.github.com/users/chipzoller/gists{/gist_id}", "gravatar_id": "", "html_url": "https://github.com/chipzoller", "id": 29008123, "login": "chipzoller", "name": "chipzoller", "node_id": "MDQ6VXNlcjI5MDA4MTIz", "organizations_url": "https://api.github.com/users/chipzoller/orgs", "received_events_url": "https://api.github.com/users/chipzoller/received_events", "repos_url": "https://api.github.com/users/chipzoller/repos", "site_admin": false, "starred_url": "https://api.github.com/users/chipzoller/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/chipzoller/subscriptions", "type": "User", "url": "https://api.github.com/users/chipzoller" }, "private": false, "pulls_url": "https://api.github.com/repos/chipzoller/zulu/pulls{/number}", "pushed_at": 1656088077, "releases_url": "https://api.github.com/repos/chipzoller/zulu/releases{/id}", "size": 7070, "ssh_url": "git@github.com:chipzoller/zulu.git", "stargazers": 1, "stargazers_count": 1, "stargazers_url": "https://api.github.com/repos/chipzoller/zulu/stargazers", "statuses_url": "https://api.github.com/repos/chipzoller/zulu/statuses/{sha}", "subscribers_url": "https://api.github.com/repos/chipzoller/zulu/subscribers", "subscription_url": "https://api.github.com/repos/chipzoller/zulu/subscription", "svn_url": "https://github.com/chipzoller/zulu", "tags_url": "https://api.github.com/repos/chipzoller/zulu/tags", "teams_url": "https://api.github.com/repos/chipzoller/zulu/teams", "topics": [], "trees_url": "https://api.github.com/repos/chipzoller/zulu/git/trees{/sha}", "updated_at": "2022-06-20T01:55:18Z", "url": "https://github.com/chipzoller/zulu", "visibility": "public", "watchers": 1, "watchers_count": 1 }, "sender": { "avatar_url": "https://avatars.githubusercontent.com/u/29008123?v=4", "events_url": "https://api.github.com/users/chipzoller/events{/privacy}", "followers_url": "https://api.github.com/users/chipzoller/followers", "following_url": "https://api.github.com/users/chipzoller/following{/other_user}", "gists_url": "https://api.github.com/users/chipzoller/gists{/gist_id}", "gravatar_id": "", "html_url": "https://github.com/chipzoller", "id": 29008123, "login": "chipzoller", "node_id": "MDQ6VXNlcjI5MDA4MTIz", "organizations_url": "https://api.github.com/users/chipzoller/orgs", "received_events_url": "https://api.github.com/users/chipzoller/received_events", "repos_url": "https://api.github.com/users/chipzoller/repos", "site_admin": false, "starred_url": "https://api.github.com/users/chipzoller/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/chipzoller/subscriptions", "type": "User", "url": "https://api.github.com/users/chipzoller" } }, "github_head_ref": "", "github_ref": "refs/heads/main", "github_ref_type": "branch", "github_repository_id": "503099854", "github_repository_owner": "chipzoller", "github_repository_owner_id": "29008123", "github_run_attempt": "1", "github_run_id": "2556932297", "github_run_number": "7", "github_sha1": "2342d65d70ab3af2ac3159e6fae8078534d6717b" } }, "metadata": { "buildInvocationID": "2556932297-1", "completeness": { "parameters": true, "environment": false, "materials": false }, "reproducible": false }, "materials": [ { "uri": "git+https://github.com/chipzoller/zulu@refs/heads/main", "digest": { "sha1": "2342d65d70ab3af2ac3159e6fae8078534d6717b" } } ] } } ```
chipzoller commented 2 years ago

@laurentsimon per comment from @JimBugwadia here, it looks like the subject field in the Kyverno is actually the contents of that URI attribute in the SAN.

ianlewis commented 2 years ago

I just tested by modifying my workflow, and I'm not seeing the provenance returned with a cosign verify-attestations command. cosign tree shows there are three attestations present, but the only ones returned by cosign verify-attestations are those created in my workflow from the cosign attest command.

Hmm, I'll need to investigate later 📝

Manual verification may be tricky since I'm using keyless in the generic workflow. cosign verify-attestation seems to need the key and I'm not sure of an easy way to retrieve it from rekor to verify unless I use slsa-verifier but that currently only supports file artifacts.

chipzoller commented 2 years ago

I do see that cosign download attestation does return all three.

chipzoller commented 2 years ago

I ended up logging https://github.com/sigstore/cosign/issues/2027 for this.

ianlewis commented 2 years ago

@chipzoller BTW, I was able to verify an attestation that I created with a local key using cosign verify-attestation. Were you doing something different?

# have existing SLSA predicate in predicate.json

# create cosign.key and cosign.pub
$ cosign generate-key-pair

# Create attestation
$ cosign attest --predicate predicate.json --no-upload --type slsaprovenance --key cosign.key ghcr.io/ianlewis/actions-test > attestation.intoto.json

# Attach attestation to the image
$ cosign attach attestation --attestation attestation.intoto.jsonl ghcr.io/ianlewis/actions-test

# Verify it's there
$ cosign tree ghcr.io/ianlewis/actions-test

# Verify the attestation
$ cosign verify-attestation --key cosign.pub ghcr.io/ianlewis/actions-test
 - The cosign claims were validated                                                                                                                                                                                                                 
 - The signatures were verified against the specified public key
chipzoller commented 2 years ago

Yes, the problem is with keyless mode.

laurentsimon commented 2 years ago

other examples to provide:

ianlewis commented 2 years ago

other examples to provide:

  • [ ] using ko, jib, etc

We should definitely have an example using ko, jib, etc. Added #947 and #948 for ko and jib examples.

The current getting started doc demonstrates using build-push-action. https://github.com/slsa-framework/slsa-github-generator/blob/fc6500582079e2a1c41f181e7f04189b2a94816f/internal/builders/container/README.md#getting-started

  • [ ] using simple docker build commands

I'm not sure I really want to recommend going this route as getting the image digest is not a good experience. build-push-action is just strictly better. Created #949 to track but I'm not sure we really needed it for the GA milestone.

ianlewis commented 2 years ago

I think I want to track this on individual issues for each example so I'm going to close this issue.

Outstanding tasks to write examples are tracked in these issues: