github.com/ralexstokes/docker@v1.6.2/registry/registry.go (about) 1 package registry 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "os" 12 "path" 13 "strings" 14 "time" 15 16 log "github.com/Sirupsen/logrus" 17 "github.com/docker/docker/pkg/timeoutconn" 18 ) 19 20 var ( 21 ErrAlreadyExists = errors.New("Image already exists") 22 ErrDoesNotExist = errors.New("Image does not exist") 23 errLoginRequired = errors.New("Authentication is required.") 24 ) 25 26 type TimeoutType uint32 27 28 const ( 29 NoTimeout TimeoutType = iota 30 ReceiveTimeout 31 ConnectTimeout 32 ) 33 34 func newClient(jar http.CookieJar, roots *x509.CertPool, certs []tls.Certificate, timeout TimeoutType, secure bool) *http.Client { 35 tlsConfig := tls.Config{ 36 RootCAs: roots, 37 // Avoid fallback to SSL protocols < TLS1.0 38 MinVersion: tls.VersionTLS10, 39 Certificates: certs, 40 } 41 42 if !secure { 43 tlsConfig.InsecureSkipVerify = true 44 } 45 46 httpTransport := &http.Transport{ 47 DisableKeepAlives: true, 48 Proxy: http.ProxyFromEnvironment, 49 TLSClientConfig: &tlsConfig, 50 } 51 52 switch timeout { 53 case ConnectTimeout: 54 httpTransport.Dial = func(proto string, addr string) (net.Conn, error) { 55 // Set the connect timeout to 5 seconds 56 d := net.Dialer{Timeout: 5 * time.Second, DualStack: true} 57 58 conn, err := d.Dial(proto, addr) 59 if err != nil { 60 return nil, err 61 } 62 // Set the recv timeout to 10 seconds 63 conn.SetDeadline(time.Now().Add(10 * time.Second)) 64 return conn, nil 65 } 66 case ReceiveTimeout: 67 httpTransport.Dial = func(proto string, addr string) (net.Conn, error) { 68 d := net.Dialer{DualStack: true} 69 70 conn, err := d.Dial(proto, addr) 71 if err != nil { 72 return nil, err 73 } 74 conn = timeoutconn.New(conn, 1*time.Minute) 75 return conn, nil 76 } 77 } 78 79 return &http.Client{ 80 Transport: httpTransport, 81 CheckRedirect: AddRequiredHeadersToRedirectedRequests, 82 Jar: jar, 83 } 84 } 85 86 func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secure bool) (*http.Response, *http.Client, error) { 87 var ( 88 pool *x509.CertPool 89 certs []tls.Certificate 90 ) 91 92 if secure && req.URL.Scheme == "https" { 93 hasFile := func(files []os.FileInfo, name string) bool { 94 for _, f := range files { 95 if f.Name() == name { 96 return true 97 } 98 } 99 return false 100 } 101 102 hostDir := path.Join("/etc/docker/certs.d", req.URL.Host) 103 log.Debugf("hostDir: %s", hostDir) 104 fs, err := ioutil.ReadDir(hostDir) 105 if err != nil && !os.IsNotExist(err) { 106 return nil, nil, err 107 } 108 109 for _, f := range fs { 110 if strings.HasSuffix(f.Name(), ".crt") { 111 if pool == nil { 112 pool = x509.NewCertPool() 113 } 114 log.Debugf("crt: %s", hostDir+"/"+f.Name()) 115 data, err := ioutil.ReadFile(path.Join(hostDir, f.Name())) 116 if err != nil { 117 return nil, nil, err 118 } 119 pool.AppendCertsFromPEM(data) 120 } 121 if strings.HasSuffix(f.Name(), ".cert") { 122 certName := f.Name() 123 keyName := certName[:len(certName)-5] + ".key" 124 log.Debugf("cert: %s", hostDir+"/"+f.Name()) 125 if !hasFile(fs, keyName) { 126 return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName) 127 } 128 cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName)) 129 if err != nil { 130 return nil, nil, err 131 } 132 certs = append(certs, cert) 133 } 134 if strings.HasSuffix(f.Name(), ".key") { 135 keyName := f.Name() 136 certName := keyName[:len(keyName)-4] + ".cert" 137 log.Debugf("key: %s", hostDir+"/"+f.Name()) 138 if !hasFile(fs, certName) { 139 return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName) 140 } 141 } 142 } 143 } 144 145 if len(certs) == 0 { 146 client := newClient(jar, pool, nil, timeout, secure) 147 res, err := client.Do(req) 148 if err != nil { 149 return nil, nil, err 150 } 151 return res, client, nil 152 } 153 154 client := newClient(jar, pool, certs, timeout, secure) 155 res, err := client.Do(req) 156 return res, client, err 157 } 158 159 func trustedLocation(req *http.Request) bool { 160 var ( 161 trusteds = []string{"docker.com", "docker.io"} 162 hostname = strings.SplitN(req.Host, ":", 2)[0] 163 ) 164 if req.URL.Scheme != "https" { 165 return false 166 } 167 168 for _, trusted := range trusteds { 169 if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) { 170 return true 171 } 172 } 173 return false 174 } 175 176 func AddRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error { 177 if via != nil && via[0] != nil { 178 if trustedLocation(req) && trustedLocation(via[0]) { 179 req.Header = via[0].Header 180 return nil 181 } 182 for k, v := range via[0].Header { 183 if k != "Authorization" { 184 for _, vv := range v { 185 req.Header.Add(k, vv) 186 } 187 } 188 } 189 } 190 return nil 191 }