From 80bb49cadfd458c08286e5636ad976f6408219c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Juul?= <449942+zpon@users.noreply.github.com> Date: Wed, 30 Oct 2024 08:27:54 +0000 Subject: [PATCH 1/5] Support for client certs towards registry server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the registry options handling in the `cmd/cosign/cli/options/registry.go` file. It introduces new flags for specifying the X.509 CA certificate, client certificate, client key, and server name for the connection to the registry. This allows cosign to connect to registries that requires mTLS for authentication. Signed-off-by: Søren Juul <449942+zpon@users.noreply.github.com> --- cmd/cosign/cli/options/registry.go | 61 +++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/cmd/cosign/cli/options/registry.go b/cmd/cosign/cli/options/registry.go index 61edb47d48b..27ed3be041e 100644 --- a/cmd/cosign/cli/options/registry.go +++ b/cmd/cosign/cli/options/registry.go @@ -17,10 +17,12 @@ package options import ( "context" "crypto/tls" + "crypto/x509" "errors" "fmt" "io" "net/http" + "os" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper" @@ -45,6 +47,10 @@ type RegistryOptions struct { RefOpts ReferenceOptions Keychain Keychain AuthConfig authn.AuthConfig + RegistryCACert string + RegistryClientCert string + RegistryClientKey string + RegistryServerName string // RegistryClientOpts allows overriding the result of GetRegistryClientOpts. RegistryClientOpts []remote.Option @@ -72,6 +78,18 @@ func (o *RegistryOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.AuthConfig.RegistryToken, "registry-token", "", "registry bearer auth token") + cmd.Flags().StringVar(&o.RegistryCACert, "registry-cacert", "", + "path to the X.509 CA certificate file in PEM format to be used for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryClientCert, "registry-client-cert", "", + "path to the X.509 certificate file in PEM format to be used for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryClientKey, "registry-client-key", "", + "path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryServerName, "registry-server-name", "", + "SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry") + o.RefOpts.AddFlags(cmd) } @@ -131,8 +149,9 @@ func (o *RegistryOptions) GetRegistryClientOpts(ctx context.Context) []remote.Op opts = append(opts, remote.WithAuthFromKeychain(authn.DefaultKeychain)) } - if o.AllowInsecure { - opts = append(opts, remote.WithTransport(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}})) // #nosec G402 + tlsConfig, err := o.getTLSConfig() + if err == nil { + opts = append(opts, remote.WithTransport(&http.Transport{TLSClientConfig: tlsConfig})) } // Reuse a remote.Pusher and a remote.Puller for all operations that use these opts. @@ -193,3 +212,41 @@ func (o *RegistryExperimentalOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().Var(&o.RegistryReferrersMode, "registry-referrers-mode", "mode for fetching references from the registry. allowed: legacy, oci-1-1") } + +func (o *RegistryOptions) getTLSConfig() (*tls.Config, error) { + var tlsConfig tls.Config + + if o.RegistryCACert != "" { + f, err := os.Open(o.RegistryCACert) + if err != nil { + return nil, err + } + defer f.Close() + caCertBytes, err := io.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("unable to read CA certs from %s: %w", o.RegistryCACert, err) + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(caCertBytes) { + return nil, fmt.Errorf("no valid CA certs found in %s", o.RegistryCACert) + } + tlsConfig.RootCAs = pool + } + + if o.RegistryClientCert != "" && o.RegistryClientKey != "" { + cert, err := tls.LoadX509KeyPair(o.RegistryClientCert, o.RegistryClientKey) + if err != nil { + return nil, fmt.Errorf("unable to read CA certs from cert %s, key %s: %w", + o.RegistryClientCert, o.RegistryClientKey, err) + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + + if o.RegistryServerName != "" { + tlsConfig.ServerName = o.RegistryServerName + } + + tlsConfig.InsecureSkipVerify = o.AllowInsecure // #nosec G402 + + return &tlsConfig, nil +} From e8f3422e2df0334b867cf6277ec935e2e772bd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Juul?= <449942+zpon@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:14:38 +0000 Subject: [PATCH 2/5] Update documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Søren Juul <449942+zpon@users.noreply.github.com> --- doc/cosign_attach_attestation.md | 4 ++++ doc/cosign_attach_sbom.md | 4 ++++ doc/cosign_attach_signature.md | 4 ++++ doc/cosign_attest.md | 4 ++++ doc/cosign_clean.md | 4 ++++ doc/cosign_copy.md | 4 ++++ doc/cosign_dockerfile_verify.md | 4 ++++ doc/cosign_download_attestation.md | 4 ++++ doc/cosign_download_sbom.md | 4 ++++ doc/cosign_download_signature.md | 4 ++++ doc/cosign_generate.md | 4 ++++ doc/cosign_load.md | 4 ++++ doc/cosign_manifest_verify.md | 4 ++++ doc/cosign_save.md | 4 ++++ doc/cosign_sign.md | 4 ++++ doc/cosign_tree.md | 4 ++++ doc/cosign_triangulate.md | 4 ++++ doc/cosign_upload_blob.md | 4 ++++ doc/cosign_upload_wasm.md | 4 ++++ doc/cosign_verify-attestation.md | 4 ++++ doc/cosign_verify.md | 4 ++++ 21 files changed, 84 insertions(+) diff --git a/doc/cosign_attach_attestation.md b/doc/cosign_attach_attestation.md index 8498ed21370..2c53d1d508e 100644 --- a/doc/cosign_attach_attestation.md +++ b/doc/cosign_attach_attestation.md @@ -29,7 +29,11 @@ cosign attach attestation [flags] --attestation stringArray path to the attestation envelope -h, --help help for attestation --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_attach_sbom.md b/doc/cosign_attach_sbom.md index 07948123883..f4d5cd846c5 100644 --- a/doc/cosign_attach_sbom.md +++ b/doc/cosign_attach_sbom.md @@ -27,8 +27,12 @@ cosign attach sbom [flags] -h, --help help for sbom --input-format string type of sbom input format (json|xml|text) --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password --registry-referrers-mode registryReferrersMode mode for fetching references from the registry. allowed: legacy, oci-1-1 + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --sbom string path to the sbom, or {-} for stdin diff --git a/doc/cosign_attach_signature.md b/doc/cosign_attach_signature.md index 21e96f4f589..a0355876e0e 100644 --- a/doc/cosign_attach_signature.md +++ b/doc/cosign_attach_signature.md @@ -40,7 +40,11 @@ cosign attach signature [flags] -h, --help help for signature --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --payload string path to the payload covered by the signature + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-response string path to the rekor bundle diff --git a/doc/cosign_attest.md b/doc/cosign_attest.md index 19e201291d6..d31fb8fd8a5 100644 --- a/doc/cosign_attest.md +++ b/doc/cosign_attest.md @@ -67,7 +67,11 @@ cosign attest [flags] --predicate string path to the predicate file. --record-creation-timestamp set the createdAt timestamp in the attestation artifact to the time it was created; by default, cosign sets this to the zero value -r, --recursive if a multi-arch image is specified, additionally sign each discrete image + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-entry-type string specifies the type to be used for a rekor entry upload. Options are intoto or dsse (default). (default "dsse") diff --git a/doc/cosign_clean.md b/doc/cosign_clean.md index bbefe406d2d..fba11ddce57 100644 --- a/doc/cosign_clean.md +++ b/doc/cosign_clean.md @@ -21,7 +21,11 @@ cosign clean [flags] -f, --force do not prompt for confirmation -h, --help help for clean --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --type CLEAN_TYPE a type of clean: (sbom is deprecated) (default all) diff --git a/doc/cosign_copy.md b/doc/cosign_copy.md index 5cce9b638d5..3ba371fa016 100644 --- a/doc/cosign_copy.md +++ b/doc/cosign_copy.md @@ -38,7 +38,11 @@ cosign copy [flags] --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --only string custom string array to only copy specific items, this flag is comma delimited. ex: --only=sbom,sign,att --platform string only copy container image and its signatures for a specific platform image + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --sig-only [DEPRECATED] only copy the image signature diff --git a/doc/cosign_dockerfile_verify.md b/doc/cosign_dockerfile_verify.md index aea4d4aadde..0b64d614cd6 100644 --- a/doc/cosign_dockerfile_verify.md +++ b/doc/cosign_dockerfile_verify.md @@ -81,7 +81,11 @@ cosign dockerfile verify [flags] -o, --output string output format for the signing image information (json|text) (default "json") --payload string payload path or remote URL --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") diff --git a/doc/cosign_download_attestation.md b/doc/cosign_download_attestation.md index c1e8a656d9d..30e768ed193 100644 --- a/doc/cosign_download_attestation.md +++ b/doc/cosign_download_attestation.md @@ -22,7 +22,11 @@ cosign download attestation [flags] --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --platform string download attestation for a specific platform image --predicate-type string download attestation with matching predicateType + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_download_sbom.md b/doc/cosign_download_sbom.md index 77bbaa8c2cf..03b18f3e00a 100644 --- a/doc/cosign_download_sbom.md +++ b/doc/cosign_download_sbom.md @@ -27,7 +27,11 @@ cosign download sbom [flags] -h, --help help for sbom --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --platform string download SBOM for a specific platform image + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_download_signature.md b/doc/cosign_download_signature.md index fc830089c4f..371a8699656 100644 --- a/doc/cosign_download_signature.md +++ b/doc/cosign_download_signature.md @@ -20,7 +20,11 @@ cosign download signature [flags] --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] -h, --help help for signature --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_generate.md b/doc/cosign_generate.md index 34b55f1daac..8996f56438a 100644 --- a/doc/cosign_generate.md +++ b/doc/cosign_generate.md @@ -36,7 +36,11 @@ cosign generate [flags] --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] -h, --help help for generate --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_load.md b/doc/cosign_load.md index 8051c461413..17412943270 100644 --- a/doc/cosign_load.md +++ b/doc/cosign_load.md @@ -25,7 +25,11 @@ cosign load [flags] --dir string path to directory where the signed image is stored on disk -h, --help help for load --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_manifest_verify.md b/doc/cosign_manifest_verify.md index dc1af148c48..4bd31816c1b 100644 --- a/doc/cosign_manifest_verify.md +++ b/doc/cosign_manifest_verify.md @@ -75,7 +75,11 @@ cosign manifest verify [flags] -o, --output string output format for the signing image information (json|text) (default "json") --payload string payload path or remote URL --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") diff --git a/doc/cosign_save.md b/doc/cosign_save.md index 53cc93eb419..fa6cd9590b2 100644 --- a/doc/cosign_save.md +++ b/doc/cosign_save.md @@ -25,7 +25,11 @@ cosign save [flags] --dir string path to dir where the signed image should be stored on disk -h, --help help for save --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_sign.md b/doc/cosign_sign.md index 9e28e10fd25..07c3bb98fa3 100644 --- a/doc/cosign_sign.md +++ b/doc/cosign_sign.md @@ -99,8 +99,12 @@ cosign sign [flags] --payload string path to a payload file to use rather than generating one --record-creation-timestamp set the createdAt timestamp in the signature artifact to the time it was created; by default, cosign sets this to the zero value -r, --recursive if a multi-arch image is specified, additionally sign each discrete image + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password --registry-referrers-mode registryReferrersMode mode for fetching references from the registry. allowed: legacy, oci-1-1 + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") diff --git a/doc/cosign_tree.md b/doc/cosign_tree.md index 6af48f1290b..53deab00abd 100644 --- a/doc/cosign_tree.md +++ b/doc/cosign_tree.md @@ -20,7 +20,11 @@ cosign tree [flags] --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] -h, --help help for tree --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_triangulate.md b/doc/cosign_triangulate.md index 07b41494843..875bc72c257 100644 --- a/doc/cosign_triangulate.md +++ b/doc/cosign_triangulate.md @@ -20,7 +20,11 @@ cosign triangulate [flags] --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] -h, --help help for triangulate --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --type string related attachment to triangulate (attestation|sbom|signature|digest), default signature (sbom is deprecated) (default "signature") diff --git a/doc/cosign_upload_blob.md b/doc/cosign_upload_blob.md index 0688d525624..59c0c7a1b6b 100644 --- a/doc/cosign_upload_blob.md +++ b/doc/cosign_upload_blob.md @@ -41,7 +41,11 @@ cosign upload blob [flags] -f, --files strings :[platform/arch] -h, --help help for blob --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_upload_wasm.md b/doc/cosign_upload_wasm.md index da99e0a8f96..15bc70a7f4c 100644 --- a/doc/cosign_upload_wasm.md +++ b/doc/cosign_upload_wasm.md @@ -21,7 +21,11 @@ cosign upload wasm [flags] -f, --file string path to the wasm file to upload -h, --help help for wasm --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username ``` diff --git a/doc/cosign_verify-attestation.md b/doc/cosign_verify-attestation.md index 9b747ab9ef0..d370f05cf52 100644 --- a/doc/cosign_verify-attestation.md +++ b/doc/cosign_verify-attestation.md @@ -85,7 +85,11 @@ cosign verify-attestation [flags] -o, --output string output format for the signing image information (json|text) (default "json") --policy strings specify CUE or Rego files with policies to be used for validation --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") diff --git a/doc/cosign_verify.md b/doc/cosign_verify.md index 0852f21fc81..de9e10dd4c1 100644 --- a/doc/cosign_verify.md +++ b/doc/cosign_verify.md @@ -102,7 +102,11 @@ cosign verify [flags] -o, --output string output format for the signing image information (json|text) (default "json") --payload string payload path or remote URL --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure + --registry-cacert string path to the X.509 CA certificate file in PEM format to be used for the connection to the registry + --registry-client-cert string path to the X.509 certificate file in PEM format to be used for the connection to the registry + --registry-client-key string path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry --registry-password string registry basic auth password + --registry-server-name string SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry --registry-token string registry bearer auth token --registry-username string registry basic auth username --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") From b5823de05709fb3a8fb249fb9b600838a8cee7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Juul?= <449942+zpon@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:02:22 +0000 Subject: [PATCH 3/5] Add registry_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase test coverage of `getTLSConfig` method. Signed-off-by: Søren Juul <449942+zpon@users.noreply.github.com> --- cmd/cosign/cli/options/registry.go | 2 +- cmd/cosign/cli/options/registry_test.go | 206 ++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 cmd/cosign/cli/options/registry_test.go diff --git a/cmd/cosign/cli/options/registry.go b/cmd/cosign/cli/options/registry.go index 27ed3be041e..2fd6312999b 100644 --- a/cmd/cosign/cli/options/registry.go +++ b/cmd/cosign/cli/options/registry.go @@ -236,7 +236,7 @@ func (o *RegistryOptions) getTLSConfig() (*tls.Config, error) { if o.RegistryClientCert != "" && o.RegistryClientKey != "" { cert, err := tls.LoadX509KeyPair(o.RegistryClientCert, o.RegistryClientKey) if err != nil { - return nil, fmt.Errorf("unable to read CA certs from cert %s, key %s: %w", + return nil, fmt.Errorf("unable to read client certs from cert %s, key %s: %w", o.RegistryClientCert, o.RegistryClientKey, err) } tlsConfig.Certificates = []tls.Certificate{cert} diff --git a/cmd/cosign/cli/options/registry_test.go b/cmd/cosign/cli/options/registry_test.go new file mode 100644 index 00000000000..54b113be5a8 --- /dev/null +++ b/cmd/cosign/cli/options/registry_test.go @@ -0,0 +1,206 @@ +package options + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "os" + "testing" + "time" +) + +func generatePrivateKey(t *testing.T) *rsa.PrivateKey { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + return privateKey +} + +func writePrivateKey(t *testing.T, privateKey *rsa.PrivateKey, fileLocation string) { + // Encode the private key to PEM format + privateKeyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + // Write the private key to the specified file + err := os.WriteFile(fileLocation, privateKeyPEM, 0600) + if err != nil { + t.Fatal(err) + } +} + +func generateCertificate(t *testing.T, isCa bool) (certficateLocation, privateKeyLocation string) { + certficateLocation = createTempFile(t) + privateKeyLocation = createTempFile(t) + + // Generate a private key for the CA + privateKey := generatePrivateKey(t) + + // Create a self-signed certificate for the CA + caTemplate := &x509.Certificate{ + Subject: pkix.Name{ + CommonName: "Test CA", + }, + NotBefore: time.Now().Add(time.Hour*-24), + NotAfter: time.Now().Add(time.Hour*24), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: isCa, + SerialNumber: big.NewInt(1337), + } + + caCertDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &privateKey.PublicKey, privateKey) + if err != nil { + t.Fatal(err) + } + + // Encode the CA certificate to PEM format + caCertPEM := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: caCertDER, + }) + + // Write the CA certificate to the specified file + err = os.WriteFile(certficateLocation, caCertPEM, 0644) + if err != nil { + t.Fatal(err) + } + + writePrivateKey(t, privateKey, privateKeyLocation) + + return certficateLocation, privateKeyLocation +} + + +func TestGetTLSConfig(t *testing.T) { + validCaCertificate, validCaKey := generateCertificate(t, true) + validClientCertificate, validClientKey := generateCertificate(t, false) + + t.Cleanup(func() { + removeTempFile(t, validCaCertificate) + removeTempFile(t, validCaKey) + removeTempFile(t, validClientCertificate) + removeTempFile(t, validClientKey) + }) + + tests := []struct { + name string + registryCACert string + registryClientCert string + registryClientKey string + registryServerName string + allowInsecure bool + expectError string + }{ + { + name: "Valid CA Cert, Client Cert and Key, Server Name, Allow Insecure", + registryCACert: validCaCertificate, + registryClientCert: validClientCertificate, + registryClientKey: validClientKey, + registryServerName: "example.com", + allowInsecure: true, + }, + { + name: "Wrong key for client cert", + registryCACert: validCaCertificate, + registryClientCert: validClientCertificate, + registryClientKey: validCaKey, // using ca key for client cert must fail + registryServerName: "example.com", + allowInsecure: true, + expectError: fmt.Sprintf("unable to read client certs from cert %s, key %s: tls: private key does not match public key", validClientCertificate, validCaKey), + }, + { + name: "Wrong ca key", + registryCACert: validClientKey, // using client key for ca cert must fail + registryClientCert: validClientCertificate, + registryClientKey: validClientKey, + registryServerName: "example.com", + allowInsecure: true, + expectError: fmt.Sprintf("no valid CA certs found in %s", validClientKey), + }, + { + name: "Invalid CA path", + registryCACert: "/not/existing/path/fooobar", // this path is not expected to exist + registryClientCert: validClientCertificate, + registryClientKey: validClientKey, + registryServerName: "example.com", + allowInsecure: true, + expectError: fmt.Sprintf("open /not/existing/path/fooobar: no such file or directory"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &RegistryOptions{ + RegistryCACert: tt.registryCACert, + RegistryClientCert: tt.registryClientCert, + RegistryClientKey: tt.registryClientKey, + RegistryServerName: tt.registryServerName, + AllowInsecure: tt.allowInsecure, + } + + tlsConfig, err := o.getTLSConfig() + if tt.expectError != "" { + if err == nil || err.Error() != tt.expectError { + t.Errorf("getTLSConfig()\nerror: \"%v\",\nexpectError: \"%v\"", err, tt.expectError) + return + } + } else { + if err != nil { + t.Errorf("getTLSConfig() error = %v, expectError %v", err, tt.expectError) + return + } + } + + if err == nil { + if tt.registryCACert != "" { + if tlsConfig.RootCAs == nil { + t.Errorf("Expected RootCAs to be set") + } + } + + if tt.registryClientCert != "" && tt.registryClientKey != "" { + if len(tlsConfig.Certificates) == 0 { + t.Errorf("Expected Certificates to be set") + } + } + + if tt.registryServerName != "" { + if tlsConfig.ServerName != tt.registryServerName { + t.Errorf("Expected ServerName to be %s, got %s", tt.registryServerName, tlsConfig.ServerName) + } + } + + if tt.allowInsecure { + if !tlsConfig.InsecureSkipVerify { + t.Errorf("Expected InsecureSkipVerify to be true") + } + } + } + }) + } +} + +// Helper function to create temporary files for testing +func createTempFile(t *testing.T) string { + tmpfile, err := os.CreateTemp("", "registry-test-") + if err != nil { + t.Fatal(err) + } + + return tmpfile.Name() +} + +// Helper function to remove temporary files after testing +func removeTempFile(t *testing.T, filename string) { + if err := os.Remove(filename); err != nil { + t.Fatal(err) + } +} From 4d12839465c659a8eff258eb8beadcd4644810a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Juul?= <449942+zpon@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:39:24 +0000 Subject: [PATCH 4/5] Fix unittests on win and linter errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Søren Juul <449942+zpon@users.noreply.github.com> --- cmd/cosign/cli/options/registry_test.go | 54 ++++++++++++++++--------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/cmd/cosign/cli/options/registry_test.go b/cmd/cosign/cli/options/registry_test.go index 54b113be5a8..4e74042085a 100644 --- a/cmd/cosign/cli/options/registry_test.go +++ b/cmd/cosign/cli/options/registry_test.go @@ -1,3 +1,17 @@ +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package options import ( @@ -9,6 +23,7 @@ import ( "fmt" "math/big" "os" + "strings" "testing" "time" ) @@ -48,12 +63,12 @@ func generateCertificate(t *testing.T, isCa bool) (certficateLocation, privateKe Subject: pkix.Name{ CommonName: "Test CA", }, - NotBefore: time.Now().Add(time.Hour*-24), - NotAfter: time.Now().Add(time.Hour*24), + NotBefore: time.Now().Add(time.Hour * -24), + NotAfter: time.Now().Add(time.Hour * 24), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, BasicConstraintsValid: true, IsCA: isCa, - SerialNumber: big.NewInt(1337), + SerialNumber: big.NewInt(1337), } caCertDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &privateKey.PublicKey, privateKey) @@ -78,7 +93,6 @@ func generateCertificate(t *testing.T, isCa bool) (certficateLocation, privateKe return certficateLocation, privateKeyLocation } - func TestGetTLSConfig(t *testing.T) { validCaCertificate, validCaKey := generateCertificate(t, true) validClientCertificate, validClientKey := generateCertificate(t, false) @@ -108,31 +122,31 @@ func TestGetTLSConfig(t *testing.T) { allowInsecure: true, }, { - name: "Wrong key for client cert", - registryCACert: validCaCertificate, + name: "Wrong key for client cert", + registryCACert: validCaCertificate, registryClientCert: validClientCertificate, - registryClientKey: validCaKey, // using ca key for client cert must fail + registryClientKey: validCaKey, // using ca key for client cert must fail registryServerName: "example.com", - allowInsecure: true, - expectError: fmt.Sprintf("unable to read client certs from cert %s, key %s: tls: private key does not match public key", validClientCertificate, validCaKey), + allowInsecure: true, + expectError: fmt.Sprintf("unable to read client certs from cert %s, key %s: tls: private key does not match public key", validClientCertificate, validCaKey), }, { - name: "Wrong ca key", - registryCACert: validClientKey, // using client key for ca cert must fail + name: "Wrong ca key", + registryCACert: validClientKey, // using client key for ca cert must fail registryClientCert: validClientCertificate, - registryClientKey: validClientKey, + registryClientKey: validClientKey, registryServerName: "example.com", - allowInsecure: true, - expectError: fmt.Sprintf("no valid CA certs found in %s", validClientKey), + allowInsecure: true, + expectError: fmt.Sprintf("no valid CA certs found in %s", validClientKey), }, { - name: "Invalid CA path", - registryCACert: "/not/existing/path/fooobar", // this path is not expected to exist + name: "Invalid CA path", + registryCACert: "/not/existing/path/fooobar", // this path is not expected to exist registryClientCert: validClientCertificate, - registryClientKey: validClientKey, + registryClientKey: validClientKey, registryServerName: "example.com", - allowInsecure: true, - expectError: fmt.Sprintf("open /not/existing/path/fooobar: no such file or directory"), + allowInsecure: true, + expectError: "open /not/existing/path/fooobar: ", // the error message is OS dependent }, } @@ -148,7 +162,7 @@ func TestGetTLSConfig(t *testing.T) { tlsConfig, err := o.getTLSConfig() if tt.expectError != "" { - if err == nil || err.Error() != tt.expectError { + if err == nil || !strings.HasPrefix(err.Error(), tt.expectError) { t.Errorf("getTLSConfig()\nerror: \"%v\",\nexpectError: \"%v\"", err, tt.expectError) return } From bab0180248b1d9170b44c0e8c08eaf1615ff663f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Juul?= <449942+zpon@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:39:32 +0000 Subject: [PATCH 5/5] Fix temp file creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Søren Juul <449942+zpon@users.noreply.github.com> --- cmd/cosign/cli/options/registry_test.go | 29 +++++++++---------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/cmd/cosign/cli/options/registry_test.go b/cmd/cosign/cli/options/registry_test.go index 4e74042085a..6f788dda012 100644 --- a/cmd/cosign/cli/options/registry_test.go +++ b/cmd/cosign/cli/options/registry_test.go @@ -51,9 +51,9 @@ func writePrivateKey(t *testing.T, privateKey *rsa.PrivateKey, fileLocation stri } } -func generateCertificate(t *testing.T, isCa bool) (certficateLocation, privateKeyLocation string) { - certficateLocation = createTempFile(t) - privateKeyLocation = createTempFile(t) +func generateCertificate(t *testing.T, dir string, isCa bool) (certficateLocation, privateKeyLocation string) { + certficateLocation = createTempFile(t, dir) + privateKeyLocation = createTempFile(t, dir) // Generate a private key for the CA privateKey := generatePrivateKey(t) @@ -94,15 +94,12 @@ func generateCertificate(t *testing.T, isCa bool) (certficateLocation, privateKe } func TestGetTLSConfig(t *testing.T) { - validCaCertificate, validCaKey := generateCertificate(t, true) - validClientCertificate, validClientKey := generateCertificate(t, false) - + tempDir := t.TempDir() // Create a temporary directory for testing t.Cleanup(func() { - removeTempFile(t, validCaCertificate) - removeTempFile(t, validCaKey) - removeTempFile(t, validClientCertificate) - removeTempFile(t, validClientKey) + os.RemoveAll(tempDir) }) + validCaCertificate, validCaKey := generateCertificate(t, tempDir, true) + validClientCertificate, validClientKey := generateCertificate(t, tempDir, false) tests := []struct { name string @@ -203,18 +200,12 @@ func TestGetTLSConfig(t *testing.T) { } // Helper function to create temporary files for testing -func createTempFile(t *testing.T) string { - tmpfile, err := os.CreateTemp("", "registry-test-") +func createTempFile(t *testing.T, dir string) string { + tmpfile, err := os.CreateTemp(dir, "registry-test-") if err != nil { t.Fatal(err) } + defer tmpfile.Close() return tmpfile.Name() } - -// Helper function to remove temporary files after testing -func removeTempFile(t *testing.T, filename string) { - if err := os.Remove(filename); err != nil { - t.Fatal(err) - } -}