Files
moby/daemon/hosts.go
Sebastiaan van Stijn 2c000b8ac4 daemon: Daemon.RegistryHosts: use internal method to get daemon config
The Daemon.Config() option was added to expose the Config outside of the
daemon package. Limit the use of that function to that purpose and use
the internal, non-exported functions within the daemon itself; this makes
it easier to see if there's still external consumers of the Config()
method.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-28 16:32:51 +01:00

174 lines
4.6 KiB
Go

package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/containerd/containerd/remotes/docker"
hostconfig "github.com/containerd/containerd/remotes/docker/config"
cerrdefs "github.com/containerd/errdefs"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
)
const (
defaultPath = "/v2"
)
// RegistryHosts returns the registry hosts configuration for the host component
// of a distribution image reference.
func (daemon *Daemon) RegistryHosts(host string) ([]docker.RegistryHost, error) {
hosts, err := hostconfig.ConfigureHosts(context.Background(), hostconfig.HostOptions{
// TODO: Also check containerd path when updating containerd to use multiple host directories
HostDir: hostconfig.HostDirFromRoot(registry.CertsDir()),
})(host)
if err == nil {
// Merge in legacy configuration if provided
if cfg := daemon.config().Config; len(cfg.Mirrors) > 0 || len(cfg.InsecureRegistries) > 0 {
hosts, err = daemon.mergeLegacyConfig(host, hosts)
}
}
return hosts, err
}
func (daemon *Daemon) mergeLegacyConfig(host string, hosts []docker.RegistryHost) ([]docker.RegistryHost, error) {
// If no hosts provided, nothing to do.
// If multiple hosts provided, then a mirror configuration is already provided and
// should not overwrite with legacy config.
if len(hosts) == 0 || len(hosts) > 1 {
return hosts, nil
}
sc := daemon.registryService.ServiceConfig()
if host == "docker.io" && len(sc.Mirrors) > 0 {
hosts = mirrorsToRegistryHosts(sc.Mirrors, hosts[0])
}
hostDir := hostconfig.HostDirFromRoot(registry.CertsDir())
for i := range hosts {
t, ok := hosts[i].Client.Transport.(*http.Transport)
if !ok {
continue
}
if t.TLSClientConfig == nil {
certsDir, err := hostDir(host)
if err != nil && !cerrdefs.IsNotFound(err) {
return nil, err
} else if err == nil {
c, err := loadTLSConfig(certsDir)
if err != nil {
return nil, err
}
t.TLSClientConfig = c
}
}
if daemon.registryService.IsInsecureRegistry(hosts[i].Host) {
if t.TLSClientConfig == nil {
t.TLSClientConfig = &tls.Config{} //nolint: gosec // G402: TLS MinVersion too low.
}
t.TLSClientConfig.InsecureSkipVerify = true
hosts[i].Client.Transport = docker.NewHTTPFallback(hosts[i].Client.Transport)
}
}
return hosts, nil
}
func mirrorsToRegistryHosts(mirrors []string, dHost docker.RegistryHost) []docker.RegistryHost {
var mirrorHosts []docker.RegistryHost
for _, mirror := range mirrors {
h := dHost
h.Capabilities = docker.HostCapabilityPull | docker.HostCapabilityResolve
u, err := url.Parse(mirror)
if err != nil || u.Host == "" {
u, err = url.Parse(fmt.Sprintf("dummy://%s", mirror))
}
if err == nil && u.Host != "" {
h.Host = u.Host
h.Path = strings.TrimSuffix(u.Path, "/")
// For compatibility with legacy mirrors, ensure ends with /v2
// NOTE: Use newer configuration to completely override the path
if !strings.HasSuffix(h.Path, defaultPath) {
h.Path = path.Join(h.Path, defaultPath)
}
if u.Scheme != "dummy" {
h.Scheme = u.Scheme
}
} else {
h.Host = mirror
h.Path = defaultPath
}
mirrorHosts = append(mirrorHosts, h)
}
return append(mirrorHosts, dHost)
}
func loadTLSConfig(d string) (*tls.Config, error) {
fs, err := os.ReadDir(d)
if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrPermission) {
return nil, errors.WithStack(err)
}
type keyPair struct {
Certificate string
Key string
}
var (
rootCAs []string
keyPairs []keyPair
)
for _, f := range fs {
switch filepath.Ext(f.Name()) {
case ".crt":
rootCAs = append(rootCAs, filepath.Join(d, f.Name()))
case ".cert":
keyPairs = append(keyPairs, keyPair{
Certificate: filepath.Join(d, f.Name()),
Key: filepath.Join(d, strings.TrimSuffix(f.Name(), ".cert")+".key"),
})
}
}
tc := &tls.Config{
MinVersion: tls.VersionTLS12,
}
if len(rootCAs) > 0 {
systemPool, err := x509.SystemCertPool()
if err != nil {
if runtime.GOOS == "windows" {
systemPool = x509.NewCertPool()
} else {
return nil, errors.Wrapf(err, "unable to get system cert pool")
}
}
tc.RootCAs = systemPool
}
for _, p := range rootCAs {
dt, err := os.ReadFile(p)
if err != nil {
return nil, err
}
tc.RootCAs.AppendCertsFromPEM(dt)
}
for _, kp := range keyPairs {
cert, err := tls.LoadX509KeyPair(kp.Certificate, kp.Key)
if err != nil {
return nil, errors.Wrapf(err, "failed to load keypair for %s", kp.Certificate)
}
tc.Certificates = append(tc.Certificates, cert)
}
return tc, nil
}