βͺ back

Secure Software Supply Chain

Caleb Woodbine <calebwoodbine.public@gmail.com>

2024-02-26 Mon 17:30

Introduction πŸ—“οΈ

  • software supply chain security
  • what is Sigstore
  • how to upgrade your artifact builds in CI/CD
  • how to trust workloads with container runtimes and Kubernetes
  • how to give it a go!

About me πŸ‘‹

based in Wellington, New Zealand. Software and Infrastructure Engineer. Cloud & Open Source enthusiast.

https://calebwoodbine.nz

Background and Projects 🦾

  • Safe Surfer
    • OpenWRT routers
    • cloud infrastructure
  • ii.nz
    • Kubernetes conformance e2e test writing
    • sig-k8s-infra (registry.k8s.io, billing reports)
    • cloud infrastructure
  • personal
    • FlatTrack (software for humans living together)
    • islive.xyz (community infrastructure with friends; paused)

Software Supply Chain β›“

You have this πŸ‘‡

simplified graph. results may vary

Our Industry Has a Problem ❌

Recent attacks 😣

here are three big ones

  • log4shell
  • solarwinds
  • xz utils backdoor

Types of attacks πŸͺΏ

we’ll focus on the following today:

  • typo squatting and human error
  • compromised packages
  • compromised CI

Trust 🀝

Cloud Software Lifecycle πŸ”Ž

  1. developers
  2. programs & dependencies
  3. build systems
  4. continuous deployment
  5. production clusters

What are your trust levels? 🫡

  • do you know all of your dependencies?
  • do you trust all of them?
  • do you trust your CI?

A Call to Action πŸ“’

  • discover
    • know what’s in your software
    • know how your software is built
  • enrich
    • automate the build process
    • sign environment metadata and artifacts
  • enforce
    • require signed images in production

Under the OpenSSF / Linux Foundation

and friends, comes:

Making sure your software is what it claims to be

(see: https://sigstore.dev, https://github.com/sigstore, https://openssf.org/)

Ecosystem 🌏

https://openssf.landscape2.io/?group=sigstore&view-mode=grid

Architecture πŸ“Έ

TLDR

  • a thing to log transactions
  • a thing to author certificates
  • a thing to be a root ca
  • a thing to request certificates, sign and verify artifacts

https://www.sigstore.dev/how-it-works

Trust but verify

Sigstore is built on trust.

It has

  • a public trust root
  • folks across organisations
  • auditable infrastructure
  • live monitorable events (through Rekor, the transparency log)
  • Open Source!

cosign βš™οΈ

  • sign
  • attest
  • verify
  • tree

a CLI tool from Sigstore for signing and verifying.

supports keypairs as well as keyless with OpenID Connect (OIDC) integration.

Signing an image ✍️

with a key pair

cosign sign -r --key cosign.key localhost:5001/sigstore/demo:latest
	The sigstore service, hosted by sigstore a Series of LF Projects, LLC, is provided pursuant to the Hosted Project Tools Terms of Use, available at https://lfprojects.org/policies/hosted-project-tools-terms-of-use/.
	Note that if your submission includes personal data associated with this signed artifact, it will be part of an immutable record.
	This may include the email address associated with the account with which you authenticate your contractual Agreement.
	This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later, and is subject to the Immutable Record notice at https://lfprojects.org/policies/hosted-project-tools-immutable-records/.

By typing 'y', you attest that (1) you are not submitting the personal data of any other person; and (2) you understand and agree to the statement and the Agreement terms at the URLs listed above.
tlog entry created with index: 174277237
Pushing signature to: localhost:5001/sigstore/demo

Verifying an image πŸ”

with a key pair

cosign verify -r --key cosign.pub localhost:5001/sigstore/demo:latest
Verification for localhost:5001/sigstore/demo@sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9 --
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
  - The signatures were verified against the specified public key

[{"critical":{"identity":{"docker-reference":"localhost:5001/sigstore/demo"},"image":{"docker-manifest-digest":"sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCIE8zINyrmW0Clk/mWdzwJb7SdcaGzXI5oqZvxWVdu7nAAiEAkgnGuf8ouCccq3ZIAOtS0zKn6Ny52wyTd+lsLeNWPvY=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIxZTdjNDE3ZDI0ZjUzMmE5NmE3NTFmMzhjNzQ1YzJiODU1NjE3MTY2NDlmZTEwMzBjZWUzZWM0M2YyNTM0ZjQxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJRStXZFlXbjIzZHhaSjlxSDRBbzRqcHlOTVdJQUpEL05vN3FLcE1lM0pIdkFpRUFpNTlIYlVuSmxVWUVTWkpxbEFmZGZKMW1oSGlhT0xHK0Jpajk3UWttaDVRPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGTDBOUmR6SmxVbkJLVG5WaVZYVnlLMXBsUVdSb1NYSnZjbEp6T1FvemNtUndZalJVUTI5S1ptZGxlRmRYVkRsU1J5OWhkMU0wTjBsNVN6TndjV0Y1TDBSWVNqQm9SMkZhVTNnMlRYTldWWFZRTWxkcmVGaEJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=","integratedTime":1740515008,"logIndex":174278575,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}}]

Listing artifacts πŸ“œ

cosign tree localhost:5001/sigstore/demo:latest
πŸ“¦ Supply Chain Security Related artifacts for an image: localhost:5001/sigstore/demo@sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9
└── πŸ” Signatures for an image tag: localhost:5001/sigstore/demo:sha256-c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9.sig
   └── πŸ’ sha256:1e7c417d24f532a96a751f38c745c2b85561716649fe1030cee3ec43f2534f41
└── πŸ“¦ SBOMs for an image tag: localhost:5001/sigstore/demo:sha256-c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9.sbom
   └── πŸ’ sha256:20877239a68c06da4ec212d7623d7ac641e87bb095d51fe68fdf31c4a9c073da

Fulcio πŸ—³οΈ

Sigstore OIDC PKI that lets anyone access short-lived certificates via OIDC.

Code-signing certificate authority, issuing short-lived certificates to an authenticated identity and publishing them to a certificate transparency log.

https://github.com/sigstore/fulcio

Rekor πŸ“’

Software Supply Chain Transparency Log that allows you to verify the provenance of software artifacts.

Append-only, auditable transparency log service, Rekor records signed metadata to a ledger that can be queried, but can’t be tampered with.

https://github.com/sigstore/rekor

Looking at the public record

https://search.sigstore.dev/?logIndex=174277237

Keyless mode (1/3) πŸ”

  • short-lived certs
  • ephemeral keys
  • bound to an OIDC identity

Keyless signing associates identities, rather than keys, with an artifact signature. Fulcio issues short-lived certificates binding an ephemeral key to an OpenID Connect identity.

OpenID Connect is an authentication layer on top of OAuth 2.0, an authorization framework. The standard is controlled by the OpenID Foundation.

Keyless mode (2/3) πŸ”

Using OIDC from GitHub user auth, Google or Microsoft.

In CI, using OIDC from GitHub Actions or GitLab CI.

cosign verify \
  --certificate-oidc-issuer 'https://gitlab.com' \
  --certificate-identity-regexp 'https://gitlab.com/flattrack/flattrack//.gitlab-ci.yml@refs/(heads/main|tags/.*)' \
  -o text \
  registry.gitlab.com/flattrack/flattrack:latest \
    | jq
{
  "critical": {
    "identity": {
      "docker-reference": "registry.gitlab.com/flattrack/flattrack"
    },
    "image": {
      "docker-manifest-digest": "sha256:f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0"
    },
    "type": "cosign container image signature"
  },
  "optional": null
}

Keyless mode (3/3) πŸ”

Using OIDC from GitHub user auth, Google or Microsoft.

In CI, using OIDC from GitHub Actions or GitLab CI.

cosign verify \
    --certificate-oidc-issuer \
      'https://token.actions.githubusercontent.com' \
    --certificate-identity-regexp \
      'https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/(heads/main|tags/.*)' \
    -o text \
    cgr.dev/chainguard/nginx:latest \
      | jq
{
  "critical": {
    "identity": {
      "docker-reference": "cgr.dev/chainguard/nginx"
    },
    "image": {
      "docker-manifest-digest": "sha256:cf0d196bacf68158248da9156e0bc884abf31c9d66f9f81ea046a1a0d4253d65"
    },
    "type": "cosign container image signature"
  },
  "optional": null
}

Software Bill of Materials πŸ“ƒ

Aka SBOMs.

Declaration of components of a software package.

Formats are in variations of SPDX (Linux Foundation) and CycloneDX (OWASP).

In other words 🍰

(img src the99centchef.blogspot.com)

Like a health and ingredients label.

Software Bill of Materials πŸ“ƒ

Manual verification and contents inspection, container image:

cosign \
    verify-attestation \
    registry.gitlab.com/flattrack/flattrack:latest \
    --certificate-identity-regexp 'https://gitlab.com/flattrack/flattrack//.gitlab-ci.yml@refs/.*' \
    --certificate-oidc-issuer 'https://gitlab.com' \
      | jq -r .payload | base64 -D | jq . | jq -r .predicate.Data

Verification for registry.gitlab.com/flattrack/flattrack: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
  - The code-signing certificate was verified using trusted certificate authority certificates
Certificate subject: https://gitlab.com/flattrack/flattrack//.gitlab-ci.yml@refs/heads/main
Certificate issuer URL: https://gitlab.com
{
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "sbom-sha256:f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
  "spdxVersion": "SPDX-2.3",
  "creationInfo": {
    "created": "2025-01-24T12:35:23Z",
    "creators": [
      "Tool: ko 0.17.1"
    ]
  },
  "dataLicense": "CC0-1.0",
  "documentNamespace": "http://spdx.org/spdxdocs/ko/sha256:f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
  "documentDescribes": [
    "SPDXRef-Package-sha256-f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0"
  ],
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-sha256-f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
      "name": "sha256:f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "primaryPackagePurpose": "CONTAINER",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0"
        }
      ],
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/index@sha256:f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0?mediaType=application%2Fvnd.oci.image.index.v1%2Bjson",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sha256-84b62a12c533ff4b881519f76bd247249fa72a9754eabd95cc2bbdcd8a472f8f",
      "name": "cgr.dev/chainguard/static@sha256:84b62a12c533ff4b881519f76bd247249fa72a9754eabd95cc2bbdcd8a472f8f",
      "versionInfo": "cgr.dev/chainguard/static:latest",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "84b62a12c533ff4b881519f76bd247249fa72a9754eabd95cc2bbdcd8a472f8f"
        }
      ],
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/image@sha256:84b62a12c533ff4b881519f76bd247249fa72a9754eabd95cc2bbdcd8a472f8f?repository_url=cgr.dev%2Fchainguard%2Fstatic\u0026tag=latest",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sha256-ded1e26ff0b16299cdbae0038f1e0d1a261fe14b7a844be6fac78597cec84b9a",
      "name": "sha256:ded1e26ff0b16299cdbae0038f1e0d1a261fe14b7a844be6fac78597cec84b9a",
      "versionInfo": "linux/amd64",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "primaryPackagePurpose": "CONTAINER",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "ded1e26ff0b16299cdbae0038f1e0d1a261fe14b7a844be6fac78597cec84b9a"
        }
      ],
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/image@sha256:ded1e26ff0b16299cdbae0038f1e0d1a261fe14b7a844be6fac78597cec84b9a?arch=amd64\u0026mediaType=application%2Fvnd.oci.image.manifest.v1%2Bjson\u0026os=linux",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "name": "sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "versionInfo": "linux/arm64",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "primaryPackagePurpose": "CONTAINER",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24"
        }
      ],
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/image@sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24?arch=arm64\u0026mediaType=application%2Fvnd.oci.image.manifest.v1%2Bjson\u0026os=linux",
          "referenceType": "purl"
        }
      ]
    }
  ],
  "relationships": [
    {
      "spdxElementId": "SPDXRef-Package-sha256-f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
      "relationshipType": "DESCENDANT_OF",
      "relatedSpdxElement": "SPDXRef-Package-sha256-84b62a12c533ff4b881519f76bd247249fa72a9754eabd95cc2bbdcd8a472f8f"
    },
    {
      "spdxElementId": "SPDXRef-Package-sha256-f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
      "relationshipType": "VARIANT_OF",
      "relatedSpdxElement": "SPDXRef-Package-sha256-ded1e26ff0b16299cdbae0038f1e0d1a261fe14b7a844be6fac78597cec84b9a"
    },
    {
      "spdxElementId": "SPDXRef-Package-sha256-f2773fe9a53a52655ce9864fe49da5d6c79604165ab0ac9da596aa31d4d028a0",
      "relationshipType": "VARIANT_OF",
      "relatedSpdxElement": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24"
    }
  ]
}

Software Bill of Materials πŸ“ƒ

Manual verification and contents inspection, Go program in container image:

IMAGE=registry.gitlab.com/flattrack/flattrack
TAG=latest
PLATFORM=linux/arm64
DIGEST="$(crane digest --platform "$PLATFORM" "$IMAGE:$TAG")"
cosign \
    verify-attestation \
    "$IMAGE@$DIGEST" \
    --certificate-identity-regexp 'https://gitlab.com/flattrack/flattrack//.gitlab-ci.yml@refs/.*' \
    --certificate-oidc-issuer 'https://gitlab.com' \
      | jq -r .payload | base64 -D | jq . | jq -r .predicate.Data

Verification for registry.gitlab.com/flattrack/flattrack@sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24 --
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
  - The code-signing certificate was verified using trusted certificate authority certificates
Certificate subject: https://gitlab.com/flattrack/flattrack//.gitlab-ci.yml@refs/heads/main
Certificate issuer URL: https://gitlab.com
{
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "sbom-sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
  "spdxVersion": "SPDX-2.3",
  "creationInfo": {
    "created": "2025-01-24T12:35:23Z",
    "creators": [
      "Tool: ko 0.17.1"
    ]
  },
  "dataLicense": "CC0-1.0",
  "documentNamespace": "http://spdx.org/spdxdocs/ko/sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
  "documentDescribes": [
    "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24"
  ],
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "name": "sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "primaryPackagePurpose": "CONTAINER",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/image@sha256:d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24?mediaType=application%2Fvnd.oci.image.manifest.v1%2Bjson",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sha256-1187991523e7a7dc0513db00468d210c52b488a6e20b0b5ebda43de69981a85e",
      "name": "cgr.dev/chainguard/static@sha256:1187991523e7a7dc0513db00468d210c52b488a6e20b0b5ebda43de69981a85e",
      "versionInfo": "cgr.dev/chainguard/static:latest",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "copyrightText": "NOASSERTION",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "1187991523e7a7dc0513db00468d210c52b488a6e20b0b5ebda43de69981a85e"
        }
      ],
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:oci/image@sha256:1187991523e7a7dc0513db00468d210c52b488a6e20b0b5ebda43de69981a85e?repository_url=cgr.dev%2Fchainguard%2Fstatic\u0026tag=latest",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "name": "gitlab.com/flattrack/flattrack",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://gitlab.com/flattrack/flattrack",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/gitlab.com/flattrack/flattrack@v0.0.0-20250223082854-ea7ef89c02bd?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.NYTimes.gziphandler-v1.1.1",
      "name": "github.com/NYTimes/gziphandler",
      "versionInfo": "v1.1.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/NYTimes/gziphandler/@v/v1.1.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/nytimes/gziphandler@v1.1.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.beorn7.perks-v1.0.1",
      "name": "github.com/beorn7/perks",
      "versionInfo": "v1.0.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/beorn7/perks/@v/v1.0.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/beorn7/perks@v1.0.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.cespare.xxhash.v2-v2.2.0",
      "name": "github.com/cespare/xxhash/v2",
      "versionInfo": "v2.2.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/cespare/xxhash/v2/@v/v2.2.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/cespare/xxhash/v2@v2.2.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.go-logr.logr-v1.3.0",
      "name": "github.com/go-logr/logr",
      "versionInfo": "v1.3.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/go-logr/logr/@v/v1.3.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/go-logr/logr@v1.3.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.gogo.protobuf-v1.3.2",
      "name": "github.com/gogo/protobuf",
      "versionInfo": "v1.3.2",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/gogo/protobuf/@v/v1.3.2.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/gogo/protobuf@v1.3.2?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.golang-jwt.jwt.v5-v5.2.1",
      "name": "github.com/golang-jwt/jwt/v5",
      "versionInfo": "v5.2.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/golang-jwt/jwt/v5/@v/v5.2.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/golang-jwt/jwt/v5@v5.2.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.golang-migrate.migrate.v4-v4.17.0",
      "name": "github.com/golang-migrate/migrate/v4",
      "versionInfo": "v4.17.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/golang-migrate/migrate/v4/@v/v4.17.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/golang-migrate/migrate/v4@v4.17.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.google.gofuzz-v1.2.0",
      "name": "github.com/google/gofuzz",
      "versionInfo": "v1.2.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/google/gofuzz/@v/v1.2.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/google/gofuzz@v1.2.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.gorilla.mux-v1.8.1",
      "name": "github.com/gorilla/mux",
      "versionInfo": "v1.8.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/gorilla/mux/@v/v1.8.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/gorilla/mux@v1.8.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.hashicorp.errwrap-v1.1.0",
      "name": "github.com/hashicorp/errwrap",
      "versionInfo": "v1.1.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/hashicorp/errwrap/@v/v1.1.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/hashicorp/errwrap@v1.1.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.hashicorp.go-multierror-v1.1.1",
      "name": "github.com/hashicorp/go-multierror",
      "versionInfo": "v1.1.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/hashicorp/go-multierror/@v/v1.1.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/hashicorp/go-multierror@v1.1.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.imdario.mergo-v0.3.16",
      "name": "github.com/imdario/mergo",
      "versionInfo": "v0.3.16",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/imdario/mergo/@v/v0.3.16.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/imdario/mergo@v0.3.16?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.joho.godotenv-v1.5.1",
      "name": "github.com/joho/godotenv",
      "versionInfo": "v1.5.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/joho/godotenv/@v/v1.5.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/joho/godotenv@v1.5.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.json-iterator.go-v1.1.12",
      "name": "github.com/json-iterator/go",
      "versionInfo": "v1.1.12",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/json-iterator/go/@v/v1.1.12.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/json-iterator/go@v1.1.12?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.lib.pq-v1.10.9",
      "name": "github.com/lib/pq",
      "versionInfo": "v1.10.9",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/lib/pq/@v/v1.10.9.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/lib/pq@v1.10.9?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.matttproud.golang_protobuf_extensions.v2-v2.0.0",
      "name": "github.com/matttproud/golang_protobuf_extensions/v2",
      "versionInfo": "v2.0.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/matttproud/golang_protobuf_extensions/v2/@v/v2.0.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/matttproud/golang_protobuf_extensions/v2@v2.0.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.modern-go.concurrent-v0.0.0-20180306012644-bacd9c7ef1dd",
      "name": "github.com/modern-go/concurrent",
      "versionInfo": "v0.0.0-20180306012644-bacd9c7ef1dd",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/modern-go/concurrent/@v/v0.0.0-20180306012644-bacd9c7ef1dd.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.modern-go.reflect2-v1.0.2",
      "name": "github.com/modern-go/reflect2",
      "versionInfo": "v1.0.2",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/modern-go/reflect2/@v/v1.0.2.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/modern-go/reflect2@v1.0.2?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.prometheus.client_golang-v1.17.0",
      "name": "github.com/prometheus/client_golang",
      "versionInfo": "v1.17.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/prometheus/client_golang/@v/v1.17.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/prometheus/client_golang@v1.17.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.prometheus.client_model-v0.5.0",
      "name": "github.com/prometheus/client_model",
      "versionInfo": "v0.5.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/prometheus/client_model/@v/v0.5.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/prometheus/client_model@v0.5.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.prometheus.common-v0.45.0",
      "name": "github.com/prometheus/common",
      "versionInfo": "v0.45.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/prometheus/common/@v/v0.45.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/prometheus/common@v0.45.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.prometheus.procfs-v0.12.0",
      "name": "github.com/prometheus/procfs",
      "versionInfo": "v0.12.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/prometheus/procfs/@v/v0.12.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/prometheus/procfs@v0.12.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-github.com.rs.cors-v1.11.0",
      "name": "github.com/rs/cors",
      "versionInfo": "v1.11.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/github.com/rs/cors/@v/v1.11.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/github.com/rs/cors@v1.11.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-go.uber.org.atomic-v1.11.0",
      "name": "go.uber.org/atomic",
      "versionInfo": "v1.11.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/go.uber.org/atomic/@v/v1.11.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/go.uber.org/atomic@v1.11.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-golang.org.x.net-v0.23.0",
      "name": "golang.org/x/net",
      "versionInfo": "v0.23.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/golang.org/x/net/@v/v0.23.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/golang.org/x/net@v0.23.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-golang.org.x.sys-v0.18.0",
      "name": "golang.org/x/sys",
      "versionInfo": "v0.18.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/golang.org/x/sys/@v/v0.18.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/golang.org/x/sys@v0.18.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-golang.org.x.text-v0.14.0",
      "name": "golang.org/x/text",
      "versionInfo": "v0.14.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/golang.org/x/text/@v/v0.14.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/golang.org/x/text@v0.14.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-google.golang.org.protobuf-v1.32.0",
      "name": "google.golang.org/protobuf",
      "versionInfo": "v1.32.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/google.golang.org/protobuf/@v/v1.32.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/google.golang.org/protobuf@v1.32.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-gopkg.in.inf.v0-v0.9.1",
      "name": "gopkg.in/inf.v0",
      "versionInfo": "v0.9.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/gopkg.in/inf.v0/@v/v0.9.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/gopkg.in/inf.v0@v0.9.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-gopkg.in.yaml.v2-v2.4.0",
      "name": "gopkg.in/yaml.v2",
      "versionInfo": "v2.4.0",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/gopkg.in/yaml.v2/@v/v2.4.0.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/gopkg.in/yaml.v2@v2.4.0?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-k8s.io.apimachinery-v0.29.1",
      "name": "k8s.io/apimachinery",
      "versionInfo": "v0.29.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/k8s.io/apimachinery/@v/v0.29.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/k8s.io/apimachinery@v0.29.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-k8s.io.klog.v2-v2.110.1",
      "name": "k8s.io/klog/v2",
      "versionInfo": "v2.110.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/k8s.io/klog/v2/@v/v2.110.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/k8s.io/klog/v2@v2.110.1?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-k8s.io.utils-v0.0.0-20230726121419-3b25d923346b",
      "name": "k8s.io/utils",
      "versionInfo": "v0.0.0-20230726121419-3b25d923346b",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/k8s.io/utils/@v/v0.0.0-20230726121419-3b25d923346b.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/k8s.io/utils@v0.0.0-20230726121419-3b25d923346b?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sigs.k8s.io.json-v0.0.0-20221116044647-bc3834ca7abd",
      "name": "sigs.k8s.io/json",
      "versionInfo": "v0.0.0-20221116044647-bc3834ca7abd",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/sigs.k8s.io/json/@v/v0.0.0-20221116044647-bc3834ca7abd.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/sigs.k8s.io/json@v0.0.0-20221116044647-bc3834ca7abd?type=module",
          "referenceType": "purl"
        }
      ]
    },
    {
      "SPDXID": "SPDXRef-Package-sigs.k8s.io.structured-merge-diff.v4-v4.4.1",
      "name": "sigs.k8s.io/structured-merge-diff/v4",
      "versionInfo": "v4.4.1",
      "filesAnalyzed": false,
      "licenseDeclared": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "downloadLocation": "https://proxy.golang.org/sigs.k8s.io/structured-merge-diff/v4/@v/v4.4.1.zip",
      "copyrightText": "NOASSERTION",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceLocator": "pkg:golang/sigs.k8s.io/structured-merge-diff/v4@v4.4.1?type=module",
          "referenceType": "purl"
        }
      ]
    }
  ],
  "relationships": [
    {
      "spdxElementId": "SPDXRef-DOCUMENT",
      "relationshipType": "DESCRIBES",
      "relatedSpdxElement": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24"
    },
    {
      "spdxElementId": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "relationshipType": "DESCENDANT_OF",
      "relatedSpdxElement": "SPDXRef-Package-sha256-1187991523e7a7dc0513db00468d210c52b488a6e20b0b5ebda43de69981a85e"
    },
    {
      "spdxElementId": "SPDXRef-Package-sha256-d20d7d0bf1d33a1f4df177a23d1025ff997e56c50afc9f00b3eb081ceb14dc24",
      "relationshipType": "CONTAINS",
      "relatedSpdxElement": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.NYTimes.gziphandler-v1.1.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.beorn7.perks-v1.0.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.cespare.xxhash.v2-v2.2.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.go-logr.logr-v1.3.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.gogo.protobuf-v1.3.2"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.golang-jwt.jwt.v5-v5.2.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.golang-migrate.migrate.v4-v4.17.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.google.gofuzz-v1.2.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.gorilla.mux-v1.8.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.hashicorp.errwrap-v1.1.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.hashicorp.go-multierror-v1.1.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.imdario.mergo-v0.3.16"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.joho.godotenv-v1.5.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.json-iterator.go-v1.1.12"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.lib.pq-v1.10.9"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.matttproud.golang_protobuf_extensions.v2-v2.0.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.modern-go.concurrent-v0.0.0-20180306012644-bacd9c7ef1dd"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.modern-go.reflect2-v1.0.2"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.prometheus.client_golang-v1.17.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.prometheus.client_model-v0.5.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.prometheus.common-v0.45.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.prometheus.procfs-v0.12.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-github.com.rs.cors-v1.11.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-go.uber.org.atomic-v1.11.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-golang.org.x.net-v0.23.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-golang.org.x.sys-v0.18.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-golang.org.x.text-v0.14.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-google.golang.org.protobuf-v1.32.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-gopkg.in.inf.v0-v0.9.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-gopkg.in.yaml.v2-v2.4.0"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-k8s.io.apimachinery-v0.29.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-k8s.io.klog.v2-v2.110.1"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-k8s.io.utils-v0.0.0-20230726121419-3b25d923346b"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-sigs.k8s.io.json-v0.0.0-20221116044647-bc3834ca7abd"
    },
    {
      "spdxElementId": "SPDXRef-Package-gitlab.com.flattrack.flattrack-v0.0.0-20250223082854-ea7ef89c02bd",
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "SPDXRef-Package-sigs.k8s.io.structured-merge-diff.v4-v4.4.1"
    }
  ]
}

Understanding an ✨ SBOM ✨

  • unique name
  • syntax version
  • SBOM author (e.g Ko, Syft, etc…)
  • document reference (e.g: the sha256 of the package)
  • list of included packages
  • list of package relationships

Visualising the data (1/3) πŸ‘“

Using https://pypi.org/project/sbom2dot/

# install it
python3 -m venv ./pythonenv
source pythonenv/bin/activate
python3 -m pip install sbom2dot

# convert SPDX-JSON to graphviz dot
sbom2dot --input-file ./flattrack.spdx.json --output-file ./flattrack-sbom.dot

# convert graphviz dot to SVG
dot -Tsvg ./flattrack-sbom.dot > ./flattrack-sbom.svg

Visualising the data (2/3) πŸ‘“

Visualising the data (3/3) πŸ‘“

Producing an ✨ SBOM ✨

With Syft

brew install syft

syft registry.gitlab.com/flattrack/flattrack:latest -o spdx-json

https://github.com/anchore/syft

kubernetes-sigs/bom

go install sigs.k8s.io/bom/cmd/bom@latest

bom generate \
    --output flattrack.spdx.json \
    --image registry.gitlab.com/flattrack/flattrack:latest

https://kubernetes-sigs.github.io/bom/

With Ko

ko publish
2025/02/25 20:29:12 Using base cgr.dev/chainguard/static:latest@sha256:853bfd4495abb4b65ede8fc9332513ca2626235589c2cef59b4fce5082d0836d for example.com/demo
2025/02/25 20:29:13 current folder is not a git repository. Git info will not be available
2025/02/25 20:29:13 Building example.com/demo for linux/amd64
2025/02/25 20:29:13 Publishing localhost:5001/sigstore/demo:latest
2025/02/25 20:29:13 pushed blob: sha256:20877239a68c06da4ec212d7623d7ac641e87bb095d51fe68fdf31c4a9c073da
2025/02/25 20:29:13 pushed blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2025/02/25 20:29:13 pushed blob: sha256:8dfcf6da687c103299df7e08b8af1c9863786fa750d89357bd3efad2f0270532
2025/02/25 20:29:13 pushed blob: sha256:05c003811a4a320e42b6a9cfd38842ae3f454cda7a51084fb6b9031e0f4e0fb9
2025/02/25 20:29:13 localhost:5001/sigstore/demo:sha256-c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9.sbom: digest: sha256:ea14054d9889f45482d7a190f7d33a685364f4522ae3a13dea3d660cf56507f3 size: 373
2025/02/25 20:29:13 Published SBOM localhost:5001/sigstore/demo:sha256-c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9.sbom
2025/02/25 20:29:13 pushed blob: sha256:472ccb22946519dc592bad512835959571cb9522436348871073e62f2669ffcf
2025/02/25 20:29:14 pushed blob: sha256:9829e4b4d6e1aabff5a35581e1f325922c7ac8b963db89280960ffe2bbf91d03
2025/02/25 20:29:14 localhost:5001/sigstore/demo:latest: digest: sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9 size: 1336
2025/02/25 20:29:14 Published localhost:5001/sigstore/demo@sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9
localhost:5001/sigstore/demo@sha256:c373c8ba40a6db411dd786ce73bf31eb9649cef5654274fe91430db4b84a55a9

defaults to --sbom=spdx.

https://ko.build/

npm

npm sbom --sbom-format spdx

https://docs.npmjs.com/cli/v10/commands/npm-sbom

Salsa πŸ’ƒ

(img src withtwospoons.com)

SLSA.dev βš™οΈ

related to Sigstore…

Supply-chain Levels for Software Artifacts, or SLSA (“salsa”).

It’s a security framework, a checklist of standards and controls to prevent tampering, improve integrity, and secure packages and infrastructure. It’s how you get from “safe enough” to being as resilient as possible, at any link in the chain.

Artifact Provenance

make claims through signed metadata about

  • build environments,
  • dependencies; and
  • artifacts

SLSA levels

Level Requirements Focus Benefits
Zero (none) (n/a) (none)
One Provenance showing how the package was built Mistakes, documentation Basic reproducibility
Two Signed provenance, generated by a hosted build platform Tampering after the build Increases auditability
Three Hardened build platform Tampering during the build Confidence in providence

(see: https://slsa.dev/spec/v1.0/levels)

Who’s adopted SLSA?

projects

  • Kubernetes (at level 2)
  • Kyverno (at level 3)
  • ko.build (at level 3)
  • FluxCD (at level 3)

organisations

  • Chainguard
  • CitiBank
  • DataDog
  • Google
  • Verison

and more!

In GitHub Actions

...
  generate-kyverno-provenance:
    needs: release-images
    permissions:
      id-token: write   # To sign the provenance.
      packages: write   # To upload assets to release.
      actions: read     # To read the workflow path.
    # NOTE: The container generator workflow is not officially released as GA.
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
    with:
      image: ghcr.io/${{ github.repository_owner }}/kyverno
      digest: "${{ needs.release-images.outputs.kyverno-digest }}"
      registry-username: ${{ github.actor }}
    secrets:
      registry-password: ${{ secrets.GITHUB_TOKEN }}
...

https://github.com/slsa-framework/slsa-github-generator

https://github.blog/security/supply-chain-security/slsa-3-compliance-with-github-actions/

In GitLab CI

variables:
  RUNNER_GENERATE_ARTIFACTS_METADATA: true

https://about.gitlab.com/blog/2022/11/30/achieve-slsa-level-2-compliance-with-gitlab/

Tekton

Using Chains, the CI environment running on your own infrastructure, can both generate and validate artifact provenance within.

(see: https://tekton.dev/docs/chains/ and https://tekton.dev/docs/chains/slsa-provenance/)

Other Sigstore integrations 🧰

Not limited to but including…

Container runtime integration

CRI-O, Podman and bootc

{
    "default": [
        {
            "type": "reject"
        }
    ],
    "transports": {
        "docker": {
            "ghcr.io/ublue-os": [
                {
                    "type": "sigstoreSigned",
                    "keyPath": "/etc/pki/containers/ublue-os.pub",
                    "signedIdentity": {
                        "type": "matchRepository"
                    }
                }
            ],
            "": [
                {
                    "type": "insecureAcceptAnything"
                }
            ]
        }
    }
}

Note: containerd has some efforts but less Sigstore specific (https://github.com/containerd/containerd/blob/main/docs/image-verification.md)

ucore-k8s example repo

  • base OS is signed
  • configured to require all containers run are signed by repo author
  • all container images deployed via Kubernetes are vendored and signed
  • separately: all workloads on Knative Serving run using Kata-Containers
{
    "default": [
        {
            "type": "reject"
        }
    ],
    "transports": {
        "docker": {
            "": [
                {
                    "type": "sigstoreSigned",
                    "keyPath": "/etc/pki/containers/ucore-k8s.pub",
                    "signedIdentity": {
                        "type": "matchRepository"
                    }
                }
            ]
        }
    }
}

based on Fedora + ucore (Universal Blue): https://github.com/BobyMCbobs/ucore-k8s

policy-controller

  • based on cosign
  • verifies container images in your cluster
  • enforces policies based on supply chain metadata like SBOMs

A ValidatingWebhookConfiguration to fit your needs.

ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.

Some Functions Include

e.g:

  • images with names like SOMETHING/** must be signed with given authorities
  • parse SBOM attachments (including SPDX metadata) for gating off images containing select dependencies
    • images cannot contain the following dependencies SOMETHING at version SOMETHING

Example of policies (1/4)

apiVersion: policy.sigstore.dev/v1alpha1
kind: ClusterImagePolicy
metadata:
  name: kubernetes-signed
spec:
  images:
  - glob: registry.k8s.io/**
  authorities:
  - keyless:
      url: https://fulcio.sigstore.dev
      identities:
      - issuer: https://accounts.google.com
        subject: krel-trust@k8s-releng-prod.iam.gserviceaccount.com
    ctlog:
      url: https://rekor.sigstore.dev

Example of policies (2/4)

apiVersion: policy.sigstore.dev/v1alpha1
kind: ClusterImagePolicy
metadata:
  name: certmanager-signed
spec:
  images:
    - glob: quay.io/jetstack/cert-manager-*
  authorities:
    - key:
        hashAlgorithm: sha512
        data: |
          -----BEGIN PUBLIC KEY-----
          MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsZZKaaIRjOpzbiWYIDKO
          yry9XGBqAfve1iOGmt5VO1jpjNoEseT6zewozHfWTM7osxayy2WjN8G+QV39MlT3
          Vxo91/31g+Zcq8KcvxG+iB8GRaD9pNgLmghorv+eYDiPYMO/+fhsLImyG5WEoPct
          MeCBD7umZ/A2t96U9DQxVDqQbTHlsNludno1p1wsgRnfUM3QHexNljDvJg5FcDMo
          dCpVLpRNvbw0lbJVfybJ4siJ5o/MmXzy0QCJpw+yMIqvqMc8qgKJ1yooJtuTVF4t
          4/luP+EG/oVIiSWCFeRMqYdbJ3R+CJi+4LN7vFNYQM1Q/NwOB52RteaR7wnqmcBz
          qSYK32MM8xdPCQ5tioWwnPTRbPZuzsZsRmJsKBO9JUrBYdDntZX1xY5g4QNSufxi
          QgJgJSU7E4VGMvagEzB1JzvOr6A/qNFCO1Z6JsA3jw3cJLV1rSHfxqfSXBACTLDf
          6bOPWRILRKydTJA6uLKNKmo1/nFm3jvd5tHKOjy4VAQLJ/Vx9wBsAAiLa+06veun
          Oz3AJ9sNh3wLp21RL11u9TuOKRBipE/TYsBYp8jpIyWPXDSV+JcD/TZqoT8y0Z6S
          0damfUmspuK9DTQFL2crpeaqJSG9RA+OuPZLxGD1IMURTsPJB7kXhPtmceeirBnw
          sVcRHHDitVt8oO/x4Wus1c0CAwEAAQ==
          -----END PUBLIC KEY-----

Example of policies (3/4)

apiVersion: policy.sigstore.dev/v1alpha1
kind: ClusterImagePolicy
metadata:
  name: sample-ko-monorepo
spec:
  images:
  - glob: "ghcr.io/bobymcbobs/sample-ko-monorepo**"
  authorities:
  - keyless:
      identities:
      - issuer: https://token.actions.githubusercontent.com
        subjectRegExp: |
          ^(https://github.com/BobyMCbobs/sample-ko-monorepo/.github/workflows/build-and-release.yml@refs/(heads/main|tags/v[0-9].[0-9].[0-9]))$

Example of policies (4/4)

Block an image from running if the SBOM predicated contains a package with a version:

              predicate: {
                  Data: {
                      packages: [...{
                          name: name
                          versionInfo: versionInfo
                          if list.Contains(log4shell_names, name) &&
                              list.Contains(log4shell_versions, versionInfo) {
                              err: strings.Join([
                                  "Error: SPDX SBOM contains package",
                                  name, "version", versionInfo, "which is",
                                  "vulnerable to Log4Shell (CVE-2021-44228)"
                              ], " ")
                              name: err
                          }
                      }]
                  }
              }

https://github.com/chainguard-dev/policy-catalog/blob/main/policies/packages/log4shell-cue.yaml

Kyverno

A Kubernetes-Native Policy-As-Code tool for validating and mutating resources.

Kyverno allows platform engineers to automate security, compliance, and best practices validation and deliver secure self-service to application teams.

(see https://kyverno.io/docs/writing-policies/verify-images/sigstore/)

Kyverno example (1/3)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-image
spec:
  webhookConfiguration:
    failurePolicy: Fail
    timeoutSeconds: 30
  background: false
  rules:
    - name: check-image
      match:
        any:
        - resources:
            kinds:
              - Pod
      verifyImages:
      - imageReferences:
        - "ghcr.io/kyverno/test-verify-image*"
        failureAction: Enforce
        attestors:
        - count: 1
          entries:
          - keys:
              publicKeys: |-
                -----BEGIN PUBLIC KEY-----
                MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
                5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
                -----END PUBLIC KEY-----
              rekor:
                ignoreTlog: true
                url: https://rekor.sigstore.dev

Kyverno example (2/3)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: signed-task-image
spec:
  rules:
  - name: check-signature
    match:
      any:
      - resources:
          kinds:
          - tekton.dev/v1beta1/TaskRun.status
    imageExtractors:
      TaskRun:
        - name: "taskrunstatus"
          path: "/status/taskSpec/steps/*"
          value: "image"
          key: "name"
    verifyImages:
    - imageReferences:
      - "*"
      failureAction: Enforce
      required: false
      attestors:
      - entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEahmSvGFmxMJABilV1usgsw6ImcQ/
              gDaxw57Sq+uNGHW8Q3zUSx46PuRqdTI+4qE3Ng2oFZgLMpFN/qMrP0MQQg==
              -----END PUBLIC KEY-----

Kyverno example (3/3)

apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
  name: registry-image-datavolume
spec:
  source:
    registry:
      url: "docker://kubevirt/fedora-cloud-registry-disk-demo"
  pvc:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 5Gi
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: signed-data-volume-image
spec:
  rules:
  - name: check-signature
    match:
      any:
      - resources:
          kinds:
          - cdi.kubevirt.io/v1beta1.DataVolume
    imageExtractors:
      DataVolume:
      - path: /spec/source/registry/url
        jmesPath: "trim_prefix(@, 'docker://')"
    verifyImages:
    - imageReferences:
      - "*"
      failureAction: Enforce
      required: false
      attestors:
      - entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEahmSvGFmxMJABilV1usgsw6ImcQ/
              gDaxw57Sq+uNGHW8Q3zUSx46PuRqdTI+4qE3Ng2oFZgLMpFN/qMrP0MQQg==
              -----END PUBLIC KEY-----

FluxCD

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
  name: cert-manager
  namespace: flux-system
spec:
  interval: 5m
  url: oci://ghcr.io/${GITHUB_USER}/manifests/cert-manager
  ref:
    semver: "*"
  secretRef:
    name: ghcr-auth
  verify:
    provider: cosign
    secretRef:
      name: cert-manager-cosign-pub

Brew

All packages distributed with Homebrew are signed with cosign

brew install cosign
cosign 2.4.2 is already installed but outdated (so it will be upgraded).
==> Downloading https://ghcr.io/v2/homebrew/core/cosign/manifests/2.4.3
==> Fetching cosign
==> Downloading https://ghcr.io/v2/homebrew/core/cosign/blobs/sha256:3b6136f1eb6d0f0301016ab7650e6d81c85662a9e6a40cbf3efa98d75e24df9d
==> Verifying attestation for cosign
==> Upgrading cosign
  2.4.2 -> 2.4.3
==> Pouring cosign--2.4.3.arm64_sequoia.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /opt/homebrew/etc/bash_completion.d
==> Summary
🍺  /opt/homebrew/Cellar/cosign/2.4.3: 11 files, 77.5MB
==> Running `brew cleanup cosign`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
Removing: /opt/homebrew/Cellar/cosign/2.4.2... (11 files, 77.3MB)
Removing: /Users/calebwoodbine/Library/Caches/Homebrew/cosign_bottle_manifest--2.4.2... (7.2KB)
Removing: /Users/calebwoodbine/Library/Caches/Homebrew/cosign--2.4.2... (22.9MB)

(even the cosign package delivered with Brew is verified with Sigstore!)

https://blog.sigstore.dev/homebrew-build-provenance/

Brew is a popular package manager for both macOS and Linux

Gitsign

Sign commits using Sigstore and OIDC

# configure
git config --local gpg.x509.program gitsign  # Use gitsign for signing
git config --local gpg.format x509  # gitsign expects x509 args
git config --local commit.gpgsign true  # Sign all commits. NOTE makes git depend on internet
git config --local tag.gpgsign true  # Sign all tags. NOTE makes git depend on internet

# make a commit
git commit

# verify a commit
gitsign verify --certificate-identity=<your email> --certificate-oidc-issuer=<your issuer> HEAD

https://github.com/sigstore/gitsign

Alternatively, although it works completely differently: sign with GPG

GoReleaser

If you’re app is written in Go and you’re distributing binaries.

sboms:
  - id: foo
    documents:
      - "${artifact}.spdx.sbom.json"
    cmd: syft
    args: ["$artifact", "--output", "spdx-json=$document"]
    artifacts: archive
    ids:
      - foo
      - bar
binary_signs:
  - cmd: cosign
    stdin: "{{ .Env.COSIGN_PWD }}"
    args:
      - "sign-blob"
      - "--key=cosign.key"
      - "--output-signature=${signature}"
      - "${artifact}"
      - "--yes"

(see: https://goreleaser.com/customization/sbom/, https://goreleaser.com/customization/binary_sign/#signing-with-cosign)

Self-hosting Sigstore for your organisation 🏒

if you need to or want to: https://blog.sigstore.dev/a-guide-to-running-sigstore-locally-f312dfac0682/

What’s the goss? πŸ—£οΈ

We need to make it possible to verify provenance along the entire chain and the goal of the Sigstore effort is to enable just that.

Ryan Hurst - Google Production Security Team

An open source community coming together to collaborate and develop a solution to ease the adoption of software signing…

Luke Hinds - Co-creator, sigstore & Senior Principal Software Engineer, Red Hat

Sigstore will make code signing free and easy for software developers, providing an important first line of defense.

Lily Hay Newman - Wired

(see: https://github.com/sigstore/friends)

Further advice πŸ₯Š

  • pin your dependencies
  • review your dependencies
  • require signed commits from trusted keys or identities
  • build as much software as you can from source (“vendoring”), incorperating signing and validation along the way
  • using your CI, find tooling to vet and update dependencies such as Dependabot or Renovate, avoiding vulnerable software versions
  • deny your production and staging environments from running any unverified software

Things I’d like to see in this space β˜€οΈ

  • macOS Gatekeeper style (offline) binary authorization
  • further adoption in Open Source projects
  • further usage of SBOMs in organisations

Links πŸ‘€

Recap! πŸ€”

  • Supply chain security is important
  • Sigstore being a part of your CI, CD and infrastructure improves your security posture
  • SBOMs help you to understand what is in your software
  • SLSA elevates your ability to trust your software, dependencies and CI

End

Kia Ora and Haere Rā!

Questions ☝️