From 1949d8e930aa7dea6926cfea21fcffcf69725797 Mon Sep 17 00:00:00 2001 From: Zachary Newman Date: Sat, 23 Apr 2022 19:04:33 -0400 Subject: [PATCH] feat: add "env://" scheme for key lookup Tested manually as well: ```shell $ export COSIGN_PASSWORD=foo $ cosign generate-key-pair Enter password for private key: Enter password for private key again: Private key written to cosign.key Public key written to cosign.pub $ export MYPRIVKEY="$(cat cosign.key)" $ export MYPUBKEY="$(cat cosign.pub)" $ cosign verify-blob --key env://MYPUBKEY /dev/null --signature <(cosign sign-blob --key env://MYPRIVKEY /dev/null) Using payload from: /dev/null tlog entry created with index: 2095539 tlog entry verified with uuid: dd55086556d7ac0cded8f50961b68f7740e1435fbc5bb47460a8d78321313c7d index: 2095539 Verified OK ``` Signed-off-by: Zachary Newman --- cmd/cosign/cli/sign.go | 3 +++ cmd/cosign/cli/verify.go | 3 +++ pkg/signature/keys.go | 12 +++++++----- pkg/signature/keys_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 3acdc1b2b611..f0f2ea191295 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -47,6 +47,9 @@ func Sign() *cobra.Command { # sign a container image and add annotations cosign sign --key cosign.key -a key1=value1 -a key2=value2 + # sign a container image with a key stored in an environment variable + cosign sign --key env://[ENV_VAR] + # sign a container image with a key pair stored in Azure Key Vault cosign sign --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index e48b4175e6cd..6f180e7c5493 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -61,6 +61,9 @@ against the transparency log.`, # verify image with public key provided by URL cosign verify --key https://host.for/[FILE] + # verify image with a key stored in an environment variable + cosign verify --key env://[ENV_VAR] + # verify image with public key stored in Google Cloud KMS cosign verify --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY] diff --git a/pkg/signature/keys.go b/pkg/signature/keys.go index 775be117d15f..610d0c25b3a5 100644 --- a/pkg/signature/keys.go +++ b/pkg/signature/keys.go @@ -19,8 +19,6 @@ import ( "crypto" "crypto/x509" "fmt" - "os" - "path/filepath" "strings" "github.com/pkg/errors" @@ -86,7 +84,7 @@ func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto. } func loadKey(keyPath string, pf cosign.PassFunc) (signature.SignerVerifier, error) { - kb, err := os.ReadFile(filepath.Clean(keyPath)) + kb, err := blob.LoadFileOrURL(keyPath) if err != nil { return nil, err } @@ -169,10 +167,14 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass if strings.Contains(keyRef, "://") { sv, err := kms.Get(ctx, keyRef, crypto.SHA256) - if err != nil { + if err == nil { + return sv, nil + } + var e *kms.ProviderNotFoundError + if !errors.As(err, &e) { return nil, fmt.Errorf("kms get: %w", err) } - return sv, nil + // ProviderNotFoundError is okay; loadKey handles other URL schemes } return loadKey(keyRef, pf) diff --git a/pkg/signature/keys_test.go b/pkg/signature/keys_test.go index e95d335639e1..5f1c0ac43898 100644 --- a/pkg/signature/keys_test.go +++ b/pkg/signature/keys_test.go @@ -108,6 +108,35 @@ func TestPublicKeyFromFileRef(t *testing.T) { } } +func TestPublicKeyFromEnvVar(t *testing.T) { + keys, err := cosign.GenerateKeyPair(pass("whatever")) + if err != nil { + t.Fatalf("failed to generate keypair: %v", err) + } + ctx := context.Background() + + os.Setenv("MY_ENV_VAR", string(keys.PublicBytes)) + defer os.Unsetenv("MY_ENV_VAR") + if _, err := PublicKeyFromKeyRef(ctx, "env://MY_ENV_VAR"); err != nil { + t.Fatalf("PublicKeyFromKeyRef returned error: %v", err) + } +} + +func TestSignerVerifierFromEnvVar(t *testing.T) { + passFunc := pass("whatever") + keys, err := cosign.GenerateKeyPair(passFunc) + if err != nil { + t.Fatalf("failed to generate keypair: %v", err) + } + ctx := context.Background() + + os.Setenv("MY_ENV_VAR", string(keys.PrivateBytes)) + defer os.Unsetenv("MY_ENV_VAR") + if _, err := SignerVerifierFromKeyRef(ctx, "env://MY_ENV_VAR", passFunc); err != nil { + t.Fatalf("SignerVerifierFromKeyRef returned error: %v", err) + } +} + func pass(s string) cosign.PassFunc { return func(_ bool) ([]byte, error) { return []byte(s), nil