Skip to content

Commit

Permalink
fix: Support docker certs.d host certs again (#313)
Browse files Browse the repository at this point in the history
This was supported by skopeo before migrating to crane so this PR reintroduces it
rather than leave a breaking change.
  • Loading branch information
jimmidyson authored Jan 17, 2023
1 parent 5fbf9ce commit 6ab5849
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 40 deletions.
6 changes: 3 additions & 3 deletions cmd/mindthegap/create/imagebundle/image_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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))
Expand Down
4 changes: 1 addition & 3 deletions cmd/mindthegap/importcmd/imagebundle/image_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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},
},
),
),
Expand Down
6 changes: 2 additions & 4 deletions cmd/mindthegap/push/helmbundle/helm_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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},
},
)

Expand Down
6 changes: 2 additions & 4 deletions cmd/mindthegap/push/imagebundle/image_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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},
},
)

Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
101 changes: 87 additions & 14 deletions images/httputils/configurable_tls_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
6 changes: 5 additions & 1 deletion images/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
9 changes: 4 additions & 5 deletions test/e2e/imagebundle/push_bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
},
},
Expand Down Expand Up @@ -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,
},
},
Expand Down
5 changes: 2 additions & 3 deletions test/e2e/imagebundle/serve_bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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,
},
},
Expand Down

0 comments on commit 6ab5849

Please sign in to comment.