123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- package registry
- import (
- "crypto/tls"
- "crypto/x509"
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "path"
- "strings"
- "time"
- log "github.com/Sirupsen/logrus"
- "github.com/docker/docker/utils"
- )
- var (
- ErrAlreadyExists = errors.New("Image already exists")
- ErrDoesNotExist = errors.New("Image does not exist")
- errLoginRequired = errors.New("Authentication is required.")
- )
- type TimeoutType uint32
- const (
- NoTimeout TimeoutType = iota
- ReceiveTimeout
- ConnectTimeout
- )
- func newClient(jar http.CookieJar, roots *x509.CertPool, certs []tls.Certificate, timeout TimeoutType, secure bool) *http.Client {
- tlsConfig := tls.Config{
- RootCAs: roots,
- // Avoid fallback to SSL protocols < TLS1.0
- MinVersion: tls.VersionTLS10,
- Certificates: certs,
- }
- if !secure {
- tlsConfig.InsecureSkipVerify = true
- }
- httpTransport := &http.Transport{
- DisableKeepAlives: true,
- Proxy: http.ProxyFromEnvironment,
- TLSClientConfig: &tlsConfig,
- }
- switch timeout {
- case ConnectTimeout:
- httpTransport.Dial = func(proto string, addr string) (net.Conn, error) {
- // Set the connect timeout to 5 seconds
- d := net.Dialer{Timeout: 5 * time.Second, DualStack: true}
- conn, err := d.Dial(proto, addr)
- if err != nil {
- return nil, err
- }
- // Set the recv timeout to 10 seconds
- conn.SetDeadline(time.Now().Add(10 * time.Second))
- return conn, nil
- }
- case ReceiveTimeout:
- httpTransport.Dial = func(proto string, addr string) (net.Conn, error) {
- d := net.Dialer{DualStack: true}
- conn, err := d.Dial(proto, addr)
- if err != nil {
- return nil, err
- }
- conn = utils.NewTimeoutConn(conn, 1*time.Minute)
- return conn, nil
- }
- }
- return &http.Client{
- Transport: httpTransport,
- CheckRedirect: AddRequiredHeadersToRedirectedRequests,
- Jar: jar,
- }
- }
- func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secure bool) (*http.Response, *http.Client, error) {
- var (
- pool *x509.CertPool
- certs []tls.Certificate
- )
- if secure && req.URL.Scheme == "https" {
- hasFile := func(files []os.FileInfo, name string) bool {
- for _, f := range files {
- if f.Name() == name {
- return true
- }
- }
- return false
- }
- hostDir := path.Join("/etc/docker/certs.d", req.URL.Host)
- log.Debugf("hostDir: %s", hostDir)
- fs, err := ioutil.ReadDir(hostDir)
- if err != nil && !os.IsNotExist(err) {
- return nil, nil, err
- }
- for _, f := range fs {
- if strings.HasSuffix(f.Name(), ".crt") {
- if pool == nil {
- pool = x509.NewCertPool()
- }
- log.Debugf("crt: %s", hostDir+"/"+f.Name())
- data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
- if err != nil {
- return nil, nil, err
- }
- pool.AppendCertsFromPEM(data)
- }
- if strings.HasSuffix(f.Name(), ".cert") {
- certName := f.Name()
- keyName := certName[:len(certName)-5] + ".key"
- log.Debugf("cert: %s", hostDir+"/"+f.Name())
- if !hasFile(fs, keyName) {
- return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
- }
- cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName))
- if err != nil {
- return nil, nil, err
- }
- certs = append(certs, cert)
- }
- if strings.HasSuffix(f.Name(), ".key") {
- keyName := f.Name()
- certName := keyName[:len(keyName)-4] + ".cert"
- log.Debugf("key: %s", hostDir+"/"+f.Name())
- if !hasFile(fs, certName) {
- return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
- }
- }
- }
- }
- if len(certs) == 0 {
- client := newClient(jar, pool, nil, timeout, secure)
- res, err := client.Do(req)
- if err != nil {
- return nil, nil, err
- }
- return res, client, nil
- }
- client := newClient(jar, pool, certs, timeout, secure)
- res, err := client.Do(req)
- return res, client, err
- }
- func trustedLocation(req *http.Request) bool {
- var (
- trusteds = []string{"docker.com", "docker.io"}
- hostname = strings.SplitN(req.Host, ":", 2)[0]
- )
- if req.URL.Scheme != "https" {
- return false
- }
- for _, trusted := range trusteds {
- if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) {
- return true
- }
- }
- return false
- }
- func AddRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
- if via != nil && via[0] != nil {
- if trustedLocation(req) && trustedLocation(via[0]) {
- req.Header = via[0].Header
- return nil
- }
- for k, v := range via[0].Header {
- if k != "Authorization" {
- for _, vv := range v {
- req.Header.Add(k, vv)
- }
- }
- }
- }
- return nil
- }
|