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  }