package resolver import ( "crypto/tls" "crypto/x509" "io/ioutil" "net" "net/http" "os" "path/filepath" "runtime" "strings" "time" "github.com/containerd/containerd/remotes/docker" "github.com/moby/buildkit/cmd/buildkitd/config" "github.com/moby/buildkit/util/tracing" "github.com/pkg/errors" ) func fillInsecureOpts(host string, c config.RegistryConfig, h docker.RegistryHost) ([]docker.RegistryHost, error) { var hosts []docker.RegistryHost tc, err := loadTLSConfig(c) if err != nil { return nil, err } var isHTTP bool if c.PlainHTTP != nil && *c.PlainHTTP { isHTTP = true } if c.PlainHTTP == nil { if ok, _ := docker.MatchLocalhost(host); ok { isHTTP = true } } if isHTTP { h2 := h h2.Scheme = "http" hosts = append(hosts, h2) } if c.Insecure != nil && *c.Insecure { h2 := h transport := newDefaultTransport() transport.TLSClientConfig = tc h2.Client = &http.Client{ Transport: tracing.NewTransport(transport), } tc.InsecureSkipVerify = true hosts = append(hosts, h2) } if len(hosts) == 0 { transport := newDefaultTransport() transport.TLSClientConfig = tc h.Client = &http.Client{ Transport: tracing.NewTransport(transport), } hosts = append(hosts, h) } return hosts, nil } func loadTLSConfig(c config.RegistryConfig) (*tls.Config, error) { for _, d := range c.TLSConfigDir { fs, err := ioutil.ReadDir(d) if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrPermission) { return nil, errors.WithStack(err) } for _, f := range fs { if strings.HasSuffix(f.Name(), ".crt") { c.RootCAs = append(c.RootCAs, filepath.Join(d, f.Name())) } if strings.HasSuffix(f.Name(), ".cert") { c.KeyPairs = append(c.KeyPairs, config.TLSKeyPair{ Certificate: filepath.Join(d, f.Name()), Key: filepath.Join(d, strings.TrimSuffix(f.Name(), ".cert")+".key"), }) } } } tc := &tls.Config{} if len(c.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 c.RootCAs { dt, err := ioutil.ReadFile(p) if err != nil { return nil, errors.Wrapf(err, "failed to read %s", p) } tc.RootCAs.AppendCertsFromPEM(dt) } for _, kp := range c.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 } // NewRegistryConfig converts registry config to docker.RegistryHosts callback func NewRegistryConfig(m map[string]config.RegistryConfig) docker.RegistryHosts { return docker.Registries( func(host string) ([]docker.RegistryHost, error) { c, ok := m[host] if !ok { return nil, nil } var out []docker.RegistryHost for _, mirror := range c.Mirrors { h := docker.RegistryHost{ Scheme: "https", Client: newDefaultClient(), Host: mirror, Path: "/v2", Capabilities: docker.HostCapabilityPull | docker.HostCapabilityResolve, } hosts, err := fillInsecureOpts(mirror, m[mirror], h) if err != nil { return nil, err } out = append(out, hosts...) } if host == "docker.io" { host = "registry-1.docker.io" } h := docker.RegistryHost{ Scheme: "https", Client: newDefaultClient(), Host: host, Path: "/v2", Capabilities: docker.HostCapabilityPush | docker.HostCapabilityPull | docker.HostCapabilityResolve, } hosts, err := fillInsecureOpts(host, c, h) if err != nil { return nil, err } out = append(out, hosts...) return out, nil }, docker.ConfigureDefaultRegistries( docker.WithClient(newDefaultClient()), docker.WithPlainHTTP(docker.MatchLocalhost), ), ) } func newDefaultClient() *http.Client { return &http.Client{ Transport: tracing.NewTransport(newDefaultTransport()), } } // newDefaultTransport is for pull or push client // // NOTE: For push, there must disable http2 for https because the flow control // will limit data transfer. The net/http package doesn't provide http2 tunable // settings which limits push performance. // // REF: https://github.com/golang/go/issues/14077 func newDefaultTransport() *http.Transport { return &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 60 * time.Second, }).DialContext, MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 5 * time.Second, TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper), } }