middleware.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package server
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "io"
  6. "net/http"
  7. "runtime"
  8. "strings"
  9. "github.com/Sirupsen/logrus"
  10. "github.com/docker/docker/api"
  11. "github.com/docker/docker/api/server/httputils"
  12. "github.com/docker/docker/dockerversion"
  13. "github.com/docker/docker/errors"
  14. "github.com/docker/docker/pkg/authorization"
  15. "github.com/docker/docker/pkg/ioutils"
  16. "github.com/docker/docker/pkg/version"
  17. "golang.org/x/net/context"
  18. )
  19. // middleware is an adapter to allow the use of ordinary functions as Docker API filters.
  20. // Any function that has the appropriate signature can be register as a middleware.
  21. type middleware func(handler httputils.APIFunc) httputils.APIFunc
  22. // debugRequestMiddleware dumps the request to logger
  23. func debugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  24. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  25. logrus.Debugf("%s %s", r.Method, r.RequestURI)
  26. if r.Method != "POST" {
  27. return handler(ctx, w, r, vars)
  28. }
  29. if err := httputils.CheckForJSON(r); err != nil {
  30. return handler(ctx, w, r, vars)
  31. }
  32. maxBodySize := 4096 // 4KB
  33. if r.ContentLength > int64(maxBodySize) {
  34. return handler(ctx, w, r, vars)
  35. }
  36. body := r.Body
  37. bufReader := bufio.NewReaderSize(body, maxBodySize)
  38. r.Body = ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
  39. b, err := bufReader.Peek(maxBodySize)
  40. if err != io.EOF {
  41. // either there was an error reading, or the buffer is full (in which case the request is too large)
  42. return handler(ctx, w, r, vars)
  43. }
  44. var postForm map[string]interface{}
  45. if err := json.Unmarshal(b, &postForm); err == nil {
  46. if _, exists := postForm["password"]; exists {
  47. postForm["password"] = "*****"
  48. }
  49. formStr, errMarshal := json.Marshal(postForm)
  50. if errMarshal == nil {
  51. logrus.Debugf("form data: %s", string(formStr))
  52. } else {
  53. logrus.Debugf("form data: %q", postForm)
  54. }
  55. }
  56. return handler(ctx, w, r, vars)
  57. }
  58. }
  59. // authorizationMiddleware perform authorization on the request.
  60. func (s *Server) authorizationMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  61. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  62. // FIXME: fill when authN gets in
  63. // User and UserAuthNMethod are taken from AuthN plugins
  64. // Currently tracked in https://github.com/docker/docker/pull/13994
  65. user := ""
  66. userAuthNMethod := ""
  67. authCtx := authorization.NewCtx(s.authZPlugins, user, userAuthNMethod, r.Method, r.RequestURI)
  68. if err := authCtx.AuthZRequest(w, r); err != nil {
  69. logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
  70. return err
  71. }
  72. rw := authorization.NewResponseModifier(w)
  73. if err := handler(ctx, rw, r, vars); err != nil {
  74. logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.RequestURI, err)
  75. return err
  76. }
  77. if err := authCtx.AuthZResponse(rw, r); err != nil {
  78. logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
  79. return err
  80. }
  81. return nil
  82. }
  83. }
  84. // userAgentMiddleware checks the User-Agent header looking for a valid docker client spec.
  85. func (s *Server) userAgentMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  86. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  87. if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  88. dockerVersion := version.Version(s.cfg.Version)
  89. userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  90. // v1.20 onwards includes the GOOS of the client after the version
  91. // such as Docker/1.7.0 (linux)
  92. if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
  93. userAgent[1] = strings.Split(userAgent[1], " ")[0]
  94. }
  95. if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  96. logrus.Debugf("Client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  97. }
  98. }
  99. return handler(ctx, w, r, vars)
  100. }
  101. }
  102. // corsMiddleware sets the CORS header expectations in the server.
  103. func (s *Server) corsMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  104. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  105. // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  106. // otherwise, all head values will be passed to HTTP handler
  107. corsHeaders := s.cfg.CorsHeaders
  108. if corsHeaders == "" && s.cfg.EnableCors {
  109. corsHeaders = "*"
  110. }
  111. if corsHeaders != "" {
  112. writeCorsHeaders(w, r, corsHeaders)
  113. }
  114. return handler(ctx, w, r, vars)
  115. }
  116. }
  117. // versionMiddleware checks the api version requirements before passing the request to the server handler.
  118. func versionMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  119. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  120. apiVersion := version.Version(vars["version"])
  121. if apiVersion == "" {
  122. apiVersion = api.DefaultVersion
  123. }
  124. if apiVersion.GreaterThan(api.DefaultVersion) {
  125. return errors.ErrorCodeNewerClientVersion.WithArgs(apiVersion, api.DefaultVersion)
  126. }
  127. if apiVersion.LessThan(api.MinVersion) {
  128. return errors.ErrorCodeOldClientVersion.WithArgs(apiVersion, api.MinVersion)
  129. }
  130. w.Header().Set("Server", "Docker/"+dockerversion.Version+" ("+runtime.GOOS+")")
  131. ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
  132. return handler(ctx, w, r, vars)
  133. }
  134. }
  135. // handleWithGlobalMiddlwares wraps the handler function for a request with
  136. // the server's global middlewares. The order of the middlewares is backwards,
  137. // meaning that the first in the list will be evaluated last.
  138. //
  139. // Example: handleWithGlobalMiddlewares(s.getContainersName)
  140. //
  141. // s.loggingMiddleware(
  142. // s.userAgentMiddleware(
  143. // s.corsMiddleware(
  144. // versionMiddleware(s.getContainersName)
  145. // )
  146. // )
  147. // )
  148. // )
  149. func (s *Server) handleWithGlobalMiddlewares(handler httputils.APIFunc) httputils.APIFunc {
  150. middlewares := []middleware{
  151. versionMiddleware,
  152. s.corsMiddleware,
  153. s.userAgentMiddleware,
  154. }
  155. // Only want this on debug level
  156. if s.cfg.Logging && logrus.GetLevel() == logrus.DebugLevel {
  157. middlewares = append(middlewares, debugRequestMiddleware)
  158. }
  159. if len(s.cfg.AuthorizationPluginNames) > 0 {
  160. s.authZPlugins = authorization.NewPlugins(s.cfg.AuthorizationPluginNames)
  161. middlewares = append(middlewares, s.authorizationMiddleware)
  162. }
  163. h := handler
  164. for _, m := range middlewares {
  165. h = m(h)
  166. }
  167. return h
  168. }