mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
174 lines
4.6 KiB
Go
174 lines
4.6 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/containerd/containerd/v2/core/remotes/docker"
|
|
hostconfig "github.com/containerd/containerd/v2/core/remotes/docker/config"
|
|
cerrdefs "github.com/containerd/errdefs"
|
|
"github.com/moby/moby/v2/daemon/pkg/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
|
|
}
|