github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/moby/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 "crypto/tls" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "os" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/docker/distribution/registry/client/transport" 17 "github.com/docker/go-connections/tlsconfig" 18 "github.com/sirupsen/logrus" 19 ) 20 21 var ( 22 // ErrAlreadyExists is an error returned if an image being pushed 23 // already exists on the remote side 24 ErrAlreadyExists = errors.New("Image already exists") 25 ) 26 27 // HostCertsDir returns the config directory for a specific host 28 func HostCertsDir(hostname string) (string, error) { 29 certsDir := CertsDir() 30 31 hostDir := filepath.Join(certsDir, cleanPath(hostname)) 32 33 return hostDir, nil 34 } 35 36 func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { 37 // PreferredServerCipherSuites should have no effect 38 tlsConfig := tlsconfig.ServerDefault() 39 40 tlsConfig.InsecureSkipVerify = !isSecure 41 42 if isSecure && CertsDir() != "" { 43 hostDir, err := HostCertsDir(hostname) 44 if err != nil { 45 return nil, err 46 } 47 48 logrus.Debugf("hostDir: %s", hostDir) 49 if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil { 50 return nil, err 51 } 52 } 53 54 return tlsConfig, nil 55 } 56 57 func hasFile(files []os.FileInfo, name string) bool { 58 for _, f := range files { 59 if f.Name() == name { 60 return true 61 } 62 } 63 return false 64 } 65 66 // ReadCertsDirectory reads the directory for TLS certificates 67 // including roots and certificate pairs and updates the 68 // provided TLS configuration. 69 func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error { 70 fs, err := ioutil.ReadDir(directory) 71 if err != nil && !os.IsNotExist(err) { 72 return err 73 } 74 75 for _, f := range fs { 76 if strings.HasSuffix(f.Name(), ".crt") { 77 if tlsConfig.RootCAs == nil { 78 systemPool, err := tlsconfig.SystemCertPool() 79 if err != nil { 80 return fmt.Errorf("unable to get system cert pool: %v", err) 81 } 82 tlsConfig.RootCAs = systemPool 83 } 84 logrus.Debugf("crt: %s", filepath.Join(directory, f.Name())) 85 data, err := ioutil.ReadFile(filepath.Join(directory, f.Name())) 86 if err != nil { 87 return err 88 } 89 tlsConfig.RootCAs.AppendCertsFromPEM(data) 90 } 91 if strings.HasSuffix(f.Name(), ".cert") { 92 certName := f.Name() 93 keyName := certName[:len(certName)-5] + ".key" 94 logrus.Debugf("cert: %s", filepath.Join(directory, f.Name())) 95 if !hasFile(fs, keyName) { 96 return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName) 97 } 98 cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName)) 99 if err != nil { 100 return err 101 } 102 tlsConfig.Certificates = append(tlsConfig.Certificates, cert) 103 } 104 if strings.HasSuffix(f.Name(), ".key") { 105 keyName := f.Name() 106 certName := keyName[:len(keyName)-4] + ".cert" 107 logrus.Debugf("key: %s", filepath.Join(directory, f.Name())) 108 if !hasFile(fs, certName) { 109 return fmt.Errorf("Missing client certificate %s for key %s", certName, keyName) 110 } 111 } 112 } 113 114 return nil 115 } 116 117 // Headers returns request modifiers with a User-Agent and metaHeaders 118 func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier { 119 modifiers := []transport.RequestModifier{} 120 if userAgent != "" { 121 modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{ 122 "User-Agent": []string{userAgent}, 123 })) 124 } 125 if metaHeaders != nil { 126 modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders)) 127 } 128 return modifiers 129 } 130 131 // HTTPClient returns an HTTP client structure which uses the given transport 132 // and contains the necessary headers for redirected requests 133 func HTTPClient(transport http.RoundTripper) *http.Client { 134 return &http.Client{ 135 Transport: transport, 136 CheckRedirect: addRequiredHeadersToRedirectedRequests, 137 } 138 } 139 140 func trustedLocation(req *http.Request) bool { 141 var ( 142 trusteds = []string{"docker.com", "docker.io"} 143 hostname = strings.SplitN(req.Host, ":", 2)[0] 144 ) 145 if req.URL.Scheme != "https" { 146 return false 147 } 148 149 for _, trusted := range trusteds { 150 if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) { 151 return true 152 } 153 } 154 return false 155 } 156 157 // addRequiredHeadersToRedirectedRequests adds the necessary redirection headers 158 // for redirected requests 159 func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error { 160 if len(via) != 0 && via[0] != nil { 161 if trustedLocation(req) && trustedLocation(via[0]) { 162 req.Header = via[0].Header 163 return nil 164 } 165 for k, v := range via[0].Header { 166 if k != "Authorization" { 167 for _, vv := range v { 168 req.Header.Add(k, vv) 169 } 170 } 171 } 172 } 173 return nil 174 } 175 176 // NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the 177 // default TLS configuration. 178 func NewTransport(tlsConfig *tls.Config) *http.Transport { 179 if tlsConfig == nil { 180 tlsConfig = tlsconfig.ServerDefault() 181 } 182 183 direct := &net.Dialer{ 184 Timeout: 30 * time.Second, 185 KeepAlive: 30 * time.Second, 186 DualStack: true, 187 } 188 189 base := &http.Transport{ 190 Proxy: http.ProxyFromEnvironment, 191 DialContext: direct.DialContext, 192 TLSHandshakeTimeout: 10 * time.Second, 193 TLSClientConfig: tlsConfig, 194 // TODO(dmcgowan): Call close idle connections when complete and use keep alive 195 DisableKeepAlives: true, 196 } 197 198 return base 199 }