Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 59231ea

Browse files
committed
Merge pull request #19312 from cpuguy83/19177_fix_debug_req_dump_size
Don't dump request body when too large
2 parents 851c94d + 93268d8 commit 59231ea

File tree

2 files changed

+57
-41
lines changed

2 files changed

+57
-41
lines changed

api/server/middleware.go

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package server
22

33
import (
4-
"bytes"
4+
"bufio"
55
"encoding/json"
6-
"io/ioutil"
6+
"io"
77
"net/http"
88
"runtime"
99
"strings"
@@ -14,6 +14,7 @@ import (
1414
"github.com/docker/docker/dockerversion"
1515
"github.com/docker/docker/errors"
1616
"github.com/docker/docker/pkg/authorization"
17+
"github.com/docker/docker/pkg/ioutils"
1718
"github.com/docker/docker/pkg/version"
1819
"golang.org/x/net/context"
1920
)
@@ -27,25 +28,37 @@ func debugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
2728
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
2829
logrus.Debugf("%s %s", r.Method, r.RequestURI)
2930

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-
}
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)
4962
}
5063
}
5164

pkg/authorization/authz.go

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package authorization
22

33
import (
4+
"bufio"
45
"bytes"
56
"fmt"
67
"io"
7-
"io/ioutil"
88
"net/http"
99
"strings"
1010

1111
"github.com/Sirupsen/logrus"
12+
"github.com/docker/docker/pkg/ioutils"
1213
)
1314

15+
const maxBodySize = 1048576 // 1MB
16+
1417
// NewCtx creates new authZ context, it is used to store authorization information related to a specific docker
1518
// REST http session
1619
// A context provides two method:
@@ -52,18 +55,12 @@ type Ctx struct {
5255
func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
5356
var body []byte
5457
if sendBody(ctx.requestURI, r.Header) {
55-
var (
56-
err error
57-
drainedBody io.ReadCloser
58-
)
59-
drainedBody, r.Body, err = drainBody(r.Body)
60-
if err != nil {
61-
return err
62-
}
63-
defer drainedBody.Close()
64-
body, err = ioutil.ReadAll(drainedBody)
65-
if err != nil {
66-
return err
58+
if r.ContentLength < maxBodySize {
59+
var err error
60+
body, r.Body, err = drainBody(r.Body)
61+
if err != nil {
62+
return err
63+
}
6764
}
6865
}
6966

@@ -126,15 +123,21 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
126123

127124
// drainBody dump the body, it reads the body data into memory and
128125
// see go sources /go/src/net/http/httputil/dump.go
129-
func drainBody(b io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) {
130-
var buf bytes.Buffer
131-
if _, err := buf.ReadFrom(b); err != nil {
132-
return nil, nil, err
133-
}
134-
if err := b.Close(); err != nil {
126+
func drainBody(body io.ReadCloser) ([]byte, io.ReadCloser, error) {
127+
bufReader := bufio.NewReaderSize(body, maxBodySize)
128+
newBody := ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
129+
130+
data, err := bufReader.Peek(maxBodySize)
131+
if err != io.EOF {
132+
// This means the request is larger than our max
133+
if err == bufio.ErrBufferFull {
134+
return nil, newBody, nil
135+
}
136+
// This means we had an error reading
135137
return nil, nil, err
136138
}
137-
return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
139+
140+
return data, newBody, nil
138141
}
139142

140143
// sendBody returns true when request/response body should be sent to AuthZPlugin

0 commit comments

Comments
 (0)