sigstore / cosign

Code signing and transparency for containers and binaries
Apache License 2.0
4.41k stars 544 forks source link

Signing gitlab CI attestation with cosign #2394

Closed chunteck closed 1 year ago

chunteck commented 1 year ago

Gitlab has recently started generating attestations for build artifacts. The attestation format is the standard intoto-attestation format.

I was hoping to sign the attestation and attach it to my docker image, using the command

cosign attest --predicategitlab-attestation.json--key cosign.key my-registry/project

While this works, this seems to wrap another layer over the gitlab-attestation.json, because this attest command expects a predicate rather than a fully formed attestation.

Is there any way that I can sign and attach my fully formed gitlab attestation to my docker image, without having to extract only the predicate out from my gitlab-attestation.json?

sample of the gitlab attestation format can be found in the gitlab docs

developer-guy commented 1 year ago

Hello @chunteck, thanks for bringing it to our attention. One of the things that came to my mind is that maybe you can extract the predicate part of the attestation and save it to another file then you could go with it, does that make sense?

chunteck commented 1 year ago

Hi @developer-guy ,

I did consider that as a last resort. However, the gitlab attestation has additional keys e.g metadata, materials outside of the predicate portion. I was hoping to find a solution where I can keep all these extra information in the attestation and still be able to use cosign to sign and attach the attestation to the docker image.

developer-guy commented 1 year ago

metadata, materials outside of the predicate portion

It shouldn't be outside the predicate portion, and the documentation on GitLab's website is wrong; please see.

Also, in the example of the attestation in the documentation, these are also included in the predicate field.

Copy the example attestation content from the documentation and use one of the JSON beautifiers, you will notice that these are in the predicate field. 👇

Screen Shot 2022-11-01 at 10 03 10 AM

chunteck commented 1 year ago

well, seems like gitlab is really generating their attestation wrongly, I will have to wait for them to fix it.

What about the subject field then? any way to include both the subject and predicate into the cosign attest?

developer-guy commented 1 year ago

The only thing that you need to provide is the type of the predicate via the --type flag and the predicate itself; the rest will be added according to the type of the document, that's all, which means that you don't need to provide anything than predicate's body.

znewman01 commented 1 year ago

I don't completely understand how this is meant to be used.

What I'd like to see happen is this: GitLab generates and signs (as GitLab) a Statement; you can then attach it to a container image with cosign attach attestation. This roughly means "I, GitLab, built these artifacts with this job;" anybody can verify this and trace back to find the workflow used. I believe this issue tracks implementing such a thing.

The other common pattern is to take a predicate and use it to cosign attest to a particular container image. This means "I, Developer Foo, assert that this predicate is true about this container image."

Instead, GitLab seems to be generating a Statement but not signing it. Then, somebody else is supposed to sign it. I guess this should be interpreted to mean "I, Developer Foo, grabbed this from GitLab and on GitLab was associated with these artifacts."

For cosign attest, you provide an <image URI> argument separately from the predicate; that's the subject of the attestation. If Cosign let you provide the "subject" separately, it might conflict with the "image URI"! That doesn't make sense, so it's disallowed. You could imagine allowing a full "Statement" object to be passed, but then Cosign would just manually check that every field in the provenance object was correct (_type, subject, predicateType) so to my mind it may as well just create the provenance object itself. Maybe this is a worthwhile use case, though?

Given that the GitLab Statement includes the subject field, maybe the workflow that really makes sense is:

  1. GitLab generates a Statement
  2. You download the statement and sign it, which means basically "I, Developer Foo, got this statement from GitLab."
  3. Use cosign attach attestation to upload the signed statement.

CC @SantiagoTorres who may have thoughts


Regarding the invalid format: see https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29246

SantiagoTorres commented 1 year ago

I broadly agree. I see that in-here the consensus is around providing a way for gitlab to use a key, rather than managing the envelope. Personally, I would like for us to have a way to say "this gitlab instance attests this provenance". Who attaches it may just be a workflow problem (i.e., do you want to decide where to stick it and where? should that be configurable?). This also does make sense when considering we are fully trusting gitlab to provide the information about the provenance bits...

developer-guy commented 1 year ago

we (w/@dentrax) have opened an issue for consuming the correct models from the in-toto's Go SDK instead of writing their own 👇

https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29334

upodroid commented 1 year ago

@asraa Would it be possible for cosign to be modified to allow it to attest multiple images with a single predicate?

For knative, I build multiple images with ko but I need to attach a single attestation to all the images that I build.

# Before calling sign_release, we do ko resolve first and get a bunch of yaml files with images built by ko. Then we read all those files and pull out the images and store them in a single file called imagerefs.txt

function sign_release() {
  ID_TOKEN=$(gcloud auth print-identity-token --audiences=sigstore \
    --include-email \
    --impersonate-service-account="${SIGNING_IDENTITY}")
  echo "Signing Images with the identity ${SIGNING_IDENTITY}"
  ## Sign the images with cosign
  if [[ -f "imagerefs.txt" ]]; then
      COSIGN_EXPERIMENTAL=1 cosign sign $(cat imagerefs.txt) --recursive --identity-token="${ID_TOKEN}"
      if  [ -n "${ATTEST_IMAGES:-}" ]; then # Temporary Feature Gate
        provenance-generator --clone-log=/logs/clone.json \
          --image-refs=imagerefs.txt --output=attestation.json
        COSIGN_EXPERIMENTAL=1 cosign attest $(cat imagerefs.txt) --recursive --identity-token="${ID_TOKEN}" \
          --predicate=attestation.json --type=slsaprovenance
      fi
  fi

}

Having to attest each image separately is annoying and doesn't look really good.

 REDACTED  MCW0CDP3YY  ~  $  cosign tree gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller:v20221123-0c20c48d
📦 Supply Chain Security Related artifacts for an image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller:v20221123-0c20c48d
└── 🔐 Signatures for an image tag: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller:sha256-51351c6bbb9a1ef81a23fbdcd0fd5cb6660a93e080ff4fec5dbbb235bd4562aa.sig
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   ├── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
   └── 🍒 sha256:def545cfb64af1c6d43650a378fbcc08816c6d2adbadb3b12b55f82289c2cebf
└── 📦 SBOMs for an image tag: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller:sha256-51351c6bbb9a1ef81a23fbdcd0fd5cb6660a93e080ff4fec5dbbb235bd4562aa.sbom
   └── 🍒 sha256:c2996953a9d0befe81ecbf12521245b32a93e336e2346e86b624c5fd5dc56fb5
└── 💾 Attestations for an image tag: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller:sha256-51351c6bbb9a1ef81a23fbdcd0fd5cb6660a93e080ff4fec5dbbb235bd4562aa.att
   ├── 🍒 sha256:705676515c619e186ed78d89e5997d794dcf10c971029bf34126cd153ee7eb2b
   ├── 🍒 sha256:173687aad52bf482ac9a6699c47b97a567d738c440c4509cf3f74a9c347c9b5e
   ├── 🍒 sha256:4e2d0081c8d1c9c574281e326d4847bb525c195ecf3b5f0354f595e258c7e34c
   ├── 🍒 sha256:f9c1947602e3f8e12bd342c641b001eb84943d992654d97b30b7067846b313fb
   ├── 🍒 sha256:10cc6e263f46ed101738215e7182ca74f5e8ccf6ee0d7dc141e91774b45558b9
   ├── 🍒 sha256:043d353999710b89e6025467c5a51e5c142109c17084288a1dba765c4ba94f80
   └── 🍒 sha256:703a183ee06c4652ea85f52753c5ec4321ca4a23f5e9cc679c21d8c8f5c295ab
znewman01 commented 1 year ago

@upodroid I filed https://github.com/sigstore/cosign/issues/2480

matglas commented 1 year ago

I was looking to do sign the Predicate Statement as a workaround just like you mentione @znewman01. But that would probably need a command that looks more like cosign sign-attest just to get the DSSE Envelope for it. Accepting the Provenance Statement as its provided by GitLab from a previous job.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 1 year ago

This issue was closed because it has been stalled for 5 days with no activity.

Dentrax commented 1 year ago

Anything we need to do here? Is this resolved?

Maybe we can update cosign examples doc to include "how we can sign & attach GitLab CI attestation with cosign" for the awareness. Wdyt @developer-guy?

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 1 year ago

This issue was closed because it has been stalled for 5 days with no activity.