github.com/moby/docker@v26.1.3+incompatible/registry/registry.go (about) 1 // Package registry contains client primitives to interact with a remote Docker registry. 2 package registry // import "github.com/docker/docker/registry" 3 4 import ( 5 "context" 6 "crypto/tls" 7 "net" 8 "net/http" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/containerd/log" 15 "github.com/docker/distribution/registry/client/transport" 16 "github.com/docker/go-connections/tlsconfig" 17 ) 18 19 // HostCertsDir returns the config directory for a specific host. 20 func HostCertsDir(hostname string) string { 21 return filepath.Join(CertsDir(), cleanPath(hostname)) 22 } 23 24 // newTLSConfig constructs a client TLS configuration based on server defaults 25 func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { 26 // PreferredServerCipherSuites should have no effect 27 tlsConfig := tlsconfig.ServerDefault() 28 29 tlsConfig.InsecureSkipVerify = !isSecure 30 31 if isSecure && CertsDir() != "" { 32 hostDir := HostCertsDir(hostname) 33 log.G(context.TODO()).Debugf("hostDir: %s", hostDir) 34 if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil { 35 return nil, err 36 } 37 } 38 39 return tlsConfig, nil 40 } 41 42 func hasFile(files []os.DirEntry, name string) bool { 43 for _, f := range files { 44 if f.Name() == name { 45 return true 46 } 47 } 48 return false 49 } 50 51 // ReadCertsDirectory reads the directory for TLS certificates 52 // including roots and certificate pairs and updates the 53 // provided TLS configuration. 54 func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error { 55 fs, err := os.ReadDir(directory) 56 if err != nil && !os.IsNotExist(err) { 57 return invalidParam(err) 58 } 59 60 for _, f := range fs { 61 if strings.HasSuffix(f.Name(), ".crt") { 62 if tlsConfig.RootCAs == nil { 63 systemPool, err := tlsconfig.SystemCertPool() 64 if err != nil { 65 return invalidParamWrapf(err, "unable to get system cert pool") 66 } 67 tlsConfig.RootCAs = systemPool 68 } 69 log.G(context.TODO()).Debugf("crt: %s", filepath.Join(directory, f.Name())) 70 data, err := os.ReadFile(filepath.Join(directory, f.Name())) 71 if err != nil { 72 return err 73 } 74 tlsConfig.RootCAs.AppendCertsFromPEM(data) 75 } 76 if strings.HasSuffix(f.Name(), ".cert") { 77 certName := f.Name() 78 keyName := certName[:len(certName)-5] + ".key" 79 log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, f.Name())) 80 if !hasFile(fs, keyName) { 81 return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName) 82 } 83 cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName)) 84 if err != nil { 85 return err 86 } 87 tlsConfig.Certificates = append(tlsConfig.Certificates, cert) 88 } 89 if strings.HasSuffix(f.Name(), ".key") { 90 keyName := f.Name() 91 certName := keyName[:len(keyName)-4] + ".cert" 92 log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, f.Name())) 93 if !hasFile(fs, certName) { 94 return invalidParamf("missing client certificate %s for key %s", certName, keyName) 95 } 96 } 97 } 98 99 return nil 100 } 101 102 // Headers returns request modifiers with a User-Agent and metaHeaders 103 func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier { 104 modifiers := []transport.RequestModifier{} 105 if userAgent != "" { 106 modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{ 107 "User-Agent": []string{userAgent}, 108 })) 109 } 110 if metaHeaders != nil { 111 modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders)) 112 } 113 return modifiers 114 } 115 116 // newTransport returns a new HTTP transport. If tlsConfig is nil, it uses the 117 // default TLS configuration. 118 func newTransport(tlsConfig *tls.Config) *http.Transport { 119 if tlsConfig == nil { 120 tlsConfig = tlsconfig.ServerDefault() 121 } 122 123 direct := &net.Dialer{ 124 Timeout: 30 * time.Second, 125 KeepAlive: 30 * time.Second, 126 } 127 128 return &http.Transport{ 129 Proxy: http.ProxyFromEnvironment, 130 DialContext: direct.DialContext, 131 TLSHandshakeTimeout: 10 * time.Second, 132 TLSClientConfig: tlsConfig, 133 // TODO(dmcgowan): Call close idle connections when complete and use keep alive 134 DisableKeepAlives: true, 135 } 136 }