sigstore / policy-controller

Sigstore Policy Controller - an admission controller that can be used to enforce policy on a Kubernetes cluster based on verifiable supply-chain metadata from cosign
Other
123 stars 55 forks source link

Support for Sigstore Bundle Specification #1406

Open codysoyland opened 5 months ago

codysoyland commented 5 months ago

Description

Policy-Controller currently supports verification of attestations/signatures generated using cosign sign/cosign attest, which attach signatures/attestations using the process described in the Cosign Signature Specification. In summary, this signature attachment scheme utilizes a Tag-based discovery mechanism where an image manifest (OCI Image Manifest V1) is created to reference a specific image digest, and each layer references a payload in either the Simple Signing (application/vnd.dev.cosign.simplesigning.v1+json) format or the DSSE (application/vnd.dsse.envelope.v1+json) format. Other data needed to perform verification is stored in annotations on the layer descriptor.

A newer specification for storing Sigstore Bundles has been accepted, which utilizes the OCI 1.1 Manifest Referrers API to attach Sigstore Bundles as referring artifacts to an image, which simplifies the storage/retrieval of attestations/signatures and enables use of the newer generation of Sigstore clients (e.g. sigstore-js, sigstore-python, and sigstore-go) to verify Sigstore Bundles. cosign currently lacks that ability, but there are plans to support it in the future. Additionally, GitHub Artifact Attestations attach Sigstore Bundles following the spec, and Policy-Controller is currently unable to verify these attestations.

I would like to add support for verifying image signatures/attestations using the new bundle spec in Policy-Controller. In order to do so without breaking existing usage, I propose we add a signatureFormat property to the Authority type, which could be one of [legacy, bundle] and would default to legacy. When the authority has the property signatureFormat: bundle, the controller would take an alternative code path which would implement the Sigstore Bundle spec to look up the attestations and verify them using sigstore-go. I am preparing a branch which implements the above scheme, and I hope to open a PR in the coming days.

hectorj2f commented 4 months ago

@codysoyland It sounds good. Could you share some examples of yaml definitions here? That would help to understand the changes.

codysoyland commented 4 months ago

@hectorj2f Of course! My proposed change to the yaml is the addition of signatureFormat in the authorities list.

Taking this example from the docs:

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: image-policy-keyless-with-attestations
spec:
  images:
  - glob: registry.local:5000/policy-controller/demo*
  authorities:
  - name: verify custom attestation
    keyless:
      url: http://fulcio.fulcio-system.svc
      identities:
      - issuerRegExp: .*kubernetes.default.*
        subjectRegExp: .*kubernetes.io/namespaces/default/serviceaccounts/default
    ctlog:
      url: http://rekor.rekor-system.svc
    attestations:
    - name: custom-match-predicate
      predicateType: https://cosign.sigstore.dev/attestation/v1
      policy:
        type: cue
        data: |
          predicateType: "https://cosign.sigstore.dev/attestation/v1"
          predicate: "foobar e2e test"

After my proposed changes, the above policy would continue to work as it always had: verify the attestation using the classic cosign verification flow.

If a user wished to verify a Sigstore Bundle, they would modify the above with a signatureFormat value set to bundle. I have bolded the change to make it more clear:

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: image-policy-keyless-with-attestations
spec:
  images:
  - glob: registry.local:5000/policy-controller/demo*
  authorities:
  - name: verify custom attestation
    signatureFormat: bundle
    keyless:
      url: http://fulcio.fulcio-system.svc
      identities:
      - issuerRegExp: .*kubernetes.default.*
        subjectRegExp: .*kubernetes.io/namespaces/default/serviceaccounts/default
    ctlog:
      url: http://rekor.rekor-system.svc
    attestations:
    - name: custom-match-predicate
      predicateType: https://cosign.sigstore.dev/attestation/v1
      policy:
        type: cue
        data: |
          predicateType: "https://cosign.sigstore.dev/attestation/v1"
          predicate: "foobar e2e test"
julien-michaud commented 4 days ago

hello @codysoyland ,

Any news on this issue ?

We are signing our docker images with the actions/attest-build-provenance@v1 Action.

We are trying to use the controller from this repository to verify images in our k8s clusters but we are having this issue when submitting new pods:

Error from server (BadRequest): error when creating "pod.yaml": admission webhook "policy.sigstore.dev" denied the request: validation failed: failed policy: github-policy: spec.template.spec.containers[0].image europe-docker.pkg.dev/project/company-prod/kube/mp/tiny-developer-tools@sha256:abd5c78061356d3f9b14475a1afd11c68baf6c89c03a036b442ef7d520556fcd no bundle found in referrers

Is this problem related to your issue ?

snippet of the workflow creating, pushing and signing the image

      - name: Build Docker image
        id: image-results
        if: ${{ !inputs.dry-run }}
        uses: docker/build-push-action@v6
        with:
          context: .
          file: production/kubernetes/marketplace/containers/jre/Dockerfile
          pull: true
          push: true
          platforms: linux/amd64
          tags: ${{ env.REGISTRY }}/${{ steps.cleaned-artifact-name.outputs.ARTIFACT_CLEANED }}:${{ inputs.version }}
          build-args: |
            MIRAKL_VERSION=${{ inputs.version }}
            EXTRA_PACKAGES=${{ inputs.kubernetes-build-extra-packages }}
            BASE_IMAGE=jre${{ steps.java-major.outputs.JAVA_MAJOR_VERSION }}
            BASE_IMAGE_VERSION=stable

      # attest image
      - uses: actions/attest-build-provenance@v1
        if: ${{ !inputs.dry-run }}
        with:
          subject-digest: ${{steps.image-results.outputs.digest}}
          subject-name: '${{ env.REGISTRY }}/${{ steps.cleaned-artifact-name.outputs.ARTIFACT_CLEANED }}'
          push-to-registry: true

Thanks