Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fixes #724: add input for --provenance-repository while image verification #736

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion cli/slsa-verifier/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
}
Expand Down
42 changes: 42 additions & 0 deletions cli/slsa-verifier/verify/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
saisatishkarra marked this conversation as resolved.
Show resolved Hide resolved
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: <registry>/<repository>. 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")
Copy link
Member

@ianlewis ianlewis Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you just call o.VerifyImage.AddFlags(cmd)?

Suggested change
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: <registry>/<repository>. 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")
o.VerifyImage.AddFlags(cmd)
cmd.Flags().StringVar(&o.ProvenanceRepository, "provenance-repository", "",
"image repository for provenance with format: <registry>/<repository>. When set, overrides COSIGN_REPOSITORY environment variable")

}

// VerifyNpmOptions is the top-level options for the `verifyNpmPackage` command.
type VerifyNpmOptions struct {
VerifyOptions
Expand Down
24 changes: 15 additions & 9 deletions cli/slsa-verifier/verify/verify_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions verifiers/internal/gcb/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 16 additions & 6 deletions verifiers/internal/gha/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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. */
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove support for COSIGN_REPOSITORY.

Copy link
Member

@ianlewis ianlewis Jan 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. I was about to suggest this as well since we'll probably want to move to using sigstore-go and not rely on cosign packages. The --provenance-repository option is a better way to implement what we want.

provenanceTargetRepository, err = ociremote.GetEnvTargetRepository()
if err != nil {
return nil, nil, err
}
}

registryClientOpts := []ociremote.Option{}
Expand Down
3 changes: 2 additions & 1 deletion verifiers/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ func getVerifier(builderOpts *options.BuilderOpts) (register.SLSAVerifier, error

func VerifyImage(ctx context.Context, artifactImage string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some folks use slsa-verifier's Go API. Could we maybe add a new method VerifyImageProvenanceRepo with the new signature rather than modify VerifyImage in order to maintain API backwards compatibility?

Or maybe it makes sense to add another options.ImageOptions (with just ProvenanceRepository on it for now) and add a VerifyImageWithOptions function instead in order to protect against future API changes? WDUT?

/cc @laurentsimon

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.VerifyImage(ctx, provenance, artifactImage, provenanceOpts, builderOpts)
return verifier.VerifyImage(ctx, provenance, provenanceRepository, artifactImage, provenanceOpts, builderOpts)
}

func VerifyArtifact(ctx context.Context,
Expand Down
Loading