remotes: always try to establish tls connection when tls configured
When a endpoint is configured for http and has a tls configuration, always try to the tls connection and fallback to http when the tls connections fails from receiving an http response. This fixes an issue with default localhost endpoints which get defaulted to http with insecure tls also configured but are using tls. Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
@@ -121,8 +121,13 @@ func ConfigureHosts(ctx context.Context, options HostOptions) docker.RegistryHos
|
||||
hosts[len(hosts)-1].capabilities = docker.HostCapabilityPull | docker.HostCapabilityResolve | docker.HostCapabilityPush
|
||||
}
|
||||
|
||||
// explicitTLS indicates that TLS was explicitly configured and HTTP endpoints should
|
||||
// attempt to use the TLS configuration before falling back to HTTP
|
||||
var explicitTLS bool
|
||||
|
||||
var defaultTLSConfig *tls.Config
|
||||
if options.DefaultTLS != nil {
|
||||
explicitTLS = true
|
||||
defaultTLSConfig = options.DefaultTLS
|
||||
} else {
|
||||
defaultTLSConfig = &tls.Config{}
|
||||
@@ -160,14 +165,11 @@ func ConfigureHosts(ctx context.Context, options HostOptions) docker.RegistryHos
|
||||
|
||||
rhosts := make([]docker.RegistryHost, len(hosts))
|
||||
for i, host := range hosts {
|
||||
|
||||
rhosts[i].Scheme = host.scheme
|
||||
rhosts[i].Host = host.host
|
||||
rhosts[i].Path = host.path
|
||||
rhosts[i].Capabilities = host.capabilities
|
||||
rhosts[i].Header = host.header
|
||||
// Allow setting for each host as well
|
||||
explicitTLS := explicitTLS
|
||||
|
||||
if host.caCerts != nil || host.clientPairs != nil || host.skipVerify != nil {
|
||||
explicitTLS = true
|
||||
tr := defaultTransport.Clone()
|
||||
tlsConfig := tr.TLSClientConfig
|
||||
if host.skipVerify != nil {
|
||||
@@ -229,6 +231,21 @@ func ConfigureHosts(ctx context.Context, options HostOptions) docker.RegistryHos
|
||||
rhosts[i].Client = client
|
||||
rhosts[i].Authorizer = authorizer
|
||||
}
|
||||
|
||||
// When TLS has been explicitly configured for the operation or host, use the
|
||||
// docker.HTTPFallback roundtripper to catch TLS errors and re-attempt the request as http.
|
||||
// This allows preference for https when configured but also catches TLS errors early enough
|
||||
// in the request to avoid sending the request twice or consuming the request body.
|
||||
if host.scheme == "http" && explicitTLS {
|
||||
host.scheme = "https"
|
||||
rhosts[i].Client.Transport = docker.HTTPFallback{RoundTripper: rhosts[i].Client.Transport}
|
||||
}
|
||||
|
||||
rhosts[i].Scheme = host.scheme
|
||||
rhosts[i].Host = host.host
|
||||
rhosts[i].Path = host.path
|
||||
rhosts[i].Capabilities = host.capabilities
|
||||
rhosts[i].Header = host.header
|
||||
}
|
||||
|
||||
return rhosts, nil
|
||||
|
||||
@@ -19,6 +19,7 @@ package config
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -294,6 +295,30 @@ func TestLoadCertFiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalhostHTTPFallback(t *testing.T) {
|
||||
opts := HostOptions{
|
||||
DefaultTLS: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
hosts := ConfigureHosts(context.TODO(), opts)
|
||||
|
||||
testHosts, err := hosts("localhost:8080")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(testHosts) != 1 {
|
||||
t.Fatalf("expected a single host for localhost config, got %d hosts", len(testHosts))
|
||||
}
|
||||
if testHosts[0].Scheme != "https" {
|
||||
t.Fatalf("expected https scheme for localhost with tls config, got %q", testHosts[0].Scheme)
|
||||
}
|
||||
_, ok := testHosts[0].Client.Transport.(docker.HTTPFallback)
|
||||
if !ok {
|
||||
t.Fatal("expected http fallback configured for defaulted localhost endpoint")
|
||||
}
|
||||
}
|
||||
|
||||
func compareRegistryHost(j, k docker.RegistryHost) bool {
|
||||
if j.Scheme != k.Scheme {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user