diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index daedaf40d..702212c82 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -60,10 +60,6 @@ jobs: secrets: inherit aks-test-cleanup: - env: - AZURE_SUBSCRIPTION_ID: daae1e1a-63dc-454f-825d-b39289070f79 - AZURE_CLIENT_ID: 814e6e97-120c-4534-b8a9-f1645bc99500 - AZURE_TENANT_ID: 72f988bf-86f1-41af-91ab-2d7cd011db47 needs: ['build_test_aks_e2e_conditional'] runs-on: ubuntu-latest permissions: @@ -86,10 +82,10 @@ jobs: - name: Az CLI login uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1 with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: clean up run: | - make e2e-cleanup AZURE_SUBSCRIPTION_ID=${{ env.AZURE_SUBSCRIPTION_ID }} \ No newline at end of file + make e2e-cleanup AZURE_SUBSCRIPTION_ID=${{ secrets.AZURE_SUBSCRIPTION_ID }} \ No newline at end of file diff --git a/.github/workflows/e2e-aks.yml b/.github/workflows/e2e-aks.yml index b3c8c3da4..60b705cc9 100644 --- a/.github/workflows/e2e-aks.yml +++ b/.github/workflows/e2e-aks.yml @@ -20,11 +20,6 @@ on: jobs: build_test_aks_e2e: name: "Build and run e2e Test on AKS" - env: - AZURE_CLIENT_ID: 814e6e97-120c-4534-b8a9-f1645bc99500 - AZURE_TENANT_ID: 72f988bf-86f1-41af-91ab-2d7cd011db47 - AZURE_SUBSCRIPTION_ID: daae1e1a-63dc-454f-825d-b39289070f79 - AZURE_SP_OBJECT_ID: fd917b28-cdc0-4828-92c9-1ca8203842a3 runs-on: ubuntu-latest timeout-minutes: 30 environment: azure-test @@ -46,9 +41,9 @@ jobs: - name: Az CLI login uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1 with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Cache AAD tokens run: | az version @@ -66,7 +61,7 @@ jobs: - name: Run e2e on Azure run: | - make e2e-aks KUBERNETES_VERSION=${{ inputs.k8s_version }} GATEKEEPER_VERSION=${{ inputs.gatekeeper_version }} TENANT_ID=${{ env.AZURE_TENANT_ID }} AZURE_SP_OBJECT_ID=${{ env.AZURE_SP_OBJECT_ID }} + make e2e-aks KUBERNETES_VERSION=${{ inputs.k8s_version }} GATEKEEPER_VERSION=${{ inputs.gatekeeper_version }} TENANT_ID=${{ secrets.AZURE_TENANT_ID }} AZURE_SP_OBJECT_ID=${{ secrets.AZURE_SP_OBJECT_ID }} - name: Upload artifacts uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 diff --git a/.github/workflows/publish-dev-assets.yml b/.github/workflows/publish-dev-assets.yml index febca49ba..e1334406e 100644 --- a/.github/workflows/publish-dev-assets.yml +++ b/.github/workflows/publish-dev-assets.yml @@ -13,6 +13,8 @@ jobs: permissions: packages: write contents: read + id-token: write + environment: azure-publish steps: - name: Harden Runner uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 @@ -20,6 +22,21 @@ jobs: egress-policy: audit - name: Checkout uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - name: Install Notation + uses: notaryproject/notation-action/setup@104aa999103172f827373af8ac14dde7aa6d28f1 # v1.1.0 + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + - name: Az CLI login + uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Cache AAD tokens + run: | + az version + # Key Vault: + az account get-access-token --scope https://vault.azure.net/.default --output none - name: prepare id: prepare run: | @@ -100,6 +117,27 @@ jobs: run: | helm push ratify-${{ steps.prepare.outputs.semversion }}.tgz oci://${{ steps.prepare.outputs.chartrepo }} helm push ratify-${{ steps.prepare.outputs.semversionrolling }}.tgz oci://${{ steps.prepare.outputs.chartrepo }} + - name: Sign with Notation + uses: notaryproject/notation-action/sign@104aa999103172f827373af8ac14dde7aa6d28f1 # v1.1.0 + with: + plugin_name: azure-kv + plugin_url: ${{ vars.AZURE_KV_PLUGIN_URL }} + plugin_checksum: ${{ vars.AZURE_KV_CHECKSUM }} + key_id: ${{ secrets.AZURE_KV_KEY_ID }} + target_artifact_reference: |- + ${{ steps.prepare.outputs.crdref }}:${{ steps.prepare.outputs.version }} + ${{ steps.prepare.outputs.baseref }}:${{ steps.prepare.outputs.version }} + ${{ steps.prepare.outputs.ref }}:${{ steps.prepare.outputs.version }} + ${{ steps.prepare.outputs.chartrepo }}/ratify:${{ steps.prepare.outputs.semversionrolling }} + ${{ steps.prepare.outputs.chartrepo }}/ratify:${{ steps.prepare.outputs.semversion }} + signature_format: cose + - name: Sign with Cosign + run: | + cosign sign --yes ${{ steps.prepare.outputs.crdref }}:${{ steps.prepare.outputs.version }} + cosign sign --yes ${{ steps.prepare.outputs.baseref }}:${{ steps.prepare.outputs.version }} + cosign sign --yes ${{ steps.prepare.outputs.ref }}:${{ steps.prepare.outputs.version }} + cosign sign --yes ${{ steps.prepare.outputs.chartrepo }}/ratify:${{ steps.prepare.outputs.semversionrolling }} + cosign sign --yes ${{ steps.prepare.outputs.chartrepo }}/ratify:${{ steps.prepare.outputs.semversion }} - name: clear if: always() run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a299a089..c9fef6370 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,9 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=3.0.2 with: fetch-depth: 0 + + - name: Install Syft + uses: anchore/sbom-action/download-syft@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5 # v0.17.0 - name: Set up Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 @@ -31,10 +34,11 @@ jobs: go-version: '1.22' - name: Goreleaser + id: goreleaser uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 with: - version: '1.18.0' - args: release --rm-dist + version: '2.0.1' + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/run-full-validation.yml b/.github/workflows/run-full-validation.yml index 945dd0798..017944f75 100644 --- a/.github/workflows/run-full-validation.yml +++ b/.github/workflows/run-full-validation.yml @@ -48,10 +48,6 @@ jobs: secrets: inherit aks-test-cleanup: - env: - AZURE_SUBSCRIPTION_ID: daae1e1a-63dc-454f-825d-b39289070f79 - AZURE_CLIENT_ID: 814e6e97-120c-4534-b8a9-f1645bc99500 - AZURE_TENANT_ID: 72f988bf-86f1-41af-91ab-2d7cd011db47 needs: ['build_test_aks_e2e'] runs-on: ubuntu-latest permissions: @@ -74,10 +70,10 @@ jobs: - name: Az CLI login uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1 with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: clean up run: | - make e2e-cleanup AZURE_SUBSCRIPTION_ID=${{ env.AZURE_SUBSCRIPTION_ID }} \ No newline at end of file + make e2e-cleanup AZURE_SUBSCRIPTION_ID=${{ secrets.AZURE_SUBSCRIPTION_ID }} \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index 4d7daf6c7..73aead7f3 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,4 +1,5 @@ # Check the documentation at https://goreleaser.com for more options +version: 2 before: hooks: - go mod tidy @@ -57,15 +58,15 @@ release: prerelease: auto draft: true archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - format_overrides: + - format_overrides: - goos: windows format: zip checksum: name_template: 'checksums.txt' +sboms: + - artifacts: archive + - id: source + artifacts: source snapshot: name_template: '{{ incpatch .Version }}-next' changelog: diff --git a/.well-known/pki-validation/ratify-verification.crt b/.well-known/pki-validation/ratify-verification.crt new file mode 100644 index 000000000..065c274bf --- /dev/null +++ b/.well-known/pki-validation/ratify-verification.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQbZCNsoQpQC+/hsEf/FdjDjANBgkqhkiG9w0BAQsFADBa +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxFzAV +BgNVBAoTDnJhdGlmeS1wcm9qZWN0MRMwEQYDVQQDEwpyYXRpZnkuZGV2MB4XDTI0 +MDYxMDIyNDQ1OVoXDTI1MDYxMDIyNTQ1OVowWjELMAkGA1UEBhMCVVMxCzAJBgNV +BAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMRcwFQYDVQQKEw5yYXRpZnktcHJvamVj +dDETMBEGA1UEAxMKcmF0aWZ5LmRldjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAM6wAPQhVoIA7qbckyk8qYYiTnKjlUXaThXn8RYe6+dYQjBypxciAyyJ +NLbM675gXTc26rZDm29ldsych6G6VSP0wkziqZHIYKTSZ/kSzw0mm/KLRgHQPKsj +PdhVYnLbg4keyKvSRBDmKl6I04VMNXERezJWpcK0F+6wtt6BKZOJRSPCbTPhHIVY +U5iwqwMt9lOzNgWKsFUfTW+eZ2sPOJzpu78h/0JaOGgsms80Btm5UBjeJCWAOQDS +VzurUk8Q5EX5X4faNbUiKLREY4210ELVZq8xU1yixkzO2reh9Sw6maG2YLW3W0rc +bn1PTxz5ddvTF1r/J+A9JSQUhVCM7sEfh9oXW6az4OeZVCD1beG1mX5mtE/kzZqN +9KfzvBSwdFx4D4t0KBG4/f2/k2pwZuTy3qqA+R9cd434jJpdY4/dQi0JTAG7XmuG +HMpEM/1TQmXV3Nccwbyy5W2D07/y4fDVSxh2czumK8C7hWs0EX3CQ0C4mi0o3GjD +1meir2GYKDqQUyVQx0gcDgOVDQRsyA2zQ3gUy7/7RGDbtaGN0XVT0kBMU66aS6Mj +z69YEu+Yj3QNqVQL/Ms4A2G+mPXoqVTikQxbDI5Bi3d5U0Xu6zQ7/Wx+li1pO2TV +3tNSyy5Tj/IT5Mou5e5oDt81yIfrXtb6Mye5zJ6uiqq5RwK9+b2BAgMBAAGjcjBw +MA4GA1UdDwEB/wQEAwIHgDAJBgNVHRMEAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMD +MB8GA1UdIwQYMBaAFO4kXbjSuXzopPxrB1tKZ5g4oZQ/MB0GA1UdDgQWBBTuJF24 +0rl86KT8awdbSmeYOKGUPzANBgkqhkiG9w0BAQsFAAOCAgEASs8iFwuuh1lB1eM7 +cDM2rZfExb0RFXkcHWdF6+dEwuchn9rjKKXFLXU4k5sARKj+rTzxKGKNbymCLUto +DzaJwwWygZu7HoG5DHGcj5StlBXNELMCoRwFW/MjRTv4Sfkiakw03PUBeiZeg8Xb +SB3WuZreD0c/DPiONuXGSdx5cPgf0NkMTY8hiOoENP9eBCqcMhCUGMYRf1sIXr4m +YWuS5KTB93DsWLjdw+X9FDi0irFa1uP01IM+iSsPtoGoT1tV8fnYl5anvBCLsh8U +Y9vAXwJvZM6XlfA5XX4fzq82KuLASMBktyhXrYWeGBUOYOpQa4M3c/KiJyYjtm08 +pFRBdbzUwK1GF2mDfO/8oyyqPMhdkD3dO6/iToOklSLcNS4AVoTBE+S62QHHluBD +zlmeVabCB/TGLRm5wlKKm7YRI7DJXF0HVQ5KS8vXjbT1MzN7WB+Iey2lelH3U+CR +V50lbGJhvs8WiSojzcl1xJAvhRaWXZ/h8TCBI8srQzBlZ+/8o2zhs4nlEFrgurzq +Vd+m0s1lM1iGHrE//AxlU7NVh2yCjrhPumQ53Sv53LG3Kl5RD86Pvhd01ORvKKNR +zS27F63BGy6basxXtHR8ibCPNs1gbT24YLfaPiXmhy0j8dwPhEFGwQEIL8peSG56 +6OcrwcZ4J28hTXOl4EIJ2Prp9mQ= +-----END CERTIFICATE----- diff --git a/cmd/ratify/cmd/cmd_test.go b/cmd/ratify/cmd/cmd_test.go new file mode 100644 index 000000000..49149dcf5 --- /dev/null +++ b/cmd/ratify/cmd/cmd_test.go @@ -0,0 +1,83 @@ +/* +Copyright The Ratify Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "strings" + "testing" +) + +const ( + configFilePath = "../../../config/config.json" + subject = "localhost:5000/net-monitor:v1" + storeName = "oras" + digest = "sha256:17490f904cf278d4314a1ccba407fc8fd00fb45303589b8cc7f5174ac35554f4" +) + +func TestVerify(t *testing.T) { + err := verify((verifyCmdOptions{ + subject: subject, + artifactTypes: []string{""}, + configFilePath: configFilePath, + })) + + // TODO: make ratify cli more unit testable + // unit test should not have dependency for real image + if !strings.Contains(err.Error(), "plugin not found") { + t.Errorf("error expected") + } +} + +func TestDiscover(t *testing.T) { + err := discover((discoverCmdOptions{ + subject: subject, + artifactTypes: []string{""}, + configFilePath: configFilePath, + })) + + // TODO: make ratify cli more unit testable + // unit test should not need to resolve real image + if !strings.Contains(err.Error(), "referrer store failure") { + t.Errorf("error expected") + } +} + +func TestShowRefManifest(t *testing.T) { + err := showRefManifest((referrerCmdOptions{ + subject: subject, + configFilePath: configFilePath, + storeName: storeName, + digest: digest, + })) + + // TODO: make ratify cli more unit testable + // unit test should not need to resolve real image + if !strings.Contains(err.Error(), "failed to resolve subject descriptor") { + t.Errorf("error expected") + } + + // validate show blob returns error + err = showBlob((referrerCmdOptions{ + subject: subject, + configFilePath: configFilePath, + storeName: storeName, + digest: "invalid-digest", + })) + + if !strings.Contains(err.Error(), "the digest of the subject is invalid") { + t.Errorf("error expected") + } +} diff --git a/cmd/ratify/cmd/discover.go b/cmd/ratify/cmd/discover.go index 66d9d0921..858f12b52 100644 --- a/cmd/ratify/cmd/discover.go +++ b/cmd/ratify/cmd/discover.go @@ -96,10 +96,6 @@ func discover(opts discoverCmdOptions) error { return err } - if subRef.Digest == "" { - fmt.Println(taggedReferenceWarning) - } - cf, err := config.Load(opts.configFilePath) if err != nil { return err @@ -109,6 +105,10 @@ func discover(opts discoverCmdOptions) error { return err } + if subRef.Digest == "" { + logger.GetLogger(context.Background(), logOpt).Warn(taggedReferenceWarning) + } + rootImage := treeprint.NewWithRoot(subRef.String()) stores, err := sf.CreateStoresFromConfig(cf.StoresConfig, config.GetDefaultPluginPath()) diff --git a/cmd/ratify/cmd/referrer.go b/cmd/ratify/cmd/referrer.go index 67ebe2d9a..8921c5ede 100644 --- a/cmd/ratify/cmd/referrer.go +++ b/cmd/ratify/cmd/referrer.go @@ -97,7 +97,7 @@ func NewCmdShowRefManifest(argv ...string) *cobra.Command { cmd := &cobra.Command{ Use: "show-manifest [OPTIONS]", - Short: "show rference manifest at a digest", + Short: "show reference manifest at a digest", Example: eg, Args: cobra.NoArgs, RunE: func(_ *cobra.Command, _ []string) error { @@ -184,10 +184,6 @@ func showRefManifest(opts referrerCmdOptions) error { return err } - if subRef.Digest == "" { - fmt.Println(taggedReferenceWarning) - } - digest, err := utils.ParseDigest(opts.digest) if err != nil { return err @@ -198,6 +194,10 @@ func showRefManifest(opts referrerCmdOptions) error { return err } + if subRef.Digest == "" { + logger.GetLogger(context.Background(), logOpt).Warn(taggedReferenceWarning) + } + stores, err := sf.CreateStoresFromConfig(cf.StoresConfig, config.GetDefaultPluginPath()) if err != nil { diff --git a/cmd/ratify/cmd/verify.go b/cmd/ratify/cmd/verify.go index 1e745e9c6..c189fa68b 100644 --- a/cmd/ratify/cmd/verify.go +++ b/cmd/ratify/cmd/verify.go @@ -18,7 +18,6 @@ package cmd import ( "context" "errors" - "fmt" "github.com/ratify-project/ratify/config" "github.com/ratify-project/ratify/internal/constants" @@ -36,6 +35,10 @@ const ( verifyUse = "verify" ) +var logOpt = logger.Option{ + ComponentType: logger.CommandLine, +} + type verifyCmdOptions struct { configFilePath string subject string @@ -65,12 +68,6 @@ func NewCmdVerify(_ ...string) *cobra.Command { return cmd } -func TestVerify(subject string) { - _ = verify((verifyCmdOptions{ - subject: subject, - })) -} - func verify(opts verifyCmdOptions) error { if opts.subject == "" { return errors.New("subject parameter is required") @@ -81,10 +78,6 @@ func verify(opts verifyCmdOptions) error { return err } - if subRef.Digest == "" { - fmt.Println(taggedReferenceWarning) - } - cf, err := config.Load(opts.configFilePath) if err != nil { return err @@ -94,6 +87,10 @@ func verify(opts verifyCmdOptions) error { return err } + if subRef.Digest == "" { + logger.GetLogger(context.Background(), logOpt).Warn(taggedReferenceWarning) + } + stores, err := sf.CreateStoresFromConfig(cf.StoresConfig, config.GetDefaultPluginPath()) if err != nil { diff --git a/docs/proposals/Release-Supply-Chain-Metadata.md b/docs/proposals/Release-Supply-Chain-Metadata.md new file mode 100644 index 000000000..e50806de1 --- /dev/null +++ b/docs/proposals/Release-Supply-Chain-Metadata.md @@ -0,0 +1,192 @@ +# Supply Chain Metadata for Ratify Assets + +Ratify currently publishes multiple forms of release assets both for production and development uses. Currently, these assets are not published with accompanying supply chain metadata including signatures, SBOMs, and provenance. Shipping each of these forms of metadata with all binaries and container images produced by Ratify will provide consumers a verifiable way to guarantee integrity of Ratify assets. Furthermore, this will improve Ratify's OSSF scorecard. In general, Ratify will prioritize addressing HIGH, MEDIUM risks surfaced by OSSF scorecard. Learn more about the OSSF checks performed [here](https://github.com/ossf/scorecard/blob/main/docs/checks.md) + +## What does Ratify currently publish? + +Ratify publishes two types: release and development. Release assets accompany official Ratify Github releases. Development assets are published weekly (or adhoc as needed). + +Each publish type includes the following group of assets: + +* CRD container image to `ghcr.io/ratify-project` +* Base container image to `ghcr.io/ratify-project` +* Base + plugins container image to `ghcr.io/ratify-project` +* Ratify binaries as a single bundle per OS/arch which includes: + * ratify binary + * sbom-verifier plugin binary + * vulernability-report verifier plugin binary +* `checksums.txt` which contains list of checksum for each binary bundle +* Helm chart + * Release ONLY: published packaged helm chart to `ratify-project.github.io` + * Dev ONLY: published packaged helm chart to `ghcr.io/ratify-project` +* Source code (zip + tar ball) + +## Signatures + +Ratify should support signing all container images and binaries with both Sigstore Cosign and Notary Project. + +### Notary Project Image Signatures + +Ratify can utilize Notary Project's install and sign github actions published. +Notation requires a KMS to store the signing certificate. There is no support for Github Encrypted Secrets for storing the certificate. + +1. Configure Notation action to sign using certificate's associated key stored in AKV. +2. Sign CRD, base, plugin images + +#### Certificate Lifecycle Management + +Ratify will use a self-signed certificate stored in Azure Key Vault. This certificate is valid for 1 year. A new version is auto-generated after 9.5 months. +The notation signing action will be configured to always use the latest certificate version. + +The verification certificate will be published in 2 different directories: + +* Ratify website @ `ratify.dev/.well-known/pki-validation/...` +* Root of Ratify github repository @ `https://github.com/ratify-project/ratify/main/.well-known/pki-validation/...` + +The verification steps in the security section of Ratify website will recommend downloading certificates from the ratify website. + +The latest certificate can always be found at `ratify.dev/.well-known/pki-validation/ratify-verification.crt`. When a new version of the certificate is generated, the `./ratify-verification.crt` content MUST be updated to contain the new certificate. The previous version will be preserved as a separate file following convention: `./ratify-verification_.crt` where `` is the last date where that particular certificate version was valid. Ratify will NOT re-sign any dev release assets so older versions of certificates must be published so users can continue to validate those. + +NOTE: certificate update operations will be a MANUAL process and maintainers must track certificate regeneration date and make updates accordingly as specified by convention above. + +### Cosign Image Signatures + +Ratify can utilize Cosign's installer github action to install cosign on the runner. There is no accompanying sign action so the CLI must be directly used. Cosign will be used to generate keyless signatures. Signature will be uploaded to the Sigstore public-good transparency log. Cosign will use Ratify's workflow OIDC identity for signing. This will guarantee that the trusted identity is the Ratify workflow that published the images. + +1. Sign CRD, base, plugin images with cosign keyless + +### Sign binaries + +Notary Project is about to release support for blob signing which allows for all binaries to be signed using the same signing certificate used for container image signing. The public certificate can be appended to the release assets. + +Cosign has support for binary signing and Ratify can leverage the built-in GoReleaser support to achieve this. The keyless support will automatically append the `.sig` files as well as `.pem` to use for verification to the release. + +## SBOM + +There are multiple tools for generating SBOMS such as Syft, Trivy, Microsoft SBOM tool, etc. + +### Docker Buildx Attestations + +Recently, Ratify added support for generating SBOM in-toto attestations via buildx. Buildx supports generating SBOM via Syft and attaching the attestation as an OCI Image part of the top level image index. Read more [here](https://ratify.dev/docs/next/troubleshoot/security#build-attestations). + +Other OSS projects such as Gatekeeper publish attestations in a similar fashion. It's very simple to add such support making adoption more widespread. + +These attestations produced via buildx adhere to a docker-specific standard for discoverability and verification. + +Here's an example image index with an SBOM attestation: + +```bash +$ docker buildx imagetools inspect ghcr.io/ratify-project/ratify:v1.3.0 +Name: ghcr.io/ratify-project/ratify:latest +MediaType: application/vnd.oci.image.index.v1+json +Digest: sha256:f261f5076b8a1fd3f53cfbd10f647899d5875e4fcd40b1854598a18f580b422d + +Manifests: + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:c99c9b5edfe005e0454c4160388a70520844d1856c1fcc3f8557532d6a034f32 + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: linux/amd64 + + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:f1b520af44d5e22b9b8702cbbcd651092df8672ed7822851266b17947c2a0962 + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: linux/arm64 + + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:6105d973c1c672379abfdb63486a0327d612c4fe67bb62e4d20cb910c0008aa9 + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: linux/arm/v7 + + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:836450813252daf7854b0aec1ccafe486bbb1352ec234b9adf105ddc24b0cb37 + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: unknown/unknown + Annotations: + vnd.docker.reference.digest: sha256:c99c9b5edfe005e0454c4160388a70520844d1856c1fcc3f8557532d6a034f32 + vnd.docker.reference.type: attestation-manifest + + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:dcfa5faf20c916c9a41dd4636939594d8164f467ebb00d73570ae13cbcbf59ad + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: unknown/unknown + Annotations: + vnd.docker.reference.digest: sha256:f1b520af44d5e22b9b8702cbbcd651092df8672ed7822851266b17947c2a0962 + vnd.docker.reference.type: attestation-manifest + + Name: ghcr.io/ratify-project/ratify:v1.3.0@sha256:c936d0ed115975ee7fc8196fbc5baff8100e92bff3d401c60df6396b9451e773 + MediaType: application/vnd.oci.image.manifest.v1+json + Platform: unknown/unknown + Annotations: + vnd.docker.reference.type: attestation-manifest + vnd.docker.reference.digest: sha256:6105d973c1c672379abfdb63486a0327d612c4fe67bb62e4d20cb910c0008aa9 +``` + +You can see the `attestation-manifest` reference-type. + +To inspect, it's recommended to use docker's image inspection tool: + +```shell +docker buildx imagetools inspect ghcr.io/ratify-project/ratify:v1.3.0 --format '{{ json .SBOM }}' +``` + +### Referrer Artifacts + +Ratify should support generating SBOMs via an OSS tool and attach to the published container images via ORAS. This would be in line with the Ratify verification capabilities supported by the project. + +### SBOM Binary Artifacts + +Ratify should support publishing an SBOM in a common format like SPDX as a release artifact published next to each OS/arch specific binary. + +GoRelease already support automatic SBOM generation and Ratify should update GoReleaser to take advantage of this support. Example [here](https://github.com/goreleaser/goreleaser-example-supply-chain/blob/d6d60f6320dbe97bda24b6351d9afa2035b3a23a/.goreleaser.yaml#L48). + +Ratify should also sign the SBOM using both notation and cosign. + +## Provenance + +### Docker Buildx Attestation + +Please refer to SBOM section for format [details](#docker-buildx-attestations). + +Ratify also added SLSA provenance attestation generation support as part of docker buildx. Similar to SBOM support, this adds a SLSA provenance attestation to the image index during image build. + +### Referrer Artifacts (TBD) + +As a future improvement, Ratify can look into attaching the SLSA build provenance metadata as a referrer artifact attached to the image. This might be in the form of a standalone artifact or packaged in an in-toto attestation. + +### Provenance Binary Artifacts + +Ratify should support publishing an provenance file for each OS/arch binary. The OSSF scorecard also recommends (and looks for) a provenance file in the release. + +This would involve adding an extra workflow to generate the SLSA provenance for each output binary after GoReleaser has finished. Example [here](https://github.com/slsa-framework/slsa-github-generator/blob/main/.github/workflows/generator_generic_slsa3.yml). + +Ratify should also sign each Provenance release file with notation and cosign. + +## Proposed Dev Implementation + +### Questions + +1. Is it ok to use a self-signed certificate for Ratify's signing purposes? Yes, we are ok with this. +2. How do we handle certificate revocation scenarios? Is it Ratify's responsibility to resign all the release and dev images? Ratify will follow the supportability promise and only resign the **latest** minor release assets. +3. For binary signing, should Ratify only sign the `checksums.txt` or should Ratify sign all the assets individually? All assets should be signed. +4. Do we need to publish the same artifacts as referrers as well or is it sufficient to use docker buildx attestations? Ratify will consider this in the future as need arises. Right now, other OSS projects, like OPA Gatekeeper. have adopted buildx attestations. + +### Stage 1 + +* Generate SBOM buildx attestations and publish for release and dev images +* Generate SLSA provenance buildx attestation and publish for release and dev images +* Add verification guidance to Ratify website under `Security` section + +### Stage 2 + +* Container image signing using Notation for dev images and chart + * Generate and store self signed certificate in Azure Key vault + * AKV is in same subscription used for azure e2e tests + * Publish verification public cert in the root of the Ratify repo +* Container image signing using Cosign for dev images + * Keyless support only + * Publish to rekor public good transparency log +* Signing support only introduced for dev images to test if workflow and approach is what Ratify should adopt long term +* Add SBOM generation to GoReleaser +* Add verification guidance to Ratify website under `Security` section + +### Future (TBD) + +* Attach SBOMs to release and dev images using Syft (not as in-toto attestations) +* Add Provenance generation after GoReleaser +* Introduce binary signing using Notation and Cosign +* Attach SLSA provenance to release and dev images as referrers diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 6721cd65e..ff050cef3 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -61,6 +61,8 @@ const ( Executor componentType = "executor" // Server is the component type for the Ratify http server. Server componentType = "server" + // CommandLine is the component type for the Ratify command line. + CommandLine componentType = "commandLine" // ReferrerStore is the component type for the referrer store. ReferrerStore componentType = "referrerStore" // Cache is the component type for the cache.