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..fcaaaf870 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -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..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") diff --git a/cli/slsa-verifier/verify/verify_image.go b/cli/slsa-verifier/verify/verify_image.go index 9bf8c50e3..3dd5650a9 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) { @@ -50,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{ @@ -71,6 +73,7 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*uti } 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/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index 62b0cb1cf..68dd86f08 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,7 @@ 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, 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 +255,13 @@ 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 provenanceOpts.ExpectedProvenanceRepository != nil { + provenanceTargetRepository, err = name.NewRepository(*provenanceOpts.ExpectedProvenanceRepository) + if err != nil { + return nil, nil, err + } } registryClientOpts := []ociremote.Option{} @@ -276,6 +279,7 @@ func (v *GHAVerifier) VerifyImage(ctx context.Context, RekorPubKeys: trustedRoot.RekorPubKeys, CTLogPubKeys: trustedRoot.CTPubKeys, } + atts, _, err := container.RunCosignImageVerification(ctx, artifactImage, opts) if err != nil {