123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- // Package registry contains client primitives to interact with a remote Docker registry.
- package registry
- import (
- "crypto/tls"
- "crypto/x509"
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "path/filepath"
- "strings"
- "time"
- "github.com/Sirupsen/logrus"
- "github.com/docker/distribution/registry/client/transport"
- "github.com/docker/go-connections/sockets"
- "github.com/docker/go-connections/tlsconfig"
- )
- var (
- // ErrAlreadyExists is an error returned if an image being pushed
- // already exists on the remote side
- ErrAlreadyExists = errors.New("Image already exists")
- )
- func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
- // PreferredServerCipherSuites should have no effect
- tlsConfig := tlsconfig.ServerDefault
- tlsConfig.InsecureSkipVerify = !isSecure
- if isSecure && CertsDir != "" {
- hostDir := filepath.Join(CertsDir, cleanPath(hostname))
- logrus.Debugf("hostDir: %s", hostDir)
- if err := ReadCertsDirectory(&tlsConfig, hostDir); err != nil {
- return nil, err
- }
- }
- return &tlsConfig, nil
- }
- func hasFile(files []os.FileInfo, name string) bool {
- for _, f := range files {
- if f.Name() == name {
- return true
- }
- }
- return false
- }
- // ReadCertsDirectory reads the directory for TLS certificates
- // including roots and certificate pairs and updates the
- // provided TLS configuration.
- func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
- fs, err := ioutil.ReadDir(directory)
- if err != nil && !os.IsNotExist(err) {
- return err
- }
- for _, f := range fs {
- if strings.HasSuffix(f.Name(), ".crt") {
- if tlsConfig.RootCAs == nil {
- // TODO(dmcgowan): Copy system pool
- tlsConfig.RootCAs = x509.NewCertPool()
- }
- logrus.Debugf("crt: %s", filepath.Join(directory, f.Name()))
- data, err := ioutil.ReadFile(filepath.Join(directory, f.Name()))
- if err != nil {
- return err
- }
- tlsConfig.RootCAs.AppendCertsFromPEM(data)
- }
- if strings.HasSuffix(f.Name(), ".cert") {
- certName := f.Name()
- keyName := certName[:len(certName)-5] + ".key"
- logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
- if !hasFile(fs, keyName) {
- return fmt.Errorf("Missing key %s for client certificate %s. Note that CA certificates should use the extension .crt.", keyName, certName)
- }
- cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
- if err != nil {
- return err
- }
- tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
- }
- if strings.HasSuffix(f.Name(), ".key") {
- keyName := f.Name()
- certName := keyName[:len(keyName)-4] + ".cert"
- logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
- if !hasFile(fs, certName) {
- return fmt.Errorf("Missing client certificate %s for key %s", certName, keyName)
- }
- }
- }
- return nil
- }
- // DockerHeaders returns request modifiers with a User-Agent and metaHeaders
- func DockerHeaders(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
- modifiers := []transport.RequestModifier{}
- if userAgent != "" {
- modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
- "User-Agent": []string{userAgent},
- }))
- }
- if metaHeaders != nil {
- modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
- }
- return modifiers
- }
- // HTTPClient returns an HTTP client structure which uses the given transport
- // and contains the necessary headers for redirected requests
- func HTTPClient(transport http.RoundTripper) *http.Client {
- return &http.Client{
- Transport: transport,
- CheckRedirect: addRequiredHeadersToRedirectedRequests,
- }
- }
- 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
- }
- // addRequiredHeadersToRedirectedRequests adds the necessary redirection headers
- // for redirected requests
- 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
- }
- // NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
- // default TLS configuration.
- func NewTransport(tlsConfig *tls.Config) *http.Transport {
- if tlsConfig == nil {
- var cfg = tlsconfig.ServerDefault
- tlsConfig = &cfg
- }
- direct := &net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- DualStack: true,
- }
- base := &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- Dial: direct.Dial,
- TLSHandshakeTimeout: 10 * time.Second,
- TLSClientConfig: tlsConfig,
- // TODO(dmcgowan): Call close idle connections when complete and use keep alive
- DisableKeepAlives: true,
- }
- proxyDialer, err := sockets.DialerFromEnvironment(direct)
- if err == nil {
- base.Dial = proxyDialer.Dial
- }
- return base
- }
|