From 2769376c5f53ece39b5e3f19b4f595627d79ab98 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Mon, 16 Jan 2023 15:23:50 -0800 Subject: [PATCH] feature: support sign-blob with certificate Adds support for `--certificate` and `--certificate-chain` to the sign-blob command. Fixes #2635 Signed-off-by: Nathan Smith --- cmd/cosign/cli/attest.go | 4 ++-- cmd/cosign/cli/attest/attest.go | 4 +--- cmd/cosign/cli/attest/attest_blob.go | 4 +--- cmd/cosign/cli/attest/attest_blob_test.go | 8 +++++--- cmd/cosign/cli/attest_blob.go | 4 ++-- cmd/cosign/cli/options/key.go | 2 ++ cmd/cosign/cli/options/signblob.go | 13 +++++++++++++ cmd/cosign/cli/policy_init.go | 6 +++++- cmd/cosign/cli/sign.go | 2 ++ cmd/cosign/cli/sign/sign.go | 6 +++--- cmd/cosign/cli/sign/sign_blob.go | 2 +- cmd/cosign/cli/signblob.go | 5 +++++ doc/cosign_sign-blob.md | 5 +++++ 13 files changed, 47 insertions(+), 18 deletions(-) diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go index 57285c0e7ab..d87eabb5dfa 100644 --- a/cmd/cosign/cli/attest.go +++ b/cmd/cosign/cli/attest.go @@ -66,6 +66,8 @@ func Attest() *cobra.Command { } ko := options.KeyOpts{ KeyRef: o.Key, + Cert: o.Cert, + CertChain: o.CertChain, PassFunc: generate.GetPass, Sk: o.SecurityKey.Use, Slot: o.SecurityKey.Slot, @@ -84,8 +86,6 @@ func Attest() *cobra.Command { attestCommand := attest.AttestCommand{ KeyOpts: ko, RegistryOptions: o.Registry, - CertPath: o.Cert, - CertChainPath: o.CertChain, NoUpload: o.NoUpload, PredicatePath: o.Predicate.Path, PredicateType: o.Predicate.Type, diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go index 3b38f38c385..e7e80dd07c0 100644 --- a/cmd/cosign/cli/attest/attest.go +++ b/cmd/cosign/cli/attest/attest.go @@ -71,8 +71,6 @@ func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, type AttestCommand struct { options.KeyOpts options.RegistryOptions - CertPath string - CertChainPath string NoUpload bool PredicatePath string PredicateType string @@ -118,7 +116,7 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error { // each access. ref = digest // nolint - sv, err := sign.SignerFromKeyOpts(ctx, c.CertPath, c.CertChainPath, c.KeyOpts) + sv, err := sign.SignerFromKeyOpts(ctx, c.KeyOpts) if err != nil { return fmt.Errorf("getting signer: %w", err) } diff --git a/cmd/cosign/cli/attest/attest_blob.go b/cmd/cosign/cli/attest/attest_blob.go index ec52a3d2319..ae61ee34848 100644 --- a/cmd/cosign/cli/attest/attest_blob.go +++ b/cmd/cosign/cli/attest/attest_blob.go @@ -48,8 +48,6 @@ import ( // nolint type AttestBlobCommand struct { options.KeyOpts - CertPath string - CertChainPath string ArtifactHash string @@ -114,7 +112,7 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error } defer predicate.Close() - sv, err := sign.SignerFromKeyOpts(ctx, c.CertPath, c.CertChainPath, c.KeyOpts) + sv, err := sign.SignerFromKeyOpts(ctx, c.KeyOpts) if err != nil { return fmt.Errorf("getting signer: %w", err) } diff --git a/cmd/cosign/cli/attest/attest_blob_test.go b/cmd/cosign/cli/attest/attest_blob_test.go index 6a21fb778cd..ad7eaf284aa 100644 --- a/cmd/cosign/cli/attest/attest_blob_test.go +++ b/cmd/cosign/cli/attest/attest_blob_test.go @@ -148,9 +148,11 @@ func TestAttestBlobCmdLocalKeyAndCert(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { at := AttestBlobCommand{ - KeyOpts: options.KeyOpts{KeyRef: tc.keyref}, - CertPath: tc.certref, - CertChainPath: tc.certchainref, + KeyOpts: options.KeyOpts{ + KeyRef: tc.keyref, + Cert: tc.certref, + CertChain: tc.certchainref, + }, PredicatePath: predicatePath, PredicateType: predicateType, } diff --git a/cmd/cosign/cli/attest_blob.go b/cmd/cosign/cli/attest_blob.go index 81a85b2f682..ca05aeacc0f 100644 --- a/cmd/cosign/cli/attest_blob.go +++ b/cmd/cosign/cli/attest_blob.go @@ -53,6 +53,8 @@ func AttestBlob() *cobra.Command { } ko := options.KeyOpts{ KeyRef: o.Key, + Cert: o.Cert, + CertChain: o.CertChain, PassFunc: generate.GetPass, Sk: o.SecurityKey.Use, Slot: o.SecurityKey.Slot, @@ -72,8 +74,6 @@ func AttestBlob() *cobra.Command { } v := attest.AttestBlobCommand{ KeyOpts: ko, - CertPath: o.Cert, - CertChainPath: o.CertChain, ArtifactHash: o.Hash, TlogUpload: o.TlogUpload, PredicateType: o.Predicate.Type, diff --git a/cmd/cosign/cli/options/key.go b/cmd/cosign/cli/options/key.go index b9783a0b591..833500599e7 100644 --- a/cmd/cosign/cli/options/key.go +++ b/cmd/cosign/cli/options/key.go @@ -21,6 +21,8 @@ type KeyOpts struct { Sk bool Slot string KeyRef string + Cert string + CertChain string FulcioURL string RekorURL string IDToken string diff --git a/cmd/cosign/cli/options/signblob.go b/cmd/cosign/cli/options/signblob.go index 8cc23e23a35..1fd0a1c101d 100644 --- a/cmd/cosign/cli/options/signblob.go +++ b/cmd/cosign/cli/options/signblob.go @@ -23,6 +23,8 @@ import ( // The new output-certificate flag is only in use when COSIGN_EXPERIMENTAL is enabled type SignBlobOptions struct { Key string + Cert string + CertChain string Base64Output bool Output string // deprecated: TODO remove when the output flag is fully deprecated OutputSignature string // TODO: this should be the root output file arg. @@ -52,6 +54,17 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) { "path to the private key file, KMS URI or Kubernetes Secret") _ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{}) + cmd.Flags().StringVar(&o.Cert, "certificate", "", + "path to the X.509 certificate in PEM format to include in the OCI Signature") + _ = cmd.Flags().SetAnnotation("certificate", cobra.BashCompFilenameExt, []string{"cert"}) + + cmd.Flags().StringVar(&o.CertChain, "certificate-chain", "", + "path to a list of CA X.509 certificates in PEM format which will be needed "+ + "when building the certificate chain for the signing certificate. "+ + "Must start with the parent intermediate CA certificate of the "+ + "signing certificate and end with the root certificate. Included in the OCI Signature") + _ = cmd.Flags().SetAnnotation("certificate-chain", cobra.BashCompFilenameExt, []string{"cert"}) + cmd.Flags().BoolVar(&o.Base64Output, "b64", true, "whether to base64 encode the output") diff --git a/cmd/cosign/cli/policy_init.go b/cmd/cosign/cli/policy_init.go index 4fde1c4c91a..880ff436372 100644 --- a/cmd/cosign/cli/policy_init.go +++ b/cmd/cosign/cli/policy_init.go @@ -188,6 +188,10 @@ func signPolicy() *cobra.Command { return err } ko := options.KeyOpts{ + // TODO(nsmith5): support signing keyless policy w/ BYO PKI + Cert: "", + CertChain: "", + FulcioURL: o.Fulcio.URL, IDToken: o.Fulcio.IdentityToken, InsecureSkipFulcioVerify: o.Fulcio.InsecureSkipFulcioVerify, @@ -200,7 +204,7 @@ func signPolicy() *cobra.Command { SkipConfirmation: o.SkipConfirmation, TSAServerURL: o.TSAServerURL, } - sv, err := sign.SignerFromKeyOpts(ctx, "", "", ko) + sv, err := sign.SignerFromKeyOpts(ctx, ko) if err != nil { return err diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 81d02162b99..71f58e005fa 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -96,6 +96,8 @@ race conditions or (worse) malicious tampering. } ko := options.KeyOpts{ KeyRef: o.Key, + Cert: o.Cert, + CertChain: o.CertChain, PassFunc: generate.GetPass, Sk: o.SecurityKey.Use, Slot: o.SecurityKey.Slot, diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 560e5767fe3..9e4c0e9f064 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -146,7 +146,7 @@ func SignCmd(ro *options.RootOptions, ko options.KeyOpts, signOpts options.SignO ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() - sv, err := SignerFromKeyOpts(ctx, signOpts.Cert, signOpts.CertChain, ko) + sv, err := SignerFromKeyOpts(ctx, ko) if err != nil { return fmt.Errorf("getting signer: %w", err) } @@ -517,13 +517,13 @@ func keylessSigner(ctx context.Context, ko options.KeyOpts) (*SignerVerifier, er }, nil } -func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath string, ko options.KeyOpts) (*SignerVerifier, error) { +func SignerFromKeyOpts(ctx context.Context, ko options.KeyOpts) (*SignerVerifier, error) { if ko.Sk { return signerFromSecurityKey(ctx, ko.Slot) } if ko.KeyRef != "" { - return signerFromKeyRef(ctx, certPath, certChainPath, ko.KeyRef, ko.PassFunc) + return signerFromKeyRef(ctx, ko.Cert, ko.CertChain, ko.KeyRef, ko.PassFunc) } // Default Keyless! diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index 6c6e231c04b..4538979ada1 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -59,7 +59,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() - sv, err := SignerFromKeyOpts(ctx, "", "", ko) + sv, err := SignerFromKeyOpts(ctx, ko) if err != nil { return nil, err } diff --git a/cmd/cosign/cli/signblob.go b/cmd/cosign/cli/signblob.go index d8c2cfc0e83..946a0968afc 100644 --- a/cmd/cosign/cli/signblob.go +++ b/cmd/cosign/cli/signblob.go @@ -41,6 +41,9 @@ func SignBlob() *cobra.Command { # sign a blob with a local key pair file cosign sign-blob --key cosign.key + # sign a blob with local certificate and CA bundle + cosign sign-blob --key cosign.key --certificate --certificate-chain --bundle + # sign a blob with a key pair stored in Azure Key Vault cosign sign-blob --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] @@ -67,6 +70,8 @@ func SignBlob() *cobra.Command { } ko := options.KeyOpts{ KeyRef: o.Key, + Cert: o.Cert, + CertChain: o.CertChain, PassFunc: generate.GetPass, Sk: o.SecurityKey.Use, Slot: o.SecurityKey.Slot, diff --git a/doc/cosign_sign-blob.md b/doc/cosign_sign-blob.md index 414ec2c6ad7..f8d6d41142d 100644 --- a/doc/cosign_sign-blob.md +++ b/doc/cosign_sign-blob.md @@ -17,6 +17,9 @@ cosign sign-blob [flags] # sign a blob with a local key pair file cosign sign-blob --key cosign.key + # sign a blob with local certificate and CA bundle + cosign sign-blob --key cosign.key --certificate --certificate-chain --bundle + # sign a blob with a key pair stored in Azure Key Vault cosign sign-blob --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] @@ -35,6 +38,8 @@ cosign sign-blob [flags] ``` --b64 whether to base64 encode the output (default true) --bundle string write everything required to verify the blob to a FILE + --certificate string path to the X.509 certificate in PEM format to include in the OCI Signature + --certificate-chain string path to a list of CA X.509 certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Included in the OCI Signature --fulcio-url string [EXPERIMENTAL] address of sigstore PKI server (default "https://fulcio.sigstore.dev") -h, --help help for sign-blob --identity-token string [EXPERIMENTAL] identity token to use for certificate from fulcio. the token or a path to a file containing the token is accepted.