Browse code

Don't dump request body to log when too large

Fixes an issue where a client can send a large body but specifiy
application/json as the content-type, and cause Docker to consume lots
of RAM while trying to buffer the body so it can be dumped to the debug
log.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2016/01/14 05:28:27
Showing 1 changed files
... ...
@@ -1,9 +1,9 @@
1 1
 package server
2 2
 
3 3
 import (
4
-	"bytes"
4
+	"bufio"
5 5
 	"encoding/json"
6
-	"io/ioutil"
6
+	"io"
7 7
 	"net/http"
8 8
 	"runtime"
9 9
 	"strings"
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"github.com/docker/docker/dockerversion"
15 15
 	"github.com/docker/docker/errors"
16 16
 	"github.com/docker/docker/pkg/authorization"
17
+	"github.com/docker/docker/pkg/ioutils"
17 18
 	"github.com/docker/docker/pkg/version"
18 19
 	"golang.org/x/net/context"
19 20
 )
... ...
@@ -27,25 +28,37 @@ func debugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
27 27
 	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
28 28
 		logrus.Debugf("%s %s", r.Method, r.RequestURI)
29 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
-				}
30
+		if r.Method != "POST" {
31
+			return handler(ctx, w, r, vars)
32
+		}
33
+		if err := httputils.CheckForJSON(r); err != nil {
34
+			return handler(ctx, w, r, vars)
35
+		}
36
+		maxBodySize := 4096 // 4KB
37
+		if r.ContentLength > int64(maxBodySize) {
38
+			return handler(ctx, w, r, vars)
39
+		}
40
+
41
+		body := r.Body
42
+		bufReader := bufio.NewReaderSize(body, maxBodySize)
43
+		r.Body = ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
44
+
45
+		b, err := bufReader.Peek(maxBodySize)
46
+		if err != io.EOF {
47
+			// either there was an error reading, or the buffer is full (in which case the request is too large)
48
+			return handler(ctx, w, r, vars)
49
+		}
50
+
51
+		var postForm map[string]interface{}
52
+		if err := json.Unmarshal(b, &postForm); err == nil {
53
+			if _, exists := postForm["password"]; exists {
54
+				postForm["password"] = "*****"
55
+			}
56
+			formStr, errMarshal := json.Marshal(postForm)
57
+			if errMarshal == nil {
58
+				logrus.Debugf("form data: %s", string(formStr))
59
+			} else {
60
+				logrus.Debugf("form data: %q", postForm)
49 61
 			}
50 62
 		}
51 63