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