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>
| ... | ... |
@@ -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 |
|