mirror of
https://github.com/distribution/distribution.git
synced 2025-09-12 13:23:32 +00:00
registry/{storage,handlers}: limit content sizes
Under certain circumstances, the use of `StorageDriver.GetContent` can result in unbounded memory allocations. In particualr, this happens when accessing a layer through the manifests endpoint. This problem is mitigated by setting a 4MB limit when using to access content that may have been accepted from a user. In practice, this means setting the limit with the use of `BlobProvider.Get` by wrapping `StorageDriver.GetContent` in a helper that uses `StorageDriver.Reader` with a `limitReader` that returns an error. When mitigating this security issue, we also noticed that the size of manifests uploaded to the registry is also unlimited. We apply similar logic to the request body of payloads that are full buffered. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
// closeResources closes all the provided resources after running the target
|
||||
@@ -23,7 +22,9 @@ func closeResources(handler http.Handler, closers ...io.Closer) http.Handler {
|
||||
// copyFullPayload copies the payload of an HTTP request to destWriter. If it
|
||||
// receives less content than expected, and the client disconnected during the
|
||||
// upload, it avoids sending a 400 error to keep the logs cleaner.
|
||||
func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWriter io.Writer, context ctxu.Context, action string, errSlice *errcode.Errors) error {
|
||||
//
|
||||
// The copy will be limited to `limit` bytes, if limit is greater than zero.
|
||||
func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWriter io.Writer, limit int64, context ctxu.Context, action string) error {
|
||||
// Get a channel that tells us if the client disconnects
|
||||
var clientClosed <-chan bool
|
||||
if notifier, ok := responseWriter.(http.CloseNotifier); ok {
|
||||
@@ -32,8 +33,13 @@ func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWr
|
||||
ctxu.GetLogger(context).Warnf("the ResponseWriter does not implement CloseNotifier (type: %T)", responseWriter)
|
||||
}
|
||||
|
||||
var body = r.Body
|
||||
if limit > 0 {
|
||||
body = http.MaxBytesReader(responseWriter, body, limit)
|
||||
}
|
||||
|
||||
// Read in the data, if any.
|
||||
copied, err := io.Copy(destWriter, r.Body)
|
||||
copied, err := io.Copy(destWriter, body)
|
||||
if clientClosed != nil && (err != nil || (r.ContentLength > 0 && copied < r.ContentLength)) {
|
||||
// Didn't receive as much content as expected. Did the client
|
||||
// disconnect during the request? If so, avoid returning a 400
|
||||
@@ -58,7 +64,6 @@ func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWr
|
||||
|
||||
if err != nil {
|
||||
ctxu.GetLogger(context).Errorf("unknown error reading request payload: %v", err)
|
||||
*errSlice = append(*errSlice, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
return err
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user