github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/api/server/middleware.go (about)

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