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

Add … push --sign-by-sigstore #17088

Merged
merged 1 commit into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
44 changes: 37 additions & 7 deletions cmd/podman/common/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,34 @@ package common

import (
"fmt"
"os"

"github.com/containers/common/pkg/ssh"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/pkg/cli/sigstore"
"github.com/containers/image/v5/signature/signer"
"github.com/containers/podman/v4/pkg/domain/entities"
)

// PrepareSigningPassphrase updates pushOpts.SignPassphrase and SignSigstorePrivateKeyPassphrase based on a --sign-passphrase-file value signPassphraseFile,
// and validates pushOpts.Sign* consistency.
// It may interactively prompt for a passphrase if one is required and wasn’t provided otherwise.
func PrepareSigningPassphrase(pushOpts *entities.ImagePushOptions, signPassphraseFile string) error {
// PrepareSigning updates pushOpts.Signers, pushOpts.SignPassphrase and SignSigstorePrivateKeyPassphrase based on a --sign-passphrase-file
// value signPassphraseFile and a --sign-by-sigsstore value signBySigstoreParamFile, and validates pushOpts.Sign* consistency.
// It may interactively prompt for a passphrase if one is required and wasn’t provided otherwise;
// or it may interactively trigger an OIDC authentication, using standard input/output, or even open a web browser.
// Returns a cleanup callback on success, which must be called when done.
func PrepareSigning(pushOpts *entities.ImagePushOptions,
signPassphraseFile, signBySigstoreParamFile string) (func(), error) {
// c/common/libimage.Image does allow creating both simple signing and sigstore signatures simultaneously,
// with independent passphrases, but that would make the CLI probably too confusing.
// For now, use the passphrase with either, but only one of them.
if signPassphraseFile != "" && pushOpts.SignBy != "" && pushOpts.SignBySigstorePrivateKeyFile != "" {
return fmt.Errorf("only one of --sign-by and sign-by-sigstore-private-key can be used with --sign-passphrase-file")
return nil, fmt.Errorf("only one of --sign-by and sign-by-sigstore-private-key can be used with --sign-passphrase-file")
}

var passphrase string
if signPassphraseFile != "" {
p, err := cli.ReadPassphraseFile(signPassphraseFile)
if err != nil {
return err
return nil, err
}
passphrase = p
} else if pushOpts.SignBySigstorePrivateKeyFile != "" {
Expand All @@ -32,5 +38,29 @@ func PrepareSigningPassphrase(pushOpts *entities.ImagePushOptions, signPassphras
} // pushOpts.SignBy triggers a GPG-agent passphrase prompt, possibly using a more secure channel, so we usually shouldn’t prompt ourselves if no passphrase was explicitly provided.
pushOpts.SignPassphrase = passphrase
pushOpts.SignSigstorePrivateKeyPassphrase = []byte(passphrase)
return nil
cleanup := signingCleanup{}
if signBySigstoreParamFile != "" {
signer, err := sigstore.NewSignerFromParameterFile(signBySigstoreParamFile, &sigstore.Options{
PrivateKeyPassphrasePrompt: cli.ReadPassphraseFile,
Stdin: os.Stdin,
Stdout: os.Stdout,
})
if err != nil {
return nil, err
}
pushOpts.Signers = append(pushOpts.Signers, signer)
cleanup.signers = append(cleanup.signers, signer)
}
return cleanup.cleanup, nil
}

// signingCleanup carries state for cleanup after PrepareSigning
type signingCleanup struct {
signers []*signer.Signer
}

func (c *signingCleanup) cleanup() {
for _, s := range c.signers {
s.Close()
}
}
21 changes: 15 additions & 6 deletions cmd/podman/images/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import (
// CLI-only fields into the API types.
type pushOptionsWrapper struct {
entities.ImagePushOptions
TLSVerifyCLI bool // CLI only
CredentialsCLI string
SignPassphraseFileCLI string
EncryptionKeys []string
EncryptLayers []int
TLSVerifyCLI bool // CLI only
CredentialsCLI string
SignPassphraseFileCLI string
SignBySigstoreParamFileCLI string
EncryptionKeys []string
EncryptLayers []int
}

var (
Expand Down Expand Up @@ -110,6 +111,10 @@ func pushFlags(cmd *cobra.Command) {
flags.StringVar(&pushOptions.SignBy, signByFlagName, "", "Add a signature at the destination using the specified key")
_ = cmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone)

signBySigstoreFlagName := "sign-by-sigstore"
flags.StringVar(&pushOptions.SignBySigstoreParamFileCLI, signBySigstoreFlagName, "", "Sign the image using a sigstore parameter file at `PATH`")
_ = cmd.RegisterFlagCompletionFunc(signBySigstoreFlagName, completion.AutocompleteDefault)

signBySigstorePrivateKeyFlagName := "sign-by-sigstore-private-key"
flags.StringVar(&pushOptions.SignBySigstorePrivateKeyFile, signBySigstorePrivateKeyFlagName, "", "Sign the image using a sigstore private key at `PATH`")
_ = cmd.RegisterFlagCompletionFunc(signBySigstorePrivateKeyFlagName, completion.AutocompleteDefault)
Expand Down Expand Up @@ -138,6 +143,7 @@ func pushFlags(cmd *cobra.Command) {
_ = flags.MarkHidden("digestfile")
_ = flags.MarkHidden("quiet")
_ = flags.MarkHidden(signByFlagName)
_ = flags.MarkHidden(signBySigstoreFlagName)
_ = flags.MarkHidden(signBySigstorePrivateKeyFlagName)
_ = flags.MarkHidden(signPassphraseFileFlagName)
_ = flags.MarkHidden(encryptionKeysFlagName)
Expand Down Expand Up @@ -181,9 +187,12 @@ func imagePush(cmd *cobra.Command, args []string) error {
pushOptions.Writer = os.Stderr
}

if err := common.PrepareSigningPassphrase(&pushOptions.ImagePushOptions, pushOptions.SignPassphraseFileCLI); err != nil {
signingCleanup, err := common.PrepareSigning(&pushOptions.ImagePushOptions,
pushOptions.SignPassphraseFileCLI, pushOptions.SignBySigstoreParamFileCLI)
if err != nil {
return err
}
defer signingCleanup()

encConfig, encLayers, err := util.EncryptConfig(pushOptions.EncryptionKeys, pushOptions.EncryptLayers)
if err != nil {
Expand Down
17 changes: 13 additions & 4 deletions cmd/podman/manifest/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (
type manifestPushOptsWrapper struct {
entities.ImagePushOptions

TLSVerifyCLI, Insecure bool // CLI only
CredentialsCLI string
SignPassphraseFileCLI string
TLSVerifyCLI, Insecure bool // CLI only
CredentialsCLI string
SignBySigstoreParamFileCLI string
SignPassphraseFileCLI string
}

var (
Expand Down Expand Up @@ -76,6 +77,10 @@ func init() {
flags.StringVar(&manifestPushOpts.SignBy, signByFlagName, "", "sign the image using a GPG key with the specified `FINGERPRINT`")
_ = pushCmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone)

signBySigstoreFlagName := "sign-by-sigstore"
flags.StringVar(&manifestPushOpts.SignBySigstoreParamFileCLI, signBySigstoreFlagName, "", "Sign the image using a sigstore parameter file at `PATH`")
_ = pushCmd.RegisterFlagCompletionFunc(signBySigstoreFlagName, completion.AutocompleteDefault)

signBySigstorePrivateKeyFlagName := "sign-by-sigstore-private-key"
flags.StringVar(&manifestPushOpts.SignBySigstorePrivateKeyFile, signBySigstorePrivateKeyFlagName, "", "Sign the image using a sigstore private key at `PATH`")
_ = pushCmd.RegisterFlagCompletionFunc(signBySigstorePrivateKeyFlagName, completion.AutocompleteDefault)
Expand All @@ -97,6 +102,7 @@ func init() {
if registry.IsRemote() {
_ = flags.MarkHidden("cert-dir")
_ = flags.MarkHidden(signByFlagName)
_ = flags.MarkHidden(signBySigstoreFlagName)
_ = flags.MarkHidden(signBySigstorePrivateKeyFlagName)
_ = flags.MarkHidden(signPassphraseFileFlagName)
}
Expand Down Expand Up @@ -128,9 +134,12 @@ func push(cmd *cobra.Command, args []string) error {
manifestPushOpts.Writer = os.Stderr
}

if err := common.PrepareSigningPassphrase(&manifestPushOpts.ImagePushOptions, manifestPushOpts.SignPassphraseFileCLI); err != nil {
signingCleanup, err := common.PrepareSigning(&manifestPushOpts.ImagePushOptions,
manifestPushOpts.SignPassphraseFileCLI, manifestPushOpts.SignBySigstoreParamFileCLI)
if err != nil {
return err
}
defer signingCleanup()

// TLS verification in c/image is controlled via a `types.OptionalBool`
// which allows for distinguishing among set-true, set-false, unspecified
Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-manifest-push.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ Delete the manifest list or image index from local storage if pushing succeeds.

Sign the pushed images with a “simple signing” signature using the specified key. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)

#### **--sign-by-sigstore**=*param-file***

Add a sigstore signature based on further options specified in a containers sigstore signing parameter file *param-file*.
See containers-sigstore-signing-params.yaml(5) for details about the file format.

#### **--sign-by-sigstore-private-key**=*path*

Sign the pushed images with a sigstore signature using a private key at the specified path. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-push.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ Discard any pre-existing signatures in the image.

Add a “simple signing” signature at the destination using the specified key. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)

#### **--sign-by-sigstore**=*param-file***

Add a sigstore signature based on further options specified in a containers sigstore signing parameter file *param-file*.
See containers-sigstore-signing-params.yaml(5) for details about the file format.

#### **--sign-by-sigstore-private-key**=*path*

Add a sigstore signature at the destination using a private key at the specified path. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
Expand Down
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ require (
github.com/containerd/containerd v1.6.15 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -91,6 +92,7 @@ require (
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsouza/go-dockerclient v1.9.3 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
Expand All @@ -101,20 +103,27 @@ require (
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/validate v0.22.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-containerregistry v0.12.1 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/trillian v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jinzhu/copier v0.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
Expand All @@ -128,16 +137,19 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/proglottis/gpgme v0.1.3 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sigstore/fulcio v1.0.0 // indirect
github.com/sigstore/rekor v1.0.1 // indirect
github.com/sigstore/sigstore v1.5.1 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
github.com/sylabs/sif/v2 v2.9.0 // indirect
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
Expand All @@ -153,7 +165,9 @@ require (
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/tools v0.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.51.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
Expand Down
Loading