From 2c971c1aa68ae4200a3de6c12168287579b5e470 Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Mon, 15 Jan 2024 10:04:20 -0600 Subject: [PATCH 1/4] (feat/cli): add input for --provenance-repository while image verification Signed-off-by: saisatishkarra --- README.md | 22 +++++++++++++ cli/slsa-verifier/verify.go | 5 ++- cli/slsa-verifier/verify/options.go | 42 ++++++++++++++++++++++++ cli/slsa-verifier/verify/verify_image.go | 24 +++++++++----- register/register.go | 4 +-- verifiers/internal/gcb/verifier.go | 4 +-- verifiers/internal/gha/verifier.go | 22 +++++++++---- verifiers/verifier.go | 3 +- 8 files changed, 105 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1d654341a..aea423fd0 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,28 @@ The only requirement is that the provenance file covers all artifacts passed as To verify a container image, you need to pass a container image name that is _immutable_ by providing its digest, in order to avoid [TOCTOU attacks](#toctou-attacks). +#### The verify-image command + +```bash +$ slsa-verifier verify-image --help +Verifies SLSA provenance for an image + +Usage: + slsa-verifier verify-image [flags] tarball + +Flags: + --build-workflow-input map[] [optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events on GitHub Actions). (default map[]) + --builder-id string [optional] the unique builder ID who created the provenance + -h, --help help for verify-npm-package + --print-provenance [optional] print the verified provenance to stdout + --provenance-path string path to a provenance file + --provenance-repository string [optional] provenance repository when stored different from image repository. When set, overrides COSIGN_REPOSITORY environment variable + --source-branch string [optional] expected branch the binary was compiled from + --source-tag string [optional] expected tag the binary was compiled from + --source-uri string expected source repository that should have produced the binary, e.g. github.com/some/repo + --source-versioned-tag string [optional] expected version the binary was compiled from. Uses semantic version to match the tag +``` + First set the image name: ```shell diff --git a/cli/slsa-verifier/verify.go b/cli/slsa-verifier/verify.go index 7d5a95be5..97ce36e70 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -76,7 +76,7 @@ func verifyArtifactCmd() *cobra.Command { } func verifyImageCmd() *cobra.Command { - o := &verify.VerifyOptions{} + o := &verify.VerifyImageOptions{} cmd := &cobra.Command{ Use: "verify-image [flags] image", @@ -96,6 +96,9 @@ func verifyImageCmd() *cobra.Command { if cmd.Flags().Changed("provenance-path") { v.ProvenancePath = &o.ProvenancePath } + if cmd.Flags().Changed("provenance-repository") { + v.ProvenanceRepository = &o.ProvenanceRepository + } if cmd.Flags().Changed("source-branch") { v.SourceBranch = &o.SourceBranch } diff --git a/cli/slsa-verifier/verify/options.go b/cli/slsa-verifier/verify/options.go index c21e3fcc4..4e217701b 100644 --- a/cli/slsa-verifier/verify/options.go +++ b/cli/slsa-verifier/verify/options.go @@ -74,6 +74,48 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) { cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag") } +// VerifyImageOptions is the top-level options for the `verifyImage` command + +type VerifyImageOptions struct { + VerifyOptions + /* Other */ + ProvenanceRepository string +} + +var _ Interface = (*VerifyImageOptions)(nil) + +// AddFlags implements Interface. +func (o *VerifyImageOptions) AddFlags(cmd *cobra.Command) { + cmd.Flags().Var(&o.BuildWorkflowInputs, "build-workflow-input", + "[optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events on GitHub Actions).") + + cmd.Flags().StringVar(&o.BuilderID, "builder-id", "", "[optional] the unique builder ID who created the provenance") + + /* Source options */ + cmd.Flags().StringVar(&o.SourceURI, "source-uri", "", + "expected source repository that should have produced the binary, e.g. github.com/some/repo") + + cmd.Flags().StringVar(&o.SourceBranch, "source-branch", "", "[optional] expected branch the binary was compiled from") + + cmd.Flags().StringVar(&o.SourceTag, "source-tag", "", "[optional] expected tag the binary was compiled from") + + cmd.Flags().StringVar(&o.SourceVersionTag, "source-versioned-tag", "", + "[optional] expected version the binary was compiled from. Uses semantic version to match the tag") + + /* Other options */ + cmd.Flags().StringVar(&o.ProvenancePath, "provenance-path", "", + "path to a provenance file") + + cmd.Flags().StringVar(&o.ProvenanceRepository, "provenance-repository", "", + "image repository for provenance with format: /. When set, overrides COSIGN_REPOSITORY environment variable") + + cmd.Flags().BoolVar(&o.PrintProvenance, "print-provenance", false, + "[optional] print the verified provenance to stdout") + + cmd.MarkFlagRequired("source-uri") + cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag") +} + // VerifyNpmOptions is the top-level options for the `verifyNpmPackage` command. type VerifyNpmOptions struct { VerifyOptions diff --git a/cli/slsa-verifier/verify/verify_image.go b/cli/slsa-verifier/verify/verify_image.go index 9bf8c50e3..8785edeb0 100644 --- a/cli/slsa-verifier/verify/verify_image.go +++ b/cli/slsa-verifier/verify/verify_image.go @@ -30,14 +30,15 @@ type ComputeDigestFn func(string) (string, error) // Note: nil branch, tag, version-tag and builder-id means we ignore them during verification. type VerifyImageCommand struct { // May be nil if supplied alongside in the registry - ProvenancePath *string - BuilderID *string - SourceURI string - SourceBranch *string - SourceTag *string - SourceVersionTag *string - BuildWorkflowInputs map[string]string - PrintProvenance bool + ProvenancePath *string + ProvenanceRepository *string + BuilderID *string + SourceURI string + SourceBranch *string + SourceTag *string + SourceVersionTag *string + BuildWorkflowInputs map[string]string + PrintProvenance bool } func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*utils.TrustedBuilderID, error) { @@ -70,7 +71,12 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti } } - verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) + var provenanceRepository string + if c.ProvenanceRepository != nil { + provenanceRepository = *c.ProvenanceRepository + } + + verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceRepository, provenanceOpts, builderOpts) if err != nil { return nil, err } diff --git a/register/register.go b/register/register.go index 787bb1f3c..adbd8599f 100644 --- a/register/register.go +++ b/register/register.go @@ -24,8 +24,8 @@ type SLSAVerifier interface { // VerifyImage verifies a provenance for a supplied OCI image. VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, - provenanceOpts *options.ProvenanceOpts, + provenance []byte, provenanceRepository string, + artifactImage string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) diff --git a/verifiers/internal/gcb/verifier.go b/verifiers/internal/gcb/verifier.go index 511065009..abff5cea0 100644 --- a/verifiers/internal/gcb/verifier.go +++ b/verifiers/internal/gcb/verifier.go @@ -50,8 +50,8 @@ func (v *GCBVerifier) VerifyNpmPackage(ctx context.Context, // VerifyImage verifies provenance for an OCI image. func (v *GCBVerifier) VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, - provenanceOpts *options.ProvenanceOpts, + provenance []byte, provenanceRepository string, + artifactImage string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { prov, err := ProvenanceFromBytes(provenance) diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index 62b0cb1cf..ed66d9809 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -9,6 +9,7 @@ import ( "os" "strings" + "github.com/google/go-containerregistry/pkg/name" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/rekor/pkg/client" @@ -245,8 +246,8 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, // VerifyImage verifies provenance for an OCI image. func (v *GHAVerifier) VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, - provenanceOpts *options.ProvenanceOpts, + provenance []byte, provenanceRepository string, + artifactImage string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ @@ -255,10 +256,19 @@ func (v *GHAVerifier) VerifyImage(ctx context.Context, return nil, nil, err } - // Parse any provenance target repository set using environment variable COSIGN_REPOSITORY - provenanceTargetRepository, err := ociremote.GetEnvTargetRepository() - if err != nil { - return nil, nil, err + var provenanceTargetRepository name.Repository + // Consume input for --provenance-repository when set + if provenanceRepository != "" { + provenanceTargetRepository, err = name.NewRepository(provenanceRepository) + if err != nil { + return nil, nil, err + } + } else { + // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment + provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() + if err != nil { + return nil, nil, err + } } registryClientOpts := []ociremote.Option{} diff --git a/verifiers/verifier.go b/verifiers/verifier.go index 745523d09..2256a5630 100644 --- a/verifiers/verifier.go +++ b/verifiers/verifier.go @@ -37,6 +37,7 @@ func getVerifier(builderOpts *options.BuilderOpts) (register.SLSAVerifier, error func VerifyImage(ctx context.Context, artifactImage string, provenance []byte, + provenanceRepository string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { @@ -44,7 +45,7 @@ func VerifyImage(ctx context.Context, artifactImage string, if err != nil { return nil, nil, err } - return verifier.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts) + return verifier.VerifyImage(ctx, provenance, provenanceRepository, artifactImage, provenanceOpts, builderOpts) } func VerifyArtifact(ctx context.Context, From c475cc5cf3aea26133e8881154e9e4a252b57ae3 Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Fri, 19 Jan 2024 12:28:44 -0600 Subject: [PATCH 2/4] add VerifyImageProvenanceRepo for backward api compatibility Signed-off-by: saisatishkarra --- cli/slsa-verifier/verify/options.go | 26 +----- cli/slsa-verifier/verify/verify_image.go | 9 +- register/register.go | 6 ++ verifiers/internal/gcb/verifier.go | 12 ++- verifiers/internal/gha/verifier.go | 109 ++++++++++++++--------- verifiers/verifier.go | 14 ++- 6 files changed, 103 insertions(+), 73 deletions(-) diff --git a/cli/slsa-verifier/verify/options.go b/cli/slsa-verifier/verify/options.go index 4e217701b..fec526979 100644 --- a/cli/slsa-verifier/verify/options.go +++ b/cli/slsa-verifier/verify/options.go @@ -86,34 +86,10 @@ var _ Interface = (*VerifyImageOptions)(nil) // AddFlags implements Interface. func (o *VerifyImageOptions) AddFlags(cmd *cobra.Command) { - cmd.Flags().Var(&o.BuildWorkflowInputs, "build-workflow-input", - "[optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events on GitHub Actions).") - - cmd.Flags().StringVar(&o.BuilderID, "builder-id", "", "[optional] the unique builder ID who created the provenance") - - /* Source options */ - cmd.Flags().StringVar(&o.SourceURI, "source-uri", "", - "expected source repository that should have produced the binary, e.g. github.com/some/repo") - - cmd.Flags().StringVar(&o.SourceBranch, "source-branch", "", "[optional] expected branch the binary was compiled from") - - cmd.Flags().StringVar(&o.SourceTag, "source-tag", "", "[optional] expected tag the binary was compiled from") - - cmd.Flags().StringVar(&o.SourceVersionTag, "source-versioned-tag", "", - "[optional] expected version the binary was compiled from. Uses semantic version to match the tag") - - /* Other options */ - cmd.Flags().StringVar(&o.ProvenancePath, "provenance-path", "", - "path to a provenance file") + o.VerifyOptions.AddFlags(cmd) cmd.Flags().StringVar(&o.ProvenanceRepository, "provenance-repository", "", "image repository for provenance with format: /. When set, overrides COSIGN_REPOSITORY environment variable") - - cmd.Flags().BoolVar(&o.PrintProvenance, "print-provenance", false, - "[optional] print the verified provenance to stdout") - - cmd.MarkFlagRequired("source-uri") - cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag") } // VerifyNpmOptions is the top-level options for the `verifyNpmPackage` command. diff --git a/cli/slsa-verifier/verify/verify_image.go b/cli/slsa-verifier/verify/verify_image.go index 8785edeb0..63434eee9 100644 --- a/cli/slsa-verifier/verify/verify_image.go +++ b/cli/slsa-verifier/verify/verify_image.go @@ -71,12 +71,15 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti } } - var provenanceRepository string + var verifiedProvenance []byte + var outBuilderID *utils.TrustedBuilderID + if c.ProvenanceRepository != nil { - provenanceRepository = *c.ProvenanceRepository + verifiedProvenance, outBuilderID, err = verifiers.VerifyImageProvenanceRepo(ctx, artifacts[0], provenance, *c.ProvenanceRepository, provenanceOpts, builderOpts) + } else { + verifiedProvenance, outBuilderID, err = verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) } - verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceRepository, provenanceOpts, builderOpts) if err != nil { return nil, err } diff --git a/register/register.go b/register/register.go index adbd8599f..bbd6b1b38 100644 --- a/register/register.go +++ b/register/register.go @@ -24,6 +24,12 @@ type SLSAVerifier interface { // VerifyImage verifies a provenance for a supplied OCI image. VerifyImage(ctx context.Context, + provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, + ) ([]byte, *utils.TrustedBuilderID, error) + + // VerifyImageProvenanceRepo verifies a provenance stored in a separate repository for a supplied OCI image. + VerifyImageProvenanceRepo(ctx context.Context, provenance []byte, provenanceRepository string, artifactImage string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, diff --git a/verifiers/internal/gcb/verifier.go b/verifiers/internal/gcb/verifier.go index abff5cea0..8c713c1b1 100644 --- a/verifiers/internal/gcb/verifier.go +++ b/verifiers/internal/gcb/verifier.go @@ -50,8 +50,7 @@ func (v *GCBVerifier) VerifyNpmPackage(ctx context.Context, // VerifyImage verifies provenance for an OCI image. func (v *GCBVerifier) VerifyImage(ctx context.Context, - provenance []byte, provenanceRepository string, - artifactImage string, provenanceOpts *options.ProvenanceOpts, + provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { prov, err := ProvenanceFromBytes(provenance) @@ -126,3 +125,12 @@ func (v *GCBVerifier) VerifyImage(ctx context.Context, } return content, builderID, nil } + +// VerifyImageProvenanceRepo verifies provenance for an OCI image. +func (v *GCBVerifier) VerifyImageProvenanceRepo(ctx context.Context, + provenance []byte, provenanceRepository string, + artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, +) ([]byte, *utils.TrustedBuilderID, error) { + return v.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts) +} diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index ed66d9809..18261ba94 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -244,48 +244,9 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, utils.MergeMaps(defaultArtifactTrustedReusableWorkflows, defaultBYOBReusableWorkflows)) } -// VerifyImage verifies provenance for an OCI image. -func (v *GHAVerifier) VerifyImage(ctx context.Context, - provenance []byte, provenanceRepository string, - artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, -) ([]byte, *utils.TrustedBuilderID, error) { - /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ - trustedRoot, err := TrustedRootSingleton(ctx) - if err != nil { - return nil, nil, err - } - - var provenanceTargetRepository name.Repository - // Consume input for --provenance-repository when set - if provenanceRepository != "" { - provenanceTargetRepository, err = name.NewRepository(provenanceRepository) - if err != nil { - return nil, nil, err - } - } else { - // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment - provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() - if err != nil { - return nil, nil, err - } - } - - registryClientOpts := []ociremote.Option{} - - // Append target repository to OCI Registry opts - // Must be authenticated against the specified target repository externally - if provenanceTargetRepository.Name() != "" { - registryClientOpts = append(registryClientOpts, ociremote.WithTargetRepository(provenanceTargetRepository)) - } - - opts := &cosign.CheckOpts{ - RegistryClientOpts: registryClientOpts, - RootCerts: trustedRoot.FulcioRoot, - IntermediateCerts: trustedRoot.FulcioIntermediates, - RekorPubKeys: trustedRoot.RekorPubKeys, - CTLogPubKeys: trustedRoot.CTPubKeys, - } +// verifyImageWithOptions abstracts the cosign options and returns verified provenance for an artifact. +func verifyImageWithOptions(ctx context.Context, artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, opts *cosign.CheckOpts) ([]byte, *utils.TrustedBuilderID, error) { atts, _, err := container.RunCosignImageVerification(ctx, artifactImage, opts) if err != nil { @@ -332,6 +293,70 @@ func (v *GHAVerifier) VerifyImage(ctx context.Context, return nil, nil, fmt.Errorf("%w", serrors.ErrorNoValidSignature) } +// VerifyImage verifies provenance for an OCI image. +func (v *GHAVerifier) VerifyImage(ctx context.Context, + provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, +) ([]byte, *utils.TrustedBuilderID, error) { + /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ + trustedRoot, err := TrustedRootSingleton(ctx) + if err != nil { + return nil, nil, err + } + opts := &cosign.CheckOpts{ + RootCerts: trustedRoot.FulcioRoot, + IntermediateCerts: trustedRoot.FulcioIntermediates, + RekorPubKeys: trustedRoot.RekorPubKeys, + CTLogPubKeys: trustedRoot.CTPubKeys, + } + return verifyImageWithOptions(ctx, artifactImage, provenanceOpts, builderOpts, opts) +} + +// VerifyImageProvenanceRepo verifies provenance from a separate store for an OCI image. +func (v *GHAVerifier) VerifyImageProvenanceRepo(ctx context.Context, + provenance []byte, provenanceRepository string, + artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, +) ([]byte, *utils.TrustedBuilderID, error) { + /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ + trustedRoot, err := TrustedRootSingleton(ctx) + if err != nil { + return nil, nil, err + } + + var provenanceTargetRepository name.Repository + // Consume input for --provenance-repository when set + if provenanceRepository != "" { + provenanceTargetRepository, err = name.NewRepository(provenanceRepository) + if err != nil { + return nil, nil, err + } + } else { + // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment + provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() + if err != nil { + return nil, nil, err + } + } + + registryClientOpts := []ociremote.Option{} + + // Append target repository to OCI Registry opts + // Must be authenticated against the specified target repository externally + if provenanceTargetRepository.Name() != "" { + registryClientOpts = append(registryClientOpts, ociremote.WithTargetRepository(provenanceTargetRepository)) + } + + opts := &cosign.CheckOpts{ + RegistryClientOpts: registryClientOpts, + RootCerts: trustedRoot.FulcioRoot, + IntermediateCerts: trustedRoot.FulcioIntermediates, + RekorPubKeys: trustedRoot.RekorPubKeys, + CTLogPubKeys: trustedRoot.CTPubKeys, + } + return verifyImageWithOptions(ctx, artifactImage, provenanceOpts, builderOpts, opts) +} + // VerifyNpmPackage verifies an npm package tarball. func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context, attestations []byte, tarballHash string, diff --git a/verifiers/verifier.go b/verifiers/verifier.go index 2256a5630..8abd162ed 100644 --- a/verifiers/verifier.go +++ b/verifiers/verifier.go @@ -36,6 +36,18 @@ func getVerifier(builderOpts *options.BuilderOpts) (register.SLSAVerifier, error } func VerifyImage(ctx context.Context, artifactImage string, + provenance []byte, + provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, +) ([]byte, *utils.TrustedBuilderID, error) { + verifier, err := getVerifier(builderOpts) + if err != nil { + return nil, nil, err + } + return verifier.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts) +} + +func VerifyImageProvenanceRepo(ctx context.Context, artifactImage string, provenance []byte, provenanceRepository string, provenanceOpts *options.ProvenanceOpts, @@ -45,7 +57,7 @@ func VerifyImage(ctx context.Context, artifactImage string, if err != nil { return nil, nil, err } - return verifier.VerifyImage(ctx, provenance, provenanceRepository, artifactImage, provenanceOpts, builderOpts) + return verifier.VerifyImageProvenanceRepo(ctx, provenance, provenanceRepository, artifactImage, provenanceOpts, builderOpts) } func VerifyArtifact(ctx context.Context, From 36bf8368ef7fd278b1fbcad0018f4edf663c7098 Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Mon, 22 Jan 2024 10:34:11 -0600 Subject: [PATCH 3/4] Avoid breaking change for provenanceRepository cli option Signed-off-by: saisatishkarra --- cli/slsa-verifier/verify/verify_image.go | 19 ++-- options/options.go | 3 + register/register.go | 10 +-- verifiers/internal/gcb/verifier.go | 12 +-- verifiers/internal/gha/verifier.go | 109 +++++++++-------------- verifiers/verifier.go | 13 --- 6 files changed, 57 insertions(+), 109 deletions(-) diff --git a/cli/slsa-verifier/verify/verify_image.go b/cli/slsa-verifier/verify/verify_image.go index 63434eee9..a1207446b 100644 --- a/cli/slsa-verifier/verify/verify_image.go +++ b/cli/slsa-verifier/verify/verify_image.go @@ -51,12 +51,13 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti } provenanceOpts := &options.ProvenanceOpts{ - ExpectedSourceURI: c.SourceURI, - ExpectedBranch: c.SourceBranch, - ExpectedDigest: digest, - ExpectedVersionedTag: c.SourceVersionTag, - ExpectedTag: c.SourceTag, - ExpectedWorkflowInputs: c.BuildWorkflowInputs, + ExpectedSourceURI: c.SourceURI, + ExpectedBranch: c.SourceBranch, + ExpectedDigest: digest, + ExpectedVersionedTag: c.SourceVersionTag, + ExpectedTag: c.SourceTag, + ExpectedProvenanceRepository: c.ProvenanceRepository, + ExpectedWorkflowInputs: c.BuildWorkflowInputs, } builderOpts := &options.BuilderOpts{ @@ -74,11 +75,7 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti var verifiedProvenance []byte var outBuilderID *utils.TrustedBuilderID - if c.ProvenanceRepository != nil { - verifiedProvenance, outBuilderID, err = verifiers.VerifyImageProvenanceRepo(ctx, artifacts[0], provenance, *c.ProvenanceRepository, provenanceOpts, builderOpts) - } else { - verifiedProvenance, outBuilderID, err = verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) - } + verifiedProvenance, outBuilderID, err = verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) if err != nil { return nil, err diff --git a/options/options.go b/options/options.go index 11ef9fb4a..c3b68442f 100644 --- a/options/options.go +++ b/options/options.go @@ -27,6 +27,9 @@ type ProvenanceOpts struct { ExpectedPackageName *string ExpectedPackageVersion *string + + // ExpectedProvenanceRepository is the provenance repository that is passed from user and not verified + ExpectedProvenanceRepository *string } // BuildOpts are the options for checking the builder. diff --git a/register/register.go b/register/register.go index bbd6b1b38..787bb1f3c 100644 --- a/register/register.go +++ b/register/register.go @@ -24,14 +24,8 @@ type SLSAVerifier interface { // VerifyImage verifies a provenance for a supplied OCI image. VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, - ) ([]byte, *utils.TrustedBuilderID, error) - - // VerifyImageProvenanceRepo verifies a provenance stored in a separate repository for a supplied OCI image. - VerifyImageProvenanceRepo(ctx context.Context, - provenance []byte, provenanceRepository string, - artifactImage string, provenanceOpts *options.ProvenanceOpts, + provenance []byte, artifactImage string, + provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) diff --git a/verifiers/internal/gcb/verifier.go b/verifiers/internal/gcb/verifier.go index 8c713c1b1..511065009 100644 --- a/verifiers/internal/gcb/verifier.go +++ b/verifiers/internal/gcb/verifier.go @@ -50,7 +50,8 @@ func (v *GCBVerifier) VerifyNpmPackage(ctx context.Context, // VerifyImage verifies provenance for an OCI image. func (v *GCBVerifier) VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, + provenance []byte, artifactImage string, + provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { prov, err := ProvenanceFromBytes(provenance) @@ -125,12 +126,3 @@ func (v *GCBVerifier) VerifyImage(ctx context.Context, } return content, builderID, nil } - -// VerifyImageProvenanceRepo verifies provenance for an OCI image. -func (v *GCBVerifier) VerifyImageProvenanceRepo(ctx context.Context, - provenance []byte, provenanceRepository string, - artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, -) ([]byte, *utils.TrustedBuilderID, error) { - return v.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts) -} diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index 18261ba94..b6d0fc7c0 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -244,9 +244,48 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, utils.MergeMaps(defaultArtifactTrustedReusableWorkflows, defaultBYOBReusableWorkflows)) } -// verifyImageWithOptions abstracts the cosign options and returns verified provenance for an artifact. -func verifyImageWithOptions(ctx context.Context, artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, opts *cosign.CheckOpts) ([]byte, *utils.TrustedBuilderID, error) { +// VerifyImage verifies provenance for an OCI image. +func (v *GHAVerifier) VerifyImage(ctx context.Context, + provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, + builderOpts *options.BuilderOpts, +) ([]byte, *utils.TrustedBuilderID, error) { + /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ + trustedRoot, err := TrustedRootSingleton(ctx) + if err != nil { + return nil, nil, err + } + + var provenanceTargetRepository name.Repository + // Consume input for --provenance-repository when set + if *provenanceOpts.ExpectedProvenanceRepository != "" { + provenanceTargetRepository, err = name.NewRepository(*provenanceOpts.ExpectedProvenanceRepository) + if err != nil { + return nil, nil, err + } + } else { + // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment + provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() + if err != nil { + return nil, nil, err + } + } + + registryClientOpts := []ociremote.Option{} + + // Append target repository to OCI Registry opts + // Must be authenticated against the specified target repository externally + if provenanceTargetRepository.Name() != "" { + registryClientOpts = append(registryClientOpts, ociremote.WithTargetRepository(provenanceTargetRepository)) + } + + opts := &cosign.CheckOpts{ + RegistryClientOpts: registryClientOpts, + RootCerts: trustedRoot.FulcioRoot, + IntermediateCerts: trustedRoot.FulcioIntermediates, + RekorPubKeys: trustedRoot.RekorPubKeys, + CTLogPubKeys: trustedRoot.CTPubKeys, + } + atts, _, err := container.RunCosignImageVerification(ctx, artifactImage, opts) if err != nil { @@ -293,70 +332,6 @@ func verifyImageWithOptions(ctx context.Context, artifactImage string, provenanc return nil, nil, fmt.Errorf("%w", serrors.ErrorNoValidSignature) } -// VerifyImage verifies provenance for an OCI image. -func (v *GHAVerifier) VerifyImage(ctx context.Context, - provenance []byte, artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, -) ([]byte, *utils.TrustedBuilderID, error) { - /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ - trustedRoot, err := TrustedRootSingleton(ctx) - if err != nil { - return nil, nil, err - } - opts := &cosign.CheckOpts{ - RootCerts: trustedRoot.FulcioRoot, - IntermediateCerts: trustedRoot.FulcioIntermediates, - RekorPubKeys: trustedRoot.RekorPubKeys, - CTLogPubKeys: trustedRoot.CTPubKeys, - } - return verifyImageWithOptions(ctx, artifactImage, provenanceOpts, builderOpts, opts) -} - -// VerifyImageProvenanceRepo verifies provenance from a separate store for an OCI image. -func (v *GHAVerifier) VerifyImageProvenanceRepo(ctx context.Context, - provenance []byte, provenanceRepository string, - artifactImage string, provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, -) ([]byte, *utils.TrustedBuilderID, error) { - /* Retrieve any valid signed attestations that chain up to Fulcio root CA. */ - trustedRoot, err := TrustedRootSingleton(ctx) - if err != nil { - return nil, nil, err - } - - var provenanceTargetRepository name.Repository - // Consume input for --provenance-repository when set - if provenanceRepository != "" { - provenanceTargetRepository, err = name.NewRepository(provenanceRepository) - if err != nil { - return nil, nil, err - } - } else { - // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment - provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() - if err != nil { - return nil, nil, err - } - } - - registryClientOpts := []ociremote.Option{} - - // Append target repository to OCI Registry opts - // Must be authenticated against the specified target repository externally - if provenanceTargetRepository.Name() != "" { - registryClientOpts = append(registryClientOpts, ociremote.WithTargetRepository(provenanceTargetRepository)) - } - - opts := &cosign.CheckOpts{ - RegistryClientOpts: registryClientOpts, - RootCerts: trustedRoot.FulcioRoot, - IntermediateCerts: trustedRoot.FulcioIntermediates, - RekorPubKeys: trustedRoot.RekorPubKeys, - CTLogPubKeys: trustedRoot.CTPubKeys, - } - return verifyImageWithOptions(ctx, artifactImage, provenanceOpts, builderOpts, opts) -} - // VerifyNpmPackage verifies an npm package tarball. func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context, attestations []byte, tarballHash string, diff --git a/verifiers/verifier.go b/verifiers/verifier.go index 8abd162ed..745523d09 100644 --- a/verifiers/verifier.go +++ b/verifiers/verifier.go @@ -47,19 +47,6 @@ func VerifyImage(ctx context.Context, artifactImage string, return verifier.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts) } -func VerifyImageProvenanceRepo(ctx context.Context, artifactImage string, - provenance []byte, - provenanceRepository string, - provenanceOpts *options.ProvenanceOpts, - builderOpts *options.BuilderOpts, -) ([]byte, *utils.TrustedBuilderID, error) { - verifier, err := getVerifier(builderOpts) - if err != nil { - return nil, nil, err - } - return verifier.VerifyImageProvenanceRepo(ctx, provenance, provenanceRepository, artifactImage, provenanceOpts, builderOpts) -} - func VerifyArtifact(ctx context.Context, provenance []byte, artifactHash string, provenanceOpts *options.ProvenanceOpts, From 93ddb5d5cc21f6a7c19e809bf522f049082da89c Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Mon, 22 Jan 2024 11:41:30 -0600 Subject: [PATCH 4/4] deprecate COSIGN_REPOSITORY in favor of provenanceRepository Signed-off-by: saisatishkarra --- cli/slsa-verifier/verify.go | 2 +- cli/slsa-verifier/verify/options.go | 26 ++++++------------------ cli/slsa-verifier/verify/verify_image.go | 5 +---- verifiers/internal/gha/verifier.go | 8 +------- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/cli/slsa-verifier/verify.go b/cli/slsa-verifier/verify.go index 97ce36e70..fcaaaf870 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -76,7 +76,7 @@ func verifyArtifactCmd() *cobra.Command { } func verifyImageCmd() *cobra.Command { - o := &verify.VerifyImageOptions{} + o := &verify.VerifyOptions{} cmd := &cobra.Command{ Use: "verify-image [flags] image", diff --git a/cli/slsa-verifier/verify/options.go b/cli/slsa-verifier/verify/options.go index fec526979..e9079ea44 100644 --- a/cli/slsa-verifier/verify/options.go +++ b/cli/slsa-verifier/verify/options.go @@ -38,8 +38,9 @@ type VerifyOptions struct { BuildWorkflowInputs workflowInputs BuilderID string /* Other */ - ProvenancePath string - PrintProvenance bool + ProvenancePath string + ProvenanceRepository string + PrintProvenance bool } var _ Interface = (*VerifyOptions)(nil) @@ -67,6 +68,9 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.ProvenancePath, "provenance-path", "", "path to a provenance file") + cmd.Flags().StringVar(&o.ProvenanceRepository, "provenance-repository", "", + "image repository for provenance with format: /") + cmd.Flags().BoolVar(&o.PrintProvenance, "print-provenance", false, "[optional] print the verified provenance to stdout") @@ -74,24 +78,6 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) { cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag") } -// VerifyImageOptions is the top-level options for the `verifyImage` command - -type VerifyImageOptions struct { - VerifyOptions - /* Other */ - ProvenanceRepository string -} - -var _ Interface = (*VerifyImageOptions)(nil) - -// AddFlags implements Interface. -func (o *VerifyImageOptions) AddFlags(cmd *cobra.Command) { - o.VerifyOptions.AddFlags(cmd) - - cmd.Flags().StringVar(&o.ProvenanceRepository, "provenance-repository", "", - "image repository for provenance with format: /. When set, overrides COSIGN_REPOSITORY environment variable") -} - // VerifyNpmOptions is the top-level options for the `verifyNpmPackage` command. type VerifyNpmOptions struct { VerifyOptions diff --git a/cli/slsa-verifier/verify/verify_image.go b/cli/slsa-verifier/verify/verify_image.go index a1207446b..3dd5650a9 100644 --- a/cli/slsa-verifier/verify/verify_image.go +++ b/cli/slsa-verifier/verify/verify_image.go @@ -72,10 +72,7 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti } } - var verifiedProvenance []byte - var outBuilderID *utils.TrustedBuilderID - - verifiedProvenance, outBuilderID, err = verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) + verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts) if err != nil { return nil, err diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index b6d0fc7c0..68dd86f08 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -257,17 +257,11 @@ func (v *GHAVerifier) VerifyImage(ctx context.Context, var provenanceTargetRepository name.Repository // Consume input for --provenance-repository when set - if *provenanceOpts.ExpectedProvenanceRepository != "" { + if provenanceOpts.ExpectedProvenanceRepository != nil { provenanceTargetRepository, err = name.NewRepository(*provenanceOpts.ExpectedProvenanceRepository) if err != nil { return nil, nil, err } - } else { - // If user input --provenance-repository is empty, look for COSIGN_REPOSITORY environment - provenanceTargetRepository, err = ociremote.GetEnvTargetRepository() - if err != nil { - return nil, nil, err - } } registryClientOpts := []ociremote.Option{}