|
@@ -19,6 +19,7 @@ import (
|
|
|
"github.com/docker/docker/autogen/dockerversion"
|
|
|
"github.com/docker/docker/pkg/parsers/kernel"
|
|
|
"github.com/docker/docker/pkg/timeoutconn"
|
|
|
+ "github.com/docker/docker/pkg/transport"
|
|
|
"github.com/docker/docker/pkg/useragent"
|
|
|
)
|
|
|
|
|
@@ -36,17 +37,32 @@ const (
|
|
|
ConnectTimeout
|
|
|
)
|
|
|
|
|
|
-type httpsTransport struct {
|
|
|
- *http.Transport
|
|
|
+// dockerUserAgent is the User-Agent the Docker client uses to identify itself.
|
|
|
+// It is populated on init(), comprising version information of different components.
|
|
|
+var dockerUserAgent string
|
|
|
+
|
|
|
+func init() {
|
|
|
+ httpVersion := make([]useragent.VersionInfo, 0, 6)
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
|
|
+ if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
|
|
|
+ }
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
|
|
|
+ httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
|
|
|
+
|
|
|
+ dockerUserAgent = useragent.AppendVersions("", httpVersion...)
|
|
|
}
|
|
|
|
|
|
+type httpsRequestModifier struct{ tlsConfig *tls.Config }
|
|
|
+
|
|
|
// DRAGONS(tiborvass): If someone wonders why do we set tlsconfig in a roundtrip,
|
|
|
// it's because it's so as to match the current behavior in master: we generate the
|
|
|
// certpool on every-goddam-request. It's not great, but it allows people to just put
|
|
|
// the certs in /etc/docker/certs.d/.../ and let docker "pick it up" immediately. Would
|
|
|
// prefer an fsnotify implementation, but that was out of scope of my refactoring.
|
|
|
-// TODO: improve things
|
|
|
-func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
+func (m *httpsRequestModifier) ModifyRequest(req *http.Request) error {
|
|
|
var (
|
|
|
roots *x509.CertPool
|
|
|
certs []tls.Certificate
|
|
@@ -66,7 +82,7 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
logrus.Debugf("hostDir: %s", hostDir)
|
|
|
fs, err := ioutil.ReadDir(hostDir)
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
- return nil, err
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
for _, f := range fs {
|
|
@@ -77,7 +93,7 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
logrus.Debugf("crt: %s", hostDir+"/"+f.Name())
|
|
|
data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
|
|
|
if err != nil {
|
|
|
- return nil, err
|
|
|
+ return err
|
|
|
}
|
|
|
roots.AppendCertsFromPEM(data)
|
|
|
}
|
|
@@ -86,11 +102,11 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
keyName := certName[:len(certName)-5] + ".key"
|
|
|
logrus.Debugf("cert: %s", hostDir+"/"+f.Name())
|
|
|
if !hasFile(fs, keyName) {
|
|
|
- return nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
|
|
|
+ return 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, err
|
|
|
+ return err
|
|
|
}
|
|
|
certs = append(certs, cert)
|
|
|
}
|
|
@@ -99,38 +115,32 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
certName := keyName[:len(keyName)-4] + ".cert"
|
|
|
logrus.Debugf("key: %s", hostDir+"/"+f.Name())
|
|
|
if !hasFile(fs, certName) {
|
|
|
- return nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
|
|
+ return fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if tr.Transport.TLSClientConfig == nil {
|
|
|
- tr.Transport.TLSClientConfig = &tls.Config{
|
|
|
- // Avoid fallback to SSL protocols < TLS1.0
|
|
|
- MinVersion: tls.VersionTLS10,
|
|
|
- }
|
|
|
- }
|
|
|
- tr.Transport.TLSClientConfig.RootCAs = roots
|
|
|
- tr.Transport.TLSClientConfig.Certificates = certs
|
|
|
+ m.tlsConfig.RootCAs = roots
|
|
|
+ m.tlsConfig.Certificates = certs
|
|
|
}
|
|
|
- return tr.Transport.RoundTrip(req)
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
|
|
- tlsConfig := tls.Config{
|
|
|
+ tlsConfig := &tls.Config{
|
|
|
// Avoid fallback to SSL protocols < TLS1.0
|
|
|
MinVersion: tls.VersionTLS10,
|
|
|
InsecureSkipVerify: !secure,
|
|
|
}
|
|
|
|
|
|
- transport := &http.Transport{
|
|
|
+ tr := &http.Transport{
|
|
|
DisableKeepAlives: true,
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
- TLSClientConfig: &tlsConfig,
|
|
|
+ TLSClientConfig: tlsConfig,
|
|
|
}
|
|
|
|
|
|
switch timeout {
|
|
|
case ConnectTimeout:
|
|
|
- transport.Dial = func(proto string, addr string) (net.Conn, error) {
|
|
|
+ tr.Dial = func(proto string, addr string) (net.Conn, error) {
|
|
|
// Set the connect timeout to 30 seconds to allow for slower connection
|
|
|
// times...
|
|
|
d := net.Dialer{Timeout: 30 * time.Second, DualStack: true}
|
|
@@ -144,7 +154,7 @@ func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
|
|
return conn, nil
|
|
|
}
|
|
|
case ReceiveTimeout:
|
|
|
- transport.Dial = func(proto string, addr string) (net.Conn, error) {
|
|
|
+ tr.Dial = func(proto string, addr string) (net.Conn, error) {
|
|
|
d := net.Dialer{DualStack: true}
|
|
|
|
|
|
conn, err := d.Dial(proto, addr)
|
|
@@ -159,51 +169,23 @@ func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
|
|
if secure {
|
|
|
// note: httpsTransport also handles http transport
|
|
|
// but for HTTPS, it sets up the certs
|
|
|
- return &httpsTransport{transport}
|
|
|
+ return transport.NewTransport(tr, &httpsRequestModifier{tlsConfig})
|
|
|
}
|
|
|
|
|
|
- return transport
|
|
|
+ return tr
|
|
|
}
|
|
|
|
|
|
-type DockerHeaders struct {
|
|
|
- http.RoundTripper
|
|
|
- Headers http.Header
|
|
|
-}
|
|
|
-
|
|
|
-// cloneRequest returns a clone of the provided *http.Request.
|
|
|
-// The clone is a shallow copy of the struct and its Header map
|
|
|
-func cloneRequest(r *http.Request) *http.Request {
|
|
|
- // shallow copy of the struct
|
|
|
- r2 := new(http.Request)
|
|
|
- *r2 = *r
|
|
|
- // deep copy of the Header
|
|
|
- r2.Header = make(http.Header, len(r.Header))
|
|
|
- for k, s := range r.Header {
|
|
|
- r2.Header[k] = append([]string(nil), s...)
|
|
|
+// DockerHeaders returns request modifiers that ensure requests have
|
|
|
+// the User-Agent header set to dockerUserAgent and that metaHeaders
|
|
|
+// are added.
|
|
|
+func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier {
|
|
|
+ modifiers := []transport.RequestModifier{
|
|
|
+ transport.NewHeaderRequestModifier(http.Header{"User-Agent": []string{dockerUserAgent}}),
|
|
|
}
|
|
|
- return r2
|
|
|
-}
|
|
|
-
|
|
|
-func (tr *DockerHeaders) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
- req = cloneRequest(req)
|
|
|
- httpVersion := make([]useragent.VersionInfo, 0, 4)
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
|
|
- if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
|
|
|
- }
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
|
|
|
- httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
|
|
|
-
|
|
|
- userAgent := useragent.AppendVersions(req.UserAgent(), httpVersion...)
|
|
|
-
|
|
|
- req.Header.Set("User-Agent", userAgent)
|
|
|
-
|
|
|
- for k, v := range tr.Headers {
|
|
|
- req.Header[k] = v
|
|
|
+ if metaHeaders != nil {
|
|
|
+ modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
|
|
|
}
|
|
|
- return tr.RoundTripper.RoundTrip(req)
|
|
|
+ return modifiers
|
|
|
}
|
|
|
|
|
|
type debugTransport struct{ http.RoundTripper }
|