github.com/sbward/docker@v1.4.2-0.20150114010528-c9dab702bed3/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/utils"
    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 = utils.NewTimeoutConn(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  }