c8d/resolver: Fallback to http for insecure registries

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2022-09-01 17:03:10 +02:00 committed by Djordje Lukic
parent c83fce86d4
commit 9032e6779d
4 changed files with 68 additions and 25 deletions

View file

@ -1,6 +1,9 @@
package containerd
import (
"net/http"
"strings"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
registrytypes "github.com/docker/docker/api/types/registry"
@ -9,10 +12,10 @@ import (
)
func (i *ImageService) newResolverFromAuthConfig(authConfig *registrytypes.AuthConfig) (remotes.Resolver, docker.StatusTracker) {
hostsFn := i.registryHosts.RegistryHosts()
hosts := hostsAuthorizerWrapper(hostsFn, authConfig)
tracker := docker.NewInMemoryTracker()
hostsFn := i.registryHosts.RegistryHosts()
hosts := hostsWrapper(hostsFn, authConfig, i.registryService)
return docker.NewResolver(docker.ResolverOptions{
Hosts: hosts,
@ -20,24 +23,29 @@ func (i *ImageService) newResolverFromAuthConfig(authConfig *registrytypes.AuthC
}), tracker
}
func hostsAuthorizerWrapper(hostsFn docker.RegistryHosts, authConfig *registrytypes.AuthConfig) docker.RegistryHosts {
return docker.RegistryHosts(func(n string) ([]docker.RegistryHost, error) {
func hostsWrapper(hostsFn docker.RegistryHosts, authConfig *registrytypes.AuthConfig, regService registry.Service) docker.RegistryHosts {
return func(n string) ([]docker.RegistryHost, error) {
hosts, err := hostsFn(n)
if err == nil {
for idx, host := range hosts {
if host.Authorizer == nil {
var opts []docker.AuthorizerOpt
if authConfig != nil {
opts = append(opts, authorizationCredsFromAuthConfig(*authConfig))
}
host.Authorizer = docker.NewDockerAuthorizer(opts...)
hosts[idx] = host
if err != nil {
return nil, err
}
for i := range hosts {
if hosts[i].Authorizer == nil {
var opts []docker.AuthorizerOpt
if authConfig != nil {
opts = append(opts, authorizationCredsFromAuthConfig(*authConfig))
}
hosts[i].Authorizer = docker.NewDockerAuthorizer(opts...)
isInsecure := regService.IsInsecureRegistry(hosts[i].Host)
if hosts[i].Client.Transport != nil && isInsecure {
hosts[i].Client.Transport = httpFallback{super: hosts[i].Client.Transport}
}
}
}
return hosts, err
})
return hosts, nil
}
}
func authorizationCredsFromAuthConfig(authConfig registrytypes.AuthConfig) docker.AuthorizerOpt {
@ -57,3 +65,20 @@ func authorizationCredsFromAuthConfig(authConfig registrytypes.AuthConfig) docke
return authConfig.Username, authConfig.Password, nil
})
}
type httpFallback struct {
super http.RoundTripper
}
func (f httpFallback) RoundTrip(r *http.Request) (*http.Response, error) {
resp, err := f.super.RoundTrip(r)
if err != nil {
if strings.Contains(err.Error(), "http: server gave HTTP response to HTTPS client") {
plain := r.Clone(r.Context())
plain.URL.Scheme = "http"
return http.DefaultTransport.RoundTrip(plain)
}
}
return resp, err
}

View file

@ -12,14 +12,16 @@ import (
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
)
// ImageService implements daemon.ImageService
type ImageService struct {
client *containerd.Client
snapshotter string
registryHosts RegistryHostsProvider
client *containerd.Client
snapshotter string
registryHosts RegistryHostsProvider
registryService registry.Service
}
type RegistryHostsProvider interface {
@ -27,11 +29,12 @@ type RegistryHostsProvider interface {
}
// NewService creates a new ImageService.
func NewService(c *containerd.Client, snapshotter string, hostsProvider RegistryHostsProvider) *ImageService {
func NewService(c *containerd.Client, snapshotter string, hostsProvider RegistryHostsProvider, registry registry.Service) *ImageService {
return &ImageService{
client: c,
snapshotter: snapshotter,
registryHosts: hostsProvider,
client: c,
snapshotter: snapshotter,
registryHosts: hostsProvider,
registryService: registry,
}
}

View file

@ -14,6 +14,7 @@ import (
"path"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
@ -177,6 +178,13 @@ func (daemon *Daemon) RegistryHosts() docker.RegistryHosts {
for _, v := range daemon.configStore.InsecureRegistries {
u, err := url.Parse(v)
if err != nil && !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
originalErr := err
u, err = url.Parse("http://" + v)
if err != nil {
err = originalErr
}
}
c := resolverconfig.RegistryConfig{}
if err == nil {
v = u.Host
@ -994,7 +1002,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
if err := configureKernelSecuritySupport(config, driverName); err != nil {
return nil, err
}
d.imageService = ctrd.NewService(d.containerdCli, driverName, d)
d.imageService = ctrd.NewService(d.containerdCli, driverName, d, d.registryService)
} else {
layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{
Root: config.Root,

View file

@ -26,6 +26,7 @@ type Service interface {
LoadAllowNondistributableArtifacts([]string) error
LoadMirrors([]string) error
LoadInsecureRegistries([]string) error
IsInsecureRegistry(string) bool
}
// defaultService is a registry service. It tracks configuration data such as a list
@ -232,3 +233,9 @@ func (s *defaultService) LookupPushEndpoints(hostname string) (endpoints []APIEn
}
return endpoints, err
}
// IsInsecureRegistry returns true if the registry at given host is configured as
// insecure registry.
func (s *defaultService) IsInsecureRegistry(host string) bool {
return !s.config.isSecureIndex(host)
}