Don't dump request body when too large
| ... | ... |
@@ -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 |
|
| ... | ... |
@@ -1,16 +1,19 @@ |
| 1 | 1 |
package authorization |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "bufio" |
|
| 4 | 5 |
"bytes" |
| 5 | 6 |
"fmt" |
| 6 | 7 |
"io" |
| 7 |
- "io/ioutil" |
|
| 8 | 8 |
"net/http" |
| 9 | 9 |
"strings" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/Sirupsen/logrus" |
| 12 |
+ "github.com/docker/docker/pkg/ioutils" |
|
| 12 | 13 |
) |
| 13 | 14 |
|
| 15 |
+const maxBodySize = 1048576 // 1MB |
|
| 16 |
+ |
|
| 14 | 17 |
// NewCtx creates new authZ context, it is used to store authorization information related to a specific docker |
| 15 | 18 |
// REST http session |
| 16 | 19 |
// A context provides two method: |
| ... | ... |
@@ -52,18 +55,12 @@ type Ctx struct {
|
| 52 | 52 |
func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
|
| 53 | 53 |
var body []byte |
| 54 | 54 |
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 |
|
| 55 |
+ if r.ContentLength < maxBodySize {
|
|
| 56 |
+ var err error |
|
| 57 |
+ body, r.Body, err = drainBody(r.Body) |
|
| 58 |
+ if err != nil {
|
|
| 59 |
+ return err |
|
| 60 |
+ } |
|
| 67 | 61 |
} |
| 68 | 62 |
} |
| 69 | 63 |
|
| ... | ... |
@@ -126,15 +123,21 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
| 126 | 126 |
|
| 127 | 127 |
// drainBody dump the body, it reads the body data into memory and |
| 128 | 128 |
// 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 {
|
|
| 129 |
+func drainBody(body io.ReadCloser) ([]byte, io.ReadCloser, error) {
|
|
| 130 |
+ bufReader := bufio.NewReaderSize(body, maxBodySize) |
|
| 131 |
+ newBody := ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
|
|
| 132 |
+ |
|
| 133 |
+ data, err := bufReader.Peek(maxBodySize) |
|
| 134 |
+ if err != io.EOF {
|
|
| 135 |
+ // This means the request is larger than our max |
|
| 136 |
+ if err == bufio.ErrBufferFull {
|
|
| 137 |
+ return nil, newBody, nil |
|
| 138 |
+ } |
|
| 139 |
+ // This means we had an error reading |
|
| 135 | 140 |
return nil, nil, err |
| 136 | 141 |
} |
| 137 |
- return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil |
|
| 142 |
+ |
|
| 143 |
+ return data, newBody, nil |
|
| 138 | 144 |
} |
| 139 | 145 |
|
| 140 | 146 |
// sendBody returns true when request/response body should be sent to AuthZPlugin |