diff --git a/cmd/mindthegap/create/imagebundle/image_bundle.go b/cmd/mindthegap/create/imagebundle/image_bundle.go index 7d32deb5..09dd9389 100644 --- a/cmd/mindthegap/create/imagebundle/image_bundle.go +++ b/cmd/mindthegap/create/imagebundle/image_bundle.go @@ -14,7 +14,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/spf13/cobra" - "k8s.io/client-go/transport" "github.com/mesosphere/dkp-cli-runtime/core/output" @@ -118,8 +117,9 @@ func NewCommand(out output.Output) *cobra.Command { var remoteOpts []remote.Option if registryConfig.TLSVerify != nil && !*registryConfig.TLSVerify { transport := httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, - httputils.TLSHostsConfig{registryName: transport.TLSConfig{Insecure: true}}, + httputils.TLSHostsConfig{ + registryName: httputils.TLSHostConfig{Insecure: true}, + }, ) remoteOpts = append(remoteOpts, remote.WithTransport(transport)) diff --git a/cmd/mindthegap/importcmd/imagebundle/image_bundle.go b/cmd/mindthegap/importcmd/imagebundle/image_bundle.go index 4613da1b..3889a626 100644 --- a/cmd/mindthegap/importcmd/imagebundle/image_bundle.go +++ b/cmd/mindthegap/importcmd/imagebundle/image_bundle.go @@ -15,7 +15,6 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/spf13/cobra" - "k8s.io/client-go/transport" "github.com/mesosphere/dkp-cli-runtime/core/output" @@ -98,9 +97,8 @@ func NewCommand(out output.Output) *cobra.Command { ref, remote.WithTransport( httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - reg.Address(): transport.TLSConfig{Insecure: true}, + reg.Address(): httputils.TLSHostConfig{Insecure: true}, }, ), ), diff --git a/cmd/mindthegap/push/helmbundle/helm_bundle.go b/cmd/mindthegap/push/helmbundle/helm_bundle.go index e1d6ec75..e0b907ba 100644 --- a/cmd/mindthegap/push/helmbundle/helm_bundle.go +++ b/cmd/mindthegap/push/helmbundle/helm_bundle.go @@ -13,7 +13,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/spf13/cobra" - "k8s.io/client-go/transport" "github.com/mesosphere/dkp-cli-runtime/core/output" @@ -85,13 +84,12 @@ func NewCommand(out output.Output) *cobra.Command { insecure := flags.SkipTLSVerify(destRegistrySkipTLSVerify, destRegistryURI) if insecure || destRegistryCACertificateFile != "" { transport := httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - destRegistryURI.Host(): transport.TLSConfig{ + destRegistryURI.Host(): httputils.TLSHostConfig{ Insecure: insecure, CAFile: destRegistryCACertificateFile, }, - reg.Address(): transport.TLSConfig{Insecure: true}, + reg.Address(): httputils.TLSHostConfig{Insecure: true}, }, ) diff --git a/cmd/mindthegap/push/imagebundle/image_bundle.go b/cmd/mindthegap/push/imagebundle/image_bundle.go index 5a565973..6a70c033 100644 --- a/cmd/mindthegap/push/imagebundle/image_bundle.go +++ b/cmd/mindthegap/push/imagebundle/image_bundle.go @@ -13,7 +13,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/spf13/cobra" - "k8s.io/client-go/transport" "github.com/mesosphere/dkp-cli-runtime/core/output" @@ -86,13 +85,12 @@ func NewCommand(out output.Output) *cobra.Command { insecure := flags.SkipTLSVerify(destRegistrySkipTLSVerify, destRegistryURI) if insecure || destRegistryCACertificateFile != "" { transport := httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - destRegistryURI.Host(): transport.TLSConfig{ + destRegistryURI.Host(): httputils.TLSHostConfig{ Insecure: insecure, CAFile: destRegistryCACertificateFile, }, - reg.Address(): transport.TLSConfig{Insecure: true}, + reg.Address(): httputils.TLSHostConfig{Insecure: true}, }, ) diff --git a/go.mod b/go.mod index e2dcb393..658f3713 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,10 @@ require ( github.com/aws/aws-sdk-go-v2/service/ecr v1.18.0 github.com/containers/image/v5 v5.23.1 github.com/distribution/distribution/v3 v3.0.0-20220907155224-78b9c98c5c31 + github.com/docker/cli v20.10.22+incompatible github.com/docker/docker v20.10.22+incompatible github.com/docker/docker-credential-helpers v0.7.0 + github.com/docker/go-connections v0.4.0 github.com/google/go-containerregistry v0.12.1 github.com/hashicorp/go-getter v1.6.2 github.com/mesosphere/dkp-cli-runtime/core v0.7.1 @@ -27,7 +29,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 k8s.io/apimachinery v0.26.0 - k8s.io/client-go v0.26.0 k8s.io/klog/v2 v2.80.1 k8s.io/utils v0.0.0-20221107191617-1a15be271d1d ) @@ -66,9 +67,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.22+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -185,6 +184,7 @@ require ( k8s.io/apiextensions-apiserver v0.25.2 // indirect k8s.io/apiserver v0.25.2 // indirect k8s.io/cli-runtime v0.25.2 // indirect + k8s.io/client-go v0.26.0 // indirect k8s.io/component-base v0.25.2 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/kubectl v0.25.2 // indirect diff --git a/images/httputils/configurable_tls_transport.go b/images/httputils/configurable_tls_transport.go index 00b8afcc..93813926 100644 --- a/images/httputils/configurable_tls_transport.go +++ b/images/httputils/configurable_tls_transport.go @@ -4,45 +4,118 @@ package httputils import ( + "fmt" "net/http" + "os" + "path/filepath" + "strings" - "k8s.io/client-go/transport" + "github.com/docker/cli/cli/config" + "github.com/docker/docker/registry" + "github.com/docker/go-connections/tlsconfig" + "github.com/google/go-containerregistry/pkg/logs" + "github.com/google/go-containerregistry/pkg/v1/remote" ) type configurableTLSTransport struct { cfg TLSHostsConfig - delegateTransport http.RoundTripper + delegateTransport *http.Transport } func (rt *configurableTLSTransport) RoundTrip(req *http.Request) (*http.Response, error) { + tr := rt.delegateTransport.Clone() + + if tr.TLSClientConfig.RootCAs == nil { + systemPool, err := tlsconfig.SystemCertPool() + if err != nil { + return nil, fmt.Errorf("unable to get system cert pool: %v", err) + } + tr.TLSClientConfig.RootCAs = systemPool + } else { + tr.TLSClientConfig.RootCAs = tr.TLSClientConfig.RootCAs.Clone() + } + host := req.Host if host == "" { host = req.URL.Host } - if tlsConfig, ok := rt.cfg[host]; ok { - t, err := transport.New( - &transport.Config{ - TLS: tlsConfig, - }, - ) + tlsHostConfig, tlsHostConfigFound := rt.cfg[host] + + // Always returns nil error... + hostDockerCertsDir, _ := registry.HostCertsDir(host) + fs, err := os.ReadDir(hostDockerCertsDir) + if err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("failed to read from Docker registry certs: %w", err) + } + + for _, f := range fs { + if strings.HasSuffix(f.Name(), ".crt") { + data, err := os.ReadFile(filepath.Join(hostDockerCertsDir, f.Name())) + if err != nil { + return nil, fmt.Errorf("failed tp read CA certificate from Docker certs.d: %w", err) + } + _ = tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(data) + } + } + + tr.TLSClientConfig.InsecureSkipVerify = tlsHostConfigFound && tlsHostConfig.Insecure + + if tlsHostConfigFound && tlsHostConfig.CAFile != "" { + b, err := os.ReadFile(tlsHostConfig.CAFile) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read specified CA file: %w", err) } - return t.RoundTrip(req) + _ = tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(b) } - return rt.delegateTransport.RoundTrip(req) + return tr.RoundTrip(req) } -type TLSHostsConfig map[string]transport.TLSConfig +type TLSHostConfig struct { + CAFile string // Path of the PEM-encoded server trusted root certificates. + Insecure bool // Server should be accessed without verifying the certificate. For testing only. +} + +type TLSHostsConfig map[string]TLSHostConfig func NewConfigurableTLSRoundTripper( - delegate http.RoundTripper, cfg TLSHostsConfig, ) http.RoundTripper { + var tr http.RoundTripper = remote.DefaultTransport.(*http.Transport).Clone() + + // Add any http headers if they are set in the config file. + cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + logs.Debug.Printf("failed to read config file: %v", err) + } else if len(cf.HTTPHeaders) != 0 { + tr = &headerTransport{ + inner: tr, + httpHeaders: cf.HTTPHeaders, + } + } + return &configurableTLSTransport{ cfg: cfg, - delegateTransport: delegate, + delegateTransport: tr.(*http.Transport), + } +} + +// headerTransport sets headers on outgoing requests. +type headerTransport struct { + httpHeaders map[string]string + inner http.RoundTripper +} + +// RoundTrip implements http.RoundTripper. +func (ht *headerTransport) RoundTrip(in *http.Request) (*http.Response, error) { + for k, v := range ht.httpHeaders { + if http.CanonicalHeaderKey(k) == "User-Agent" { + // Docker sets this, which is annoying, since we're not docker. + // We might want to revisit completely ignoring this. + continue + } + in.Header.Set(k, v) } + return ht.inner.RoundTrip(in) } diff --git a/images/manifest.go b/images/manifest.go index 469a6169..1dd13352 100644 --- a/images/manifest.go +++ b/images/manifest.go @@ -29,7 +29,11 @@ func ManifestListForImage( if err != nil { localImage, localErr := daemon.Image(ref) if localErr != nil { - return nil, fmt.Errorf("failed to read image descriptor for %q from registry: %w", img, err) + return nil, fmt.Errorf( + "failed to read image descriptor for %q from registry: %w", + img, + err, + ) } return indexForSinglePlatformImage(ref, localImage, platforms...) diff --git a/test/e2e/imagebundle/push_bundle_test.go b/test/e2e/imagebundle/push_bundle_test.go index 43bb4839..a761c8ca 100644 --- a/test/e2e/imagebundle/push_bundle_test.go +++ b/test/e2e/imagebundle/push_bundle_test.go @@ -19,7 +19,6 @@ import ( . "github.com/onsi/gomega" "github.com/phayes/freeport" "github.com/spf13/cobra" - "k8s.io/client-go/transport" pushimagebundle "github.com/mesosphere/mindthegap/cmd/mindthegap/push/imagebundle" "github.com/mesosphere/mindthegap/docker/registry" @@ -153,8 +152,8 @@ var _ = Describe("Push Bundle", func() { }}, remote.WithTransport( httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): transport.TLSConfig{ + httputils.TLSHostsConfig{ + net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): httputils.TLSHostConfig{ CAFile: caCertFile, }, }, @@ -227,8 +226,8 @@ var _ = Describe("Push Bundle", func() { }}, remote.WithTransport( httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): transport.TLSConfig{ + httputils.TLSHostsConfig{ + net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): httputils.TLSHostConfig{ CAFile: caCertFile, }, }, diff --git a/test/e2e/imagebundle/serve_bundle_test.go b/test/e2e/imagebundle/serve_bundle_test.go index 62702524..79236144 100644 --- a/test/e2e/imagebundle/serve_bundle_test.go +++ b/test/e2e/imagebundle/serve_bundle_test.go @@ -18,7 +18,6 @@ import ( . "github.com/onsi/gomega" "github.com/phayes/freeport" "github.com/spf13/cobra" - "k8s.io/client-go/transport" "github.com/mesosphere/dkp-cli-runtime/core/output" @@ -137,8 +136,8 @@ var _ = Describe("Serve Bundle", func() { }}, remote.WithTransport( httputils.NewConfigurableTLSRoundTripper( - remote.DefaultTransport, httputils.TLSHostsConfig{ - net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): transport.TLSConfig{ + httputils.TLSHostsConfig{ + net.JoinHostPort(ipAddr.String(), strconv.Itoa(port)): httputils.TLSHostConfig{ CAFile: caCertFile, }, },