From db90d1380ff084749fd00ada8ce0f348d04e5ad7 Mon Sep 17 00:00:00 2001 From: Hayden B Date: Sat, 26 Mar 2022 05:47:54 -0700 Subject: [PATCH] Add certificate chain flag for signing (#1656) * Add certificate chain flag for signing This allows users to pass their own certificate chain to include in the OCI signature. The chain is checked for validity using the provided certificate. Also refactored the check for matching public keys using a method from sigstore/sigstore, comparing the certificate's key with the provided key. Also added this check when extracting the PKCS11 certificate. Certificate chains must be PEM-encoded. I changed the text of the certificate flag to also specify a preference for PEM encoding, but didn't remove the code that handles DER encoding for backwards compatibility. Signed-off-by: Hayden Blauzvern * Adding 3rd party licenses Signed-off-by: Hayden Blauzvern * Added check for empty chain Signed-off-by: Hayden Blauzvern --- cmd/cosign/cli/attest.go | 5 +- cmd/cosign/cli/attest/attest.go | 4 +- cmd/cosign/cli/options/attest.go | 9 +- cmd/cosign/cli/options/sign.go | 9 +- cmd/cosign/cli/policy_init.go | 2 +- cmd/cosign/cli/sign.go | 6 +- cmd/cosign/cli/sign/sign.go | 90 +++++++--- cmd/cosign/cli/sign/sign_blob.go | 2 +- cmd/cosign/cli/sign/sign_test.go | 159 +++++++++++++++++- doc/cosign_attest.md | 6 +- doc/cosign_sign.md | 6 +- go.mod | 10 +- go.sum | 30 ++-- test/e2e_test.go | 42 ++--- .../go/kms/{apiv1 => }/LICENSE | 0 .../github.com/hashicorp/vault/api/client.go | 91 +++++++++- .../hashicorp/vault/api/lifetime_watcher.go | 2 +- .../github.com/hashicorp/vault/api/logical.go | 5 +- .../hashicorp/vault/api/output_string.go | 24 ++- .../hashicorp/vault/api/plugin_helpers.go | 1 - .../github.com/hashicorp/vault/api/secret.go | 3 + .../hashicorp/vault/api/sys_mounts.go | 91 +++++++++- 22 files changed, 498 insertions(+), 99 deletions(-) rename third_party/VENDOR-LICENSE/cloud.google.com/go/kms/{apiv1 => }/LICENSE (100%) diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go index 1bf7723ec08..16b2e99bf8c 100644 --- a/cmd/cosign/cli/attest.go +++ b/cmd/cosign/cli/attest.go @@ -51,6 +51,9 @@ func Attest() *cobra.Command { # attach an attestation to a container image with a key pair stored in Hashicorp Vault cosign attest --predicate --type --key hashivault://[KEY] + # attach an attestation to a container image with a local key pair file, including a certificate and certificate chain + cosign attest --predicate --type --key cosign.key --cert cosign.crt --cert-chain chain.crt + # attach an attestation to a container image which does not fully support OCI media types COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate --type --key cosign.key legacy-registry.example.com/my/image`, @@ -70,7 +73,7 @@ func Attest() *cobra.Command { OIDCClientSecret: o.OIDC.ClientSecret, } for _, img := range args { - if err := attest.AttestCmd(cmd.Context(), ko, o.Registry, img, o.Cert, o.NoUpload, + if err := attest.AttestCmd(cmd.Context(), ko, o.Registry, img, o.Cert, o.CertChain, o.NoUpload, o.Predicate.Path, o.Force, o.Predicate.Type, o.Replace, ro.Timeout); err != nil { return errors.Wrapf(err, "signing %s", img) } diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go index 712d8a35294..bef095b5ba1 100644 --- a/cmd/cosign/cli/attest/attest.go +++ b/cmd/cosign/cli/attest/attest.go @@ -74,7 +74,7 @@ func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, } //nolint -func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOptions, imageRef string, certPath string, +func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOptions, imageRef string, certPath string, certChainPath string, noUpload bool, predicatePath string, force bool, predicateType string, replace bool, timeout time.Duration) error { // A key file or token is required unless we're in experimental mode! if options.EnableExperimental() { @@ -117,7 +117,7 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt // each access. ref = digest // nolint - sv, err := sign.SignerFromKeyOpts(ctx, certPath, ko) + sv, err := sign.SignerFromKeyOpts(ctx, certPath, certChainPath, ko) if err != nil { return errors.Wrap(err, "getting signer") } diff --git a/cmd/cosign/cli/options/attest.go b/cmd/cosign/cli/options/attest.go index 5f4dc1e3415..caa1db52133 100644 --- a/cmd/cosign/cli/options/attest.go +++ b/cmd/cosign/cli/options/attest.go @@ -23,6 +23,7 @@ import ( type AttestOptions struct { Key string Cert string + CertChain string NoUpload bool Force bool Recursive bool @@ -51,7 +52,13 @@ func (o *AttestOptions) AddFlags(cmd *cobra.Command) { "path to the private key file, KMS URI or Kubernetes Secret") cmd.Flags().StringVar(&o.Cert, "cert", "", - "path to the x509 certificate to include in the Signature") + "path to the X.509 certificate in PEM format to include in the OCI Signature") + + cmd.Flags().StringVar(&o.CertChain, "cert-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().BoolVar(&o.NoUpload, "no-upload", false, "do not upload the generated attestation") diff --git a/cmd/cosign/cli/options/sign.go b/cmd/cosign/cli/options/sign.go index 649037d3695..a4a1df779fb 100644 --- a/cmd/cosign/cli/options/sign.go +++ b/cmd/cosign/cli/options/sign.go @@ -23,6 +23,7 @@ import ( type SignOptions struct { Key string Cert string + CertChain string Upload bool Output string // deprecated: TODO remove when the output flag is fully deprecated OutputSignature string // TODO: this should be the root output file arg. @@ -55,7 +56,13 @@ func (o *SignOptions) AddFlags(cmd *cobra.Command) { "path to the private key file, KMS URI or Kubernetes Secret") cmd.Flags().StringVar(&o.Cert, "cert", "", - "path to the x509 certificate to include in the Signature") + "path to the X.509 certificate in PEM format to include in the OCI Signature") + + cmd.Flags().StringVar(&o.CertChain, "cert-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().BoolVar(&o.Upload, "upload", true, "whether to upload the signature") diff --git a/cmd/cosign/cli/policy_init.go b/cmd/cosign/cli/policy_init.go index 2ce0e3fe603..97fded69dc3 100644 --- a/cmd/cosign/cli/policy_init.go +++ b/cmd/cosign/cli/policy_init.go @@ -175,7 +175,7 @@ func signPolicy() *cobra.Command { } // Get Fulcio signer - sv, err := sign.SignerFromKeyOpts(ctx, "", sign.KeyOpts{ + sv, err := sign.SignerFromKeyOpts(ctx, "", "", sign.KeyOpts{ FulcioURL: o.Fulcio.URL, IDToken: o.Fulcio.IdentityToken, InsecureSkipFulcioVerify: o.Fulcio.InsecureSkipFulcioVerify, diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index b7036b2211f..9311156928a 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -62,6 +62,9 @@ func Sign() *cobra.Command { # sign a container image with a key pair stored in a Kubernetes secret cosign sign --key k8s://[NAMESPACE]/[KEY] + # sign a container image with a key, attaching a certificate and certificate chain + cosign sign --key cosign.key --cert cosign.crt --cert-chain chain.crt + # sign a container in a registry which does not fully support OCI media types COSIGN_DOCKER_MEDIA_TYPES=1 cosign sign --key cosign.key legacy-registry.example.com/my/image`, Args: cobra.MinimumNArgs(1), @@ -89,7 +92,8 @@ func Sign() *cobra.Command { if err != nil { return err } - if err := sign.SignCmd(ro, ko, o.Registry, annotationsMap.Annotations, args, o.Cert, o.Upload, o.OutputSignature, o.OutputCertificate, o.PayloadPath, o.Force, o.Recursive, o.Attachment); err != nil { + if err := sign.SignCmd(ro, ko, o.Registry, annotationsMap.Annotations, args, o.Cert, o.CertChain, o.Upload, + o.OutputSignature, o.OutputCertificate, o.PayloadPath, o.Force, o.Recursive, o.Attachment); err != nil { if o.Attachment == "" { return errors.Wrapf(err, "signing %v", args) } diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 18d0d5d99e4..4e72a89c30e 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -18,8 +18,6 @@ package sign import ( "bytes" "context" - "crypto/ecdsa" - "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" @@ -95,7 +93,8 @@ func GetAttachedImageRef(ref name.Reference, attachment string, opts ...ociremot // nolint func SignCmd(ro *options.RootOptions, ko KeyOpts, regOpts options.RegistryOptions, annotations map[string]interface{}, - imgs []string, certPath string, upload bool, outputSignature, outputCertificate string, payloadPath string, force bool, recursive bool, attachment string) error { + imgs []string, certPath string, certChainPath string, upload bool, outputSignature, outputCertificate string, + payloadPath string, force bool, recursive bool, attachment string) error { if options.EnableExperimental() { if options.NOf(ko.KeyRef, ko.Sk) > 1 { return &options.KeyParseError{} @@ -109,7 +108,7 @@ func SignCmd(ro *options.RootOptions, ko KeyOpts, regOpts options.RegistryOption ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() - sv, err := SignerFromKeyOpts(ctx, certPath, ko) + sv, err := SignerFromKeyOpts(ctx, certPath, certChainPath, ko) if err != nil { return errors.Wrap(err, "getting signer") } @@ -310,29 +309,40 @@ func signerFromSecurityKey(keySlot string) (*SignerVerifier, error) { }, nil } -func signerFromKeyRef(ctx context.Context, certPath, keyRef string, passFunc cosign.PassFunc) (*SignerVerifier, error) { +func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef string, passFunc cosign.PassFunc) (*SignerVerifier, error) { k, err := sigs.SignerVerifierFromKeyRef(ctx, keyRef, passFunc) if err != nil { return nil, errors.Wrap(err, "reading key") } - // Handle the -cert flag + // Attempt to extract certificate from PKCS11 token // With PKCS11, we assume the certificate is in the same slot on the PKCS11 // token as the private key. If it's not there, show a warning to the // user. if pkcs11Key, ok := k.(*pkcs11key.Key); ok { certFromPKCS11, _ := pkcs11Key.Certificate() - var pemBytes []byte if certFromPKCS11 == nil { fmt.Fprintln(os.Stderr, "warning: no x509 certificate retrieved from the PKCS11 token") - } else { - pemBytes, err = cryptoutils.MarshalCertificateToPEM(certFromPKCS11) - if err != nil { - pkcs11Key.Close() - return nil, err - } + return &SignerVerifier{ + SignerVerifier: k, + close: pkcs11Key.Close, + }, nil + } + pemBytes, err := cryptoutils.MarshalCertificateToPEM(certFromPKCS11) + if err != nil { + pkcs11Key.Close() + return nil, err + } + // Check that provided public key and certificate key match + pubKey, err := k.PublicKey() + if err != nil { + pkcs11Key.Close() + return nil, err + } + if cryptoutils.EqualKeys(pubKey, certFromPKCS11.PublicKey) != nil { + pkcs11Key.Close() + return nil, errors.New("pkcs11 key and certificate do not match") } - return &SignerVerifier{ Cert: pemBytes, SignerVerifier: k, @@ -342,15 +352,18 @@ func signerFromKeyRef(ctx context.Context, certPath, keyRef string, passFunc cos certSigner := &SignerVerifier{ SignerVerifier: k, } + if certPath == "" { return certSigner, nil } + // Handle --cert flag + // Allow both DER and PEM encoding certBytes, err := os.ReadFile(certPath) if err != nil { return nil, errors.Wrap(err, "read certificate") } - // Handle PEM. + // Handle PEM if bytes.HasPrefix(certBytes, []byte("-----")) { decoded, _ := pem.Decode(certBytes) if decoded.Type != "CERTIFICATE" { @@ -366,23 +379,44 @@ func signerFromKeyRef(ctx context.Context, certPath, keyRef string, passFunc cos if err != nil { return nil, errors.Wrap(err, "get public key") } - switch kt := parsedCert.PublicKey.(type) { - case *ecdsa.PublicKey: - if !kt.Equal(pk) { - return nil, errors.New("public key in certificate does not match that in the signing key") - } - case *rsa.PublicKey: - if !kt.Equal(pk) { - return nil, errors.New("public key in certificate does not match that in the signing key") - } - default: - return nil, fmt.Errorf("unsupported key type: %T", parsedCert.PublicKey) + if cryptoutils.EqualKeys(pk, parsedCert.PublicKey) != nil { + return nil, errors.New("public key in certificate does not match the provided public key") } pemBytes, err := cryptoutils.MarshalCertificateToPEM(parsedCert) if err != nil { return nil, errors.Wrap(err, "marshaling certificate to PEM") } certSigner.Cert = pemBytes + + if certChainPath == "" { + return certSigner, nil + } + + // Handle --cert-chain flag + // Accept only PEM encoded certificate chain + certChainBytes, err := os.ReadFile(certChainPath) + if err != nil { + return nil, errors.Wrap(err, "reading certificate chain from path") + } + certChain, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(certChainBytes)) + if err != nil { + return nil, errors.Wrap(err, "loading certificate chain") + } + if len(certChain) == 0 { + return nil, errors.New("no certificates in certificate chain") + } + // Verify certificate chain is valid + rootPool := x509.NewCertPool() + rootPool.AddCert(certChain[len(certChain)-1]) + subPool := x509.NewCertPool() + for _, c := range certChain[:len(certChain)-1] { + subPool.AddCert(c) + } + if err := cosign.TrustedCert(parsedCert, rootPool, subPool); err != nil { + return nil, errors.Wrap(err, "unable to validate certificate chain") + } + certSigner.Chain = certChainBytes + return certSigner, nil } @@ -418,13 +452,13 @@ func keylessSigner(ctx context.Context, ko KeyOpts) (*SignerVerifier, error) { }, nil } -func SignerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*SignerVerifier, error) { +func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath string, ko KeyOpts) (*SignerVerifier, error) { if ko.Sk { return signerFromSecurityKey(ko.Slot) } if ko.KeyRef != "" { - return signerFromKeyRef(ctx, certPath, ko.KeyRef, ko.PassFunc) + return signerFromKeyRef(ctx, certPath, certChainPath, 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 c32dff48be6..04821cb968d 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -71,7 +71,7 @@ func SignBlobCmd(ro *options.RootOptions, ko KeyOpts, regOpts options.RegistryOp 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/sign/sign_test.go b/cmd/cosign/cli/sign/sign_test.go index 65b0532d5ad..3dc923d8823 100644 --- a/cmd/cosign/cli/sign/sign_test.go +++ b/cmd/cosign/cli/sign/sign_test.go @@ -16,13 +16,95 @@ package sign import ( + "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" "errors" + "os" + "reflect" + "strings" "testing" "github.com/sigstore/cosign/cmd/cosign/cli/generate" "github.com/sigstore/cosign/cmd/cosign/cli/options" + "github.com/sigstore/cosign/pkg/cosign" + "github.com/sigstore/cosign/test" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/theupdateframework/go-tuf/encrypted" ) +func pass(s string) cosign.PassFunc { + return func(_ bool) ([]byte, error) { + return []byte(s), nil + } +} + +func generateCertificateFiles(t *testing.T, tmpDir string, pf cosign.PassFunc) (privFile, certFile, chainFile string, privKey *ecdsa.PrivateKey, cert *x509.Certificate, chain []*x509.Certificate) { + t.Helper() + + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey) + leafCert, privKey, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) + pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw}) + pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) + pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) + + x509Encoded, err := x509.MarshalPKCS8PrivateKey(privKey) + if err != nil { + t.Fatalf("failed to encode private key: %v", err) + } + password := []byte{} + if pf != nil { + password, err = pf(true) + if err != nil { + t.Fatalf("failed to read password: %v", err) + } + } + + encBytes, err := encrypted.Encrypt(x509Encoded, password) + if err != nil { + t.Fatalf("failed to encrypt key: %v", err) + } + + // store in PEM format + privBytes := pem.EncodeToMemory(&pem.Block{ + Bytes: encBytes, + Type: cosign.CosignPrivateKeyPemType, + }) + + tmpPrivFile, err := os.CreateTemp(tmpDir, "cosign_test_*.key") + if err != nil { + t.Fatalf("failed to create temp key file: %v", err) + } + defer tmpPrivFile.Close() + if _, err := tmpPrivFile.Write(privBytes); err != nil { + t.Fatalf("failed to write key file: %v", err) + } + + tmpCertFile, err := os.CreateTemp(tmpDir, "cosign.crt") + if err != nil { + t.Fatalf("failed to create temp certificate file: %v", err) + } + defer tmpCertFile.Close() + if _, err := tmpCertFile.Write(pemLeaf); err != nil { + t.Fatalf("failed to write certificate file: %v", err) + } + + tmpChainFile, err := os.CreateTemp(tmpDir, "cosign_chain.crt") + if err != nil { + t.Fatalf("failed to create temp chain file: %v", err) + } + defer tmpChainFile.Close() + pemChain := pemSub + pemChain = append(pemChain, pemRoot...) + if _, err := tmpChainFile.Write(pemChain); err != nil { + t.Fatalf("failed to write chain file: %v", err) + } + + return tmpPrivFile.Name(), tmpCertFile.Name(), tmpChainFile.Name(), privKey, leafCert, []*x509.Certificate{subCert, rootCert} +} + // TestSignCmdLocalKeyAndSk verifies the SignCmd returns an error // if both a local key path and a sk are specified func TestSignCmdLocalKeyAndSk(t *testing.T) { @@ -36,9 +118,84 @@ func TestSignCmdLocalKeyAndSk(t *testing.T) { Sk: true, }, } { - err := SignCmd(ro, ko, options.RegistryOptions{}, nil, nil, "", false, "", "", "", false, false, "") + err := SignCmd(ro, ko, options.RegistryOptions{}, nil, nil, "", "", false, "", "", "", false, false, "") if (errors.Is(err, &options.KeyParseError{}) == false) { t.Fatal("expected KeyParseError") } } } + +func Test_signerFromKeyRefSuccess(t *testing.T) { + tmpDir := t.TempDir() + ctx := context.Background() + keyFile, certFile, chainFile, privKey, cert, chain := generateCertificateFiles(t, tmpDir, pass("foo")) + + signer, err := signerFromKeyRef(ctx, certFile, chainFile, keyFile, pass("foo")) + if err != nil { + t.Fatalf("unexpected error generating signer: %v", err) + } + // Expect public key matches + pubKey, err := signer.SignerVerifier.PublicKey() + if err != nil { + t.Fatalf("unexpected error fetching pubkey: %v", err) + } + if !privKey.Public().(*ecdsa.PublicKey).Equal(pubKey) { + t.Fatalf("public keys must be equal") + } + // Expect certificate matches + expectedPemBytes, err := cryptoutils.MarshalCertificateToPEM(cert) + if err != nil { + t.Fatalf("unexpected error marshalling certificate: %v", err) + } + if !reflect.DeepEqual(signer.Cert, expectedPemBytes) { + t.Fatalf("certificates must match") + } + // Expect certificate chain matches + expectedPemBytesChain, err := cryptoutils.MarshalCertificatesToPEM(chain) + if err != nil { + t.Fatalf("unexpected error marshalling certificate chain: %v", err) + } + if !reflect.DeepEqual(signer.Chain, expectedPemBytesChain) { + t.Fatalf("certificate chains must match") + } +} + +func Test_signerFromKeyRefFailure(t *testing.T) { + tmpDir := t.TempDir() + ctx := context.Background() + keyFile, certFile, _, _, _, _ := generateCertificateFiles(t, tmpDir, pass("foo")) + // Second set of files + tmpDir2 := t.TempDir() + _, certFile2, chainFile2, _, _, _ := generateCertificateFiles(t, tmpDir2, pass("bar")) + + // Public keys don't match + _, err := signerFromKeyRef(ctx, certFile2, chainFile2, keyFile, pass("foo")) + if err == nil || err.Error() != "public key in certificate does not match the provided public key" { + t.Fatalf("expected mismatched keys error, got %v", err) + } + // Certificate chain cannot be verified + _, err = signerFromKeyRef(ctx, certFile, chainFile2, keyFile, pass("foo")) + if err == nil || !strings.Contains(err.Error(), "unable to validate certificate chain") { + t.Fatalf("expected chain verification error, got %v", err) + } +} + +func Test_signerFromKeyRefFailureEmptyChainFile(t *testing.T) { + tmpDir := t.TempDir() + ctx := context.Background() + keyFile, certFile, _, _, _, _ := generateCertificateFiles(t, tmpDir, pass("foo")) + + tmpChainFile, err := os.CreateTemp(tmpDir, "cosign_chain_empty.crt") + if err != nil { + t.Fatalf("failed to create temp chain file: %v", err) + } + defer tmpChainFile.Close() + if _, err := tmpChainFile.Write([]byte{}); err != nil { + t.Fatalf("failed to write chain file: %v", err) + } + + _, err = signerFromKeyRef(ctx, certFile, tmpChainFile.Name(), keyFile, pass("foo")) + if err == nil || err.Error() != "no certificates in certificate chain" { + t.Fatalf("expected empty chain error, got %v", err) + } +} diff --git a/doc/cosign_attest.md b/doc/cosign_attest.md index 9bd3c6331e9..ec60eddd39a 100644 --- a/doc/cosign_attest.md +++ b/doc/cosign_attest.md @@ -29,6 +29,9 @@ cosign attest [flags] # attach an attestation to a container image with a key pair stored in Hashicorp Vault cosign attest --predicate --type --key hashivault://[KEY] + # attach an attestation to a container image with a local key pair file, including a certificate and certificate chain + cosign attest --predicate --type --key cosign.key --cert cosign.crt --cert-chain chain.crt + # attach an attestation to a container image which does not fully support OCI media types COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate --type --key cosign.key legacy-registry.example.com/my/image ``` @@ -38,7 +41,8 @@ cosign attest [flags] ``` --allow-insecure-registry whether to allow insecure connections to registries. Don't use this for anything but testing --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] - --cert string path to the x509 certificate to include in the Signature + --cert string path to the X.509 certificate in PEM format to include in the OCI Signature + --cert-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 -f, --force skip warnings and confirmations --fulcio-url string [EXPERIMENTAL] address of sigstore PKI server (default "https://fulcio.sigstore.dev") -h, --help help for attest diff --git a/doc/cosign_sign.md b/doc/cosign_sign.md index 49afae408a7..33b8984fa73 100644 --- a/doc/cosign_sign.md +++ b/doc/cosign_sign.md @@ -42,6 +42,9 @@ cosign sign [flags] # sign a container image with a key pair stored in a Kubernetes secret cosign sign --key k8s://[NAMESPACE]/[KEY] + # sign a container image with a key, attaching a certificate and certificate chain + cosign sign --key cosign.key --cert cosign.crt --cert-chain chain.crt + # sign a container in a registry which does not fully support OCI media types COSIGN_DOCKER_MEDIA_TYPES=1 cosign sign --key cosign.key legacy-registry.example.com/my/image ``` @@ -53,7 +56,8 @@ cosign sign [flags] -a, --annotations strings extra key=value pairs to sign --attachment string related image attachment to sign (sbom), default none --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] - --cert string path to the x509 certificate to include in the Signature + --cert string path to the X.509 certificate in PEM format to include in the OCI Signature + --cert-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 -f, --force skip warnings and confirmations --fulcio-url string [EXPERIMENTAL] address of sigstore PKI server (default "https://fulcio.sigstore.dev") -h, --help help for sign diff --git a/go.mod b/go.mod index 4b048b4d79b..3c5d7576407 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/secure-systems-lab/go-securesystemslib v0.3.1 github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3 - github.com/sigstore/sigstore v1.1.1-0.20220217212907-e48ca03a5ba7 + github.com/sigstore/sigstore v1.1.1-0.20220324220036-a3f98177f3b0 github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.10.1 github.com/spiffe/go-spiffe/v2 v2.0.0 @@ -107,10 +107,10 @@ require ( cloud.google.com/go v0.100.2 // indirect cloud.google.com/go/compute v1.5.0 // indirect cloud.google.com/go/iam v0.1.1 // indirect - cloud.google.com/go/kms v1.3.0 // indirect + cloud.google.com/go/kms v1.4.0 // indirect contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect contrib.go.opencensus.io/exporter/prometheus v0.4.0 // indirect - github.com/Azure/azure-sdk-for-go v62.0.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go v62.3.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.24 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect @@ -128,7 +128,7 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.43.0 // indirect + github.com/aws/aws-sdk-go v1.43.24 // indirect github.com/aws/aws-sdk-go-v2 v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/config v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.9.0 // indirect @@ -210,7 +210,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/hashicorp/vault/api v1.3.1 // indirect + github.com/hashicorp/vault/api v1.4.1 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20210703085342-c1f07ee84431 // indirect diff --git a/go.sum b/go.sum index 7149cb02c73..78419f12150 100644 --- a/go.sum +++ b/go.sum @@ -50,7 +50,6 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.1.0/go.mod h1:2NIffxgWfORSI7EOYMFatGTfjMLnqrOKBEyYb6NoRgA= cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= @@ -65,8 +64,8 @@ cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= cloud.google.com/go/kms v1.0.0/go.mod h1:nhUehi+w7zht2XrUfvTRNpxrfayBHqP4lu2NSywui/0= cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI= -cloud.google.com/go/kms v1.3.0 h1:hWHaFmfTjtWVgSfX/rzE1qmZa/1AFr/6xQXhnufjyZQ= -cloud.google.com/go/kms v1.3.0/go.mod h1:EIdZ6hpR15zmiaKC8YKYoXplDFnL+Z6f5VCGHJMDhLs= +cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= cloud.google.com/go/monitoring v1.1.0/go.mod h1:L81pzz7HKn14QCMaCs6NTQkdBnE87TElyanS95vIcl4= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -130,9 +129,9 @@ github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v60.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v60.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v61.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v62.0.0+incompatible h1:8N2k27SYtc12qj5nTsuFMFJPZn5CGmgMWqTy4y9I7Jw= github.com/Azure/azure-sdk-for-go v62.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v62.3.0+incompatible h1:Ctfsn9UoA/BB4HMYQlbPPgNXdX0tZ4tmb85+KFb2+RE= +github.com/Azure/azure-sdk-for-go v62.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= @@ -251,7 +250,6 @@ github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA= @@ -315,8 +313,8 @@ github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zK github.com/aws/aws-sdk-go v1.42.8/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.42.22/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= -github.com/aws/aws-sdk-go v1.43.0 h1:y4UrPbxU/mIL08qksVPE/nwH9IXuC1udjOaNyhEe+pI= -github.com/aws/aws-sdk-go v1.43.0/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.43.24 h1:7c2PniJ0wpmWsIA6OtYBw6wS7DF0IjbhvPq+0ZQYNXw= +github.com/aws/aws-sdk-go v1.43.24/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= github.com/aws/aws-sdk-go-v2 v1.11.0/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ= @@ -920,7 +918,8 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-rod/rod v0.101.8/go.mod h1:N/zlT53CfSpq74nb6rOR0K8UF0SPUPBmzBnArrms+mY= -github.com/go-rod/rod v0.102.1/go.mod h1:RXSLAlPodTFOmZnwaAQJIcOJ1i835r0uuTGPLO09t/M= +github.com/go-rod/rod v0.104.1 h1:CmFVu210HxSFOrmqX7/yLYIOF3pNm1SN9it+pQbi4ls= +github.com/go-rod/rod v0.104.1/go.mod h1:SVRIBdiq0sXXDdQxISmWuY8g1kLIUT+Pgk4VZSbOM1s= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -1315,8 +1314,9 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ= -github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= +github.com/hashicorp/vault/api v1.4.1 h1:mWLfPT0RhxBitjKr6swieCEP2v5pp/M//t70S3kMLRo= +github.com/hashicorp/vault/api v1.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= @@ -1737,6 +1737,7 @@ github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1895,8 +1896,8 @@ github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3 h1:mbqXrm8YZXN/cJ github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3/go.mod h1:u9clLqaVjqV9pExVL1XkM37dGyMCOX/LMocS9nsnWDY= github.com/sigstore/sigstore v1.0.2-0.20211210190220-04746d994282/go.mod h1:SuM+QIHtnnR9eGsURRLv5JfxM6KeaU0XKA1O7FmLs4Q= github.com/sigstore/sigstore v1.1.0/go.mod h1:gDpcHw4VwpoL5C6N1Ud1YtBsc+ikRDwDelDlWRyYoE8= -github.com/sigstore/sigstore v1.1.1-0.20220217212907-e48ca03a5ba7 h1:YIyoYwE9ggfBS+LgryGxZEhHQLS21V78jwdAM6fLkIY= -github.com/sigstore/sigstore v1.1.1-0.20220217212907-e48ca03a5ba7/go.mod h1:fPeKQqm6gcEp4Dp6nLp2LMCo3m+U0WfhzIqguCbhY9k= +github.com/sigstore/sigstore v1.1.1-0.20220324220036-a3f98177f3b0 h1:d0qfbAdCEQoi9/5Y+aqT3KAgYYilsp9Y8CagFvV4uQo= +github.com/sigstore/sigstore v1.1.1-0.20220324220036-a3f98177f3b0/go.mod h1:BFkCXUqJrUApmvOA3rfDFTrN5+wwKQV2RgbJnVzOSiY= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -2101,10 +2102,14 @@ github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mo github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs= +github.com/ysmood/goob v0.3.1 h1:qMp5364BGS1DLJVrAqUxTF6KOFt0YDot8GC70u/0jbI= github.com/ysmood/goob v0.3.1/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs= github.com/ysmood/got v0.15.1/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= +github.com/ysmood/got v0.16.2/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.6.4 h1:Yb6tosv6bk59HqjZu2/7o4BFherpYEMkDkXmlhgryZ4= github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw= github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= @@ -2591,6 +2596,7 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/test/e2e_test.go b/test/e2e_test.go index 40d5e82d0b4..c62280ba28a 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -126,7 +126,7 @@ func TestSignVerify(t *testing.T) { // Now sign the image ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -137,7 +137,7 @@ func TestSignVerify(t *testing.T) { // Sign the image with an annotation annotations := map[string]interface{}{"foo": "bar"} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, annotations, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, annotations, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // It should match this time. must(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) @@ -161,7 +161,7 @@ func TestSignVerifyClean(t *testing.T) { // Now sign the image ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -190,7 +190,7 @@ func TestImportSignVerifyClean(t *testing.T) { // Now sign the image ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -233,7 +233,7 @@ func TestAttestVerify(t *testing.T) { // Now attest the image ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", false, slsaAttestationPath, false, + must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, "slsaprovenance", false, 30*time.Second), t) // Use cue to verify attestation @@ -284,13 +284,13 @@ func TestAttestationReplace(t *testing.T) { } // Attest once with with replace=false creating an attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", false, slsaAttestationPath, false, + must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, "slsaprovenance", false, 30*time.Second), t) // Attest again with replace=true, replacing the previous attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", false, slsaAttestationPath, false, + must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, "slsaprovenance", true, 30*time.Second), t) // Attest once more replace=true using a different predicate, to ensure it adds a new attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", false, slsaAttestationPath, false, + must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, "custom", true, 30*time.Second), t) // Download and count the attestations @@ -334,7 +334,7 @@ func TestRekorBundle(t *testing.T) { } // Sign the image - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Make sure verify works must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -364,14 +364,14 @@ func TestDuplicateSign(t *testing.T) { // Now sign the image ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) // Signing again should work just fine... - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...)) must(err, t) @@ -461,14 +461,14 @@ func TestMultipleSignatures(t *testing.T) { // Now sign the image with one key ko := sign.KeyOpts{KeyRef: priv1, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify should work with that one, but not the other must(verify(pub1, imgName, true, nil, ""), t) mustErr(verify(pub2, imgName, true, nil, ""), t) // Now sign with the other key too ko.KeyRef = priv2 - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify should work with both must(verify(pub1, imgName, true, nil, ""), t) @@ -850,7 +850,7 @@ func TestSaveLoad(t *testing.T) { ctx := context.Background() // Now sign the image and verify it ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) must(verify(pubKeyPath, imgName, true, nil, ""), t) // save the image to a temp dir @@ -883,7 +883,7 @@ func TestSaveLoadAttestation(t *testing.T) { ctx := context.Background() // Now sign the image and verify it ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) must(verify(pubKeyPath, imgName, true, nil, ""), t) // now, append an attestation to the image @@ -895,7 +895,7 @@ func TestSaveLoadAttestation(t *testing.T) { // Now attest the image ko = sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", false, slsaAttestationPath, false, + must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, "custom", false, 30*time.Second), t) // save the image to a temp dir @@ -972,7 +972,7 @@ func TestAttachSBOM(t *testing.T) { // Now sign the sbom with one key ko1 := sign.KeyOpts{KeyRef: privKeyPath1, PassFunc: passFunc} - must(sign.SignCmd(ro, ko1, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, "sbom"), t) + must(sign.SignCmd(ro, ko1, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, "sbom"), t) // Now verify should work with that one, but not the other must(verify(pubKeyPath1, imgName, true, nil, "sbom"), t) @@ -1009,7 +1009,7 @@ func TestTlog(t *testing.T) { PassFunc: passFunc, RekorURL: rekorURL, } - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // Now verify should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -1021,7 +1021,7 @@ func TestTlog(t *testing.T) { mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) // Sign again with the tlog env var on - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) // And now verify works! must(verify(pubKeyPath, imgName, true, nil, ""), t) } @@ -1185,7 +1185,7 @@ func TestInvalidBundle(t *testing.T) { ko := sign.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc, RekorURL: rekorURL} regOpts := options.RegistryOptions{} - must(sign.SignCmd(ro, ko, regOpts, nil, []string{img1}, "", true, "", "", "", true, false, ""), t) + must(sign.SignCmd(ro, ko, regOpts, nil, []string{img1}, "", "", true, "", "", "", true, false, ""), t) // verify image1 must(verify(pubKeyPath, img1, true, nil, ""), t) // extract the bundle from image1 @@ -1210,7 +1210,7 @@ func TestInvalidBundle(t *testing.T) { img2 := path.Join(regName, "unrelated") imgRef2, _, cleanup := mkimage(t, img2) defer cleanup() - must(sign.SignCmd(ro, ko, regOpts, nil, []string{img2}, "", true, "", "", "", false, false, ""), t) + must(sign.SignCmd(ro, ko, regOpts, nil, []string{img2}, "", "", true, "", "", "", false, false, ""), t) must(verify(pubKeyPath, img2, true, nil, ""), t) si2, err := ociremote.SignedEntity(imgRef2, remoteOpts) diff --git a/third_party/VENDOR-LICENSE/cloud.google.com/go/kms/apiv1/LICENSE b/third_party/VENDOR-LICENSE/cloud.google.com/go/kms/LICENSE similarity index 100% rename from third_party/VENDOR-LICENSE/cloud.google.com/go/kms/apiv1/LICENSE rename to third_party/VENDOR-LICENSE/cloud.google.com/go/kms/LICENSE diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/client.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/client.go index b6c497d0b5a..6a804091a21 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/client.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/client.go @@ -51,6 +51,8 @@ const ( EnvRateLimit = "VAULT_RATE_LIMIT" EnvHTTPProxy = "VAULT_HTTP_PROXY" HeaderIndex = "X-Vault-Index" + HeaderForward = "X-Vault-Forward" + HeaderInconsistent = "X-Vault-Inconsistent" ) // Deprecated values @@ -132,6 +134,12 @@ type Config struct { // with the same client. Cloning a client will not clone this value. OutputCurlString bool + // curlCACert, curlCAPath, curlClientCert and curlClientKey are used to keep + // track of the name of the TLS certs and keys when OutputCurlString is set. + // Cloning a client will also not clone those values. + curlCACert, curlCAPath string + curlClientCert, curlClientKey string + // SRVLookup enables the client to lookup the host through DNS SRV lookup SRVLookup bool @@ -139,6 +147,9 @@ type Config struct { // its clone. CloneHeaders bool + // CloneToken from parent. + CloneToken bool + // ReadYourWrites ensures isolated read-after-write semantics by // providing discovered cluster replication states in each request. // The shared state is automatically propagated to all Client clones. @@ -180,7 +191,7 @@ type TLSConfig struct { // The default Address is https://127.0.0.1:8200, but this can be overridden by // setting the `VAULT_ADDR` environment variable. // -// If an error is encountered, this will return nil. +// If an error is encountered, the Error field on the returned *Config will be populated with the specific error. func DefaultConfig() *Config { config := &Config{ Address: "https://127.0.0.1:8200", @@ -222,9 +233,9 @@ func DefaultConfig() *Config { return config } -// ConfigureTLS takes a set of TLS configurations and applies those to the -// HTTP client. -func (c *Config) ConfigureTLS(t *TLSConfig) error { +// configureTLS is a lock free version of ConfigureTLS that can be used in +// ReadEnvironment where the lock is already hold +func (c *Config) configureTLS(t *TLSConfig) error { if c.HttpClient == nil { c.HttpClient = DefaultConfig().HttpClient } @@ -241,11 +252,15 @@ func (c *Config) ConfigureTLS(t *TLSConfig) error { return err } foundClientCert = true + c.curlClientCert = t.ClientCert + c.curlClientKey = t.ClientKey case t.ClientCert != "" || t.ClientKey != "": return fmt.Errorf("both client cert and client key must be provided") } if t.CACert != "" || t.CAPath != "" { + c.curlCACert = t.CACert + c.curlCAPath = t.CAPath rootConfig := &rootcerts.Config{ CAFile: t.CACert, CAPath: t.CAPath, @@ -275,6 +290,15 @@ func (c *Config) ConfigureTLS(t *TLSConfig) error { return nil } +// ConfigureTLS takes a set of TLS configurations and applies those to the +// HTTP client. +func (c *Config) ConfigureTLS(t *TLSConfig) error { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + + return c.configureTLS(t) +} + // ReadEnvironment reads configuration information from the environment. If // there is an error, no configuration value is updated. func (c *Config) ReadEnvironment() error { @@ -379,7 +403,7 @@ func (c *Config) ReadEnvironment() error { c.SRVLookup = envSRVLookup c.Limiter = limit - if err := c.ConfigureTLS(t); err != nil { + if err := c.configureTLS(t); err != nil { return err } @@ -547,6 +571,7 @@ func (c *Client) CloneConfig() *Config { newConfig.OutputCurlString = c.config.OutputCurlString newConfig.SRVLookup = c.config.SRVLookup newConfig.CloneHeaders = c.config.CloneHeaders + newConfig.CloneToken = c.config.CloneToken newConfig.ReadYourWrites = c.config.ReadYourWrites // we specifically want a _copy_ of the client here, not a pointer to the original one @@ -775,6 +800,12 @@ func (c *Client) setNamespace(namespace string) { c.headers.Set(consts.NamespaceHeaderName, namespace) } +func (c *Client) ClearNamespace() { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + c.headers.Del(consts.NamespaceHeaderName) +} + // Token returns the access token being used by this client. It will // return the empty string if there is no token set. func (c *Client) Token() string { @@ -873,6 +904,26 @@ func (c *Client) CloneHeaders() bool { return c.config.CloneHeaders } +// SetCloneToken from parent +func (c *Client) SetCloneToken(cloneToken bool) { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + c.config.modifyLock.Lock() + defer c.config.modifyLock.Unlock() + + c.config.CloneToken = cloneToken +} + +// CloneToken gets the configured CloneToken value. +func (c *Client) CloneToken() bool { + c.modifyLock.RLock() + defer c.modifyLock.RUnlock() + c.config.modifyLock.RLock() + defer c.config.modifyLock.RUnlock() + + return c.config.CloneToken +} + // SetReadYourWrites to prevent reading stale cluster replication state. func (c *Client) SetReadYourWrites(preventStaleReads bool) { c.modifyLock.Lock() @@ -904,12 +955,25 @@ func (c *Client) ReadYourWrites() bool { // Clone creates a new client with the same configuration. Note that the same // underlying http.Client is used; modifying the client from more than one // goroutine at once may not be safe, so modify the client as needed and then -// clone. +// clone. The headers are cloned based on the CloneHeaders property of the +// source config // // Also, only the client's config is currently copied; this means items not in // the api.Config struct, such as policy override and wrapping function // behavior, must currently then be set as desired on the new client. func (c *Client) Clone() (*Client, error) { + return c.clone(c.config.CloneHeaders) +} + +// CloneWithHeaders creates a new client similar to Clone, with the difference +// being that the headers are always cloned +func (c *Client) CloneWithHeaders() (*Client, error) { + return c.clone(true) +} + +// clone creates a new client, with the headers being cloned based on the +// passed in cloneheaders boolean +func (c *Client) clone(cloneHeaders bool) (*Client, error) { c.modifyLock.RLock() defer c.modifyLock.RUnlock() @@ -932,6 +996,7 @@ func (c *Client) Clone() (*Client, error) { AgentAddress: config.AgentAddress, SRVLookup: config.SRVLookup, CloneHeaders: config.CloneHeaders, + CloneToken: config.CloneToken, ReadYourWrites: config.ReadYourWrites, } client, err := NewClient(newConfig) @@ -939,10 +1004,14 @@ func (c *Client) Clone() (*Client, error) { return nil, err } - if config.CloneHeaders { + if cloneHeaders { client.SetHeaders(c.Headers().Clone()) } + if config.CloneToken { + client.SetToken(c.token) + } + client.replicationStateStore = c.replicationStateStore return client, nil @@ -1080,6 +1149,10 @@ START: LastOutputStringError = &OutputStringError{ Request: req, TLSSkipVerify: c.config.HttpClient.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify, + ClientCert: c.config.curlClientCert, + ClientKey: c.config.curlClientKey, + ClientCACert: c.config.curlCACert, + ClientCAPath: c.config.curlCAPath, } return nil, LastOutputStringError } @@ -1330,7 +1403,7 @@ func ParseReplicationState(raw string, hmacKey []byte) (*logical.WALState, error // conjunction with RequireState. func ForwardInconsistent() RequestCallback { return func(req *Request) { - req.Headers.Set("X-Vault-Inconsistent", "forward-active-node") + req.Headers.Set(HeaderInconsistent, "forward-active-node") } } @@ -1339,7 +1412,7 @@ func ForwardInconsistent() RequestCallback { // This feature must be enabled in Vault's configuration. func ForwardAlways() RequestCallback { return func(req *Request) { - req.Headers.Set("X-Vault-Forward", "active-node") + req.Headers.Set(HeaderForward, "active-node") } } diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/lifetime_watcher.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/lifetime_watcher.go index b1d81332f19..f775dfb15a7 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/lifetime_watcher.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/lifetime_watcher.go @@ -225,7 +225,7 @@ func (r *LifetimeWatcher) Start() { r.doneCh <- r.doRenew() } -// Renew is for comnpatibility with the legacy api.Renewer. Calling Renew +// Renew is for compatibility with the legacy api.Renewer. Calling Renew // simply chains to Start. func (r *LifetimeWatcher) Renew() { r.Start() diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/logical.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/logical.go index 68f3e910050..f7d2b4a4050 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/logical.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/logical.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "net/http" "net/url" "os" "strings" @@ -145,9 +144,7 @@ func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, erro func (c *Logical) JSONMergePatch(ctx context.Context, path string, data map[string]interface{}) (*Secret, error) { r := c.c.NewRequest("PATCH", "/v1/"+path) - r.Headers = http.Header{ - "Content-Type": []string{"application/merge-patch+json"}, - } + r.Headers.Set("Content-Type", "application/merge-patch+json") if err := r.SetJSONBody(data); err != nil { return nil, err } diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/output_string.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/output_string.go index 8b654ad8206..b30c06eeedd 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/output_string.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/output_string.go @@ -15,9 +15,11 @@ var LastOutputStringError *OutputStringError type OutputStringError struct { *retryablehttp.Request - TLSSkipVerify bool - parsingError error - parsedCurlString string + TLSSkipVerify bool + ClientCACert, ClientCAPath string + ClientCert, ClientKey string + parsingError error + parsedCurlString string } func (d *OutputStringError) Error() string { @@ -46,6 +48,22 @@ func (d *OutputStringError) parseRequest() { if d.Request.Method != "GET" { d.parsedCurlString = fmt.Sprintf("%s-X %s ", d.parsedCurlString, d.Request.Method) } + if d.ClientCACert != "" { + clientCACert := strings.Replace(d.ClientCACert, "'", "'\"'\"'", -1) + d.parsedCurlString = fmt.Sprintf("%s--cacert '%s' ", d.parsedCurlString, clientCACert) + } + if d.ClientCAPath != "" { + clientCAPath := strings.Replace(d.ClientCAPath, "'", "'\"'\"'", -1) + d.parsedCurlString = fmt.Sprintf("%s--capath '%s' ", d.parsedCurlString, clientCAPath) + } + if d.ClientCert != "" { + clientCert := strings.Replace(d.ClientCert, "'", "'\"'\"'", -1) + d.parsedCurlString = fmt.Sprintf("%s--cert '%s' ", d.parsedCurlString, clientCert) + } + if d.ClientKey != "" { + clientKey := strings.Replace(d.ClientKey, "'", "'\"'\"'", -1) + d.parsedCurlString = fmt.Sprintf("%s--key '%s' ", d.parsedCurlString, clientKey) + } for k, v := range d.Request.Header { for _, h := range v { if strings.ToLower(k) == "x-vault-token" { diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/plugin_helpers.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/plugin_helpers.go index c2978b388f6..9acd6a58a46 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/plugin_helpers.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/plugin_helpers.go @@ -182,7 +182,6 @@ func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error) Certificates: []tls.Certificate{cert}, ServerName: serverCert.Subject.CommonName, } - tlsConfig.BuildNameToCertificate() return tlsConfig, nil } diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/secret.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/secret.go index 64865d0ba1d..a3a288bf142 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/secret.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/secret.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/vault/sdk/helper/jsonutil" + "github.com/hashicorp/vault/sdk/logical" ) // Secret is the structure returned for every secret within Vault. @@ -297,6 +298,8 @@ type SecretAuth struct { LeaseDuration int `json:"lease_duration"` Renewable bool `json:"renewable"` + + MFARequirement *logical.MFARequirement `json:"mfa_requirement"` } // ParseSecret is used to parse a secret value from JSON from an io.Reader. diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/sys_mounts.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/sys_mounts.go index 1d68a1063fd..8a0c5b98547 100644 --- a/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/sys_mounts.go +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/vault/api/sys_mounts.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "time" "github.com/mitchellh/mapstructure" ) @@ -65,7 +66,31 @@ func (c *Sys) Unmount(path string) error { return err } +// Remount kicks off a remount operation, polls the status endpoint using +// the migration ID till either success or failure state is observed func (c *Sys) Remount(from, to string) error { + remountResp, err := c.StartRemount(from, to) + if err != nil { + return err + } + + for { + remountStatusResp, err := c.RemountStatus(remountResp.MigrationID) + if err != nil { + return err + } + if remountStatusResp.MigrationInfo.MigrationStatus == "success" { + return nil + } + if remountStatusResp.MigrationInfo.MigrationStatus == "failure" { + return fmt.Errorf("Failure! Error encountered moving mount %s to %s, with migration ID %s", from, to, remountResp.MigrationID) + } + time.Sleep(1 * time.Second) + } +} + +// StartRemount kicks off a mount migration and returns a response with the migration ID +func (c *Sys) StartRemount(from, to string) (*MountMigrationOutput, error) { body := map[string]interface{}{ "from": from, "to": to, @@ -73,16 +98,59 @@ func (c *Sys) Remount(from, to string) error { r := c.c.NewRequest("POST", "/v1/sys/remount") if err := r.SetJSONBody(body); err != nil { - return err + return nil, err } ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() resp, err := c.c.RawRequestWithContext(ctx, r) - if err == nil { - defer resp.Body.Close() + if err != nil { + return nil, err } - return err + defer resp.Body.Close() + secret, err := ParseSecret(resp.Body) + if err != nil { + return nil, err + } + if secret == nil || secret.Data == nil { + return nil, errors.New("data from server response is empty") + } + + var result MountMigrationOutput + err = mapstructure.Decode(secret.Data, &result) + if err != nil { + return nil, err + } + + return &result, err +} + +// RemountStatus checks the status of a mount migration operation with the provided ID +func (c *Sys) RemountStatus(migrationID string) (*MountMigrationStatusOutput, error) { + r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/remount/status/%s", migrationID)) + + ctx, cancelFunc := context.WithCancel(context.Background()) + defer cancelFunc() + resp, err := c.c.RawRequestWithContext(ctx, r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + secret, err := ParseSecret(resp.Body) + if err != nil { + return nil, err + } + if secret == nil || secret.Data == nil { + return nil, errors.New("data from server response is empty") + } + + var result MountMigrationStatusOutput + err = mapstructure.Decode(secret.Data, &result) + if err != nil { + return nil, err + } + + return &result, err } func (c *Sys) TuneMount(path string, config MountConfigInput) error { @@ -187,3 +255,18 @@ type MountConfigOutput struct { // Deprecated: This field will always be blank for newer server responses. PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` } + +type MountMigrationOutput struct { + MigrationID string `mapstructure:"migration_id"` +} + +type MountMigrationStatusOutput struct { + MigrationID string `mapstructure:"migration_id"` + MigrationInfo *MountMigrationStatusInfo `mapstructure:"migration_info"` +} + +type MountMigrationStatusInfo struct { + SourceMount string `mapstructure:"source_mount"` + TargetMount string `mapstructure:"target_mount"` + MigrationStatus string `mapstructure:"status"` +}