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