fix: S3 gateway doesn't support full passthrough for encryption (#10484)

The entire encryption layer is dependent on the fact that
KMS should be configured for S3 encryption to work properly
and we only support passing the headers as is to the backend
for encryption only if KMS is configured.

Make sure that this predictability is maintained, currently
the code was allowing encryption to go through and fail
at later to indicate that KMS was not configured. We should
simply reply "NotImplemented" if KMS is not configured, this
allows clients to simply proceed with their tests.
This commit is contained in:
Harshavardhana 2020-09-15 13:57:15 -07:00 committed by GitHub
parent 730d2dc7be
commit 80fab03b63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 60 deletions

View File

@ -61,10 +61,6 @@ func newCachedObjectLayerFn() CacheObjectLayer {
type objectAPIHandlers struct { type objectAPIHandlers struct {
ObjectAPI func() ObjectLayer ObjectAPI func() ObjectLayer
CacheAPI func() CacheObjectLayer CacheAPI func() CacheObjectLayer
// Returns true of handlers should interpret encryption.
EncryptionEnabled func() bool
// Returns true if handlers allow SSE-KMS encryption headers.
AllowSSEKMS func() bool
} }
// getHost tries its best to return the request host. // getHost tries its best to return the request host.
@ -78,17 +74,11 @@ func getHost(r *http.Request) string {
} }
// registerAPIRouter - registers S3 compatible APIs. // registerAPIRouter - registers S3 compatible APIs.
func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) { func registerAPIRouter(router *mux.Router) {
// Initialize API. // Initialize API.
api := objectAPIHandlers{ api := objectAPIHandlers{
ObjectAPI: newObjectLayerFn, ObjectAPI: newObjectLayerFn,
CacheAPI: newCachedObjectLayerFn, CacheAPI: newCachedObjectLayerFn,
EncryptionEnabled: func() bool {
return encryptionEnabled
},
AllowSSEKMS: func() bool {
return allowSSEKMS
},
} }
// API Router // API Router

View File

@ -659,7 +659,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }

View File

@ -349,16 +349,22 @@ func ComputeCompleteMultipartMD5(parts []CompletePart) string {
// parse gateway sse env variable // parse gateway sse env variable
func parseGatewaySSE(s string) (gatewaySSE, error) { func parseGatewaySSE(s string) (gatewaySSE, error) {
l := strings.Split(s, ";") l := strings.Split(s, ";")
var gwSlice = make([]string, 0) var gwSlice gatewaySSE
for _, val := range l { for _, val := range l {
v := strings.ToUpper(val) v := strings.ToUpper(val)
if v == gatewaySSES3 || v == gatewaySSEC { switch v {
case "":
continue
case gatewaySSES3:
fallthrough
case gatewaySSEC:
gwSlice = append(gwSlice, v) gwSlice = append(gwSlice, v)
continue continue
default:
return nil, config.ErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v)
} }
return nil, config.ErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v)
} }
return gatewaySSE(gwSlice), nil return gwSlice, nil
} }
// handle gateway env vars // handle gateway env vars
@ -372,7 +378,7 @@ func gatewayHandleEnvVars() {
} }
gwsseVal := env.Get("MINIO_GATEWAY_SSE", "") gwsseVal := env.Get("MINIO_GATEWAY_SSE", "")
if len(gwsseVal) != 0 { if gwsseVal != "" {
var err error var err error
GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal) GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal)
if err != nil { if err != nil {

View File

@ -261,12 +261,8 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
logger.FatalIf(registerWebRouter(router), "Unable to configure web browser") logger.FatalIf(registerWebRouter(router), "Unable to configure web browser")
} }
// Currently only NAS and S3 gateway support encryption headers.
encryptionEnabled := gatewayName == S3BackendGateway || gatewayName == NASBackendGateway
allowSSEKMS := gatewayName == S3BackendGateway // Only S3 can support SSE-KMS (as pass-through)
// Add API router. // Add API router.
registerAPIRouter(router, encryptionEnabled, allowSSEKMS) registerAPIRouter(router)
// Use all the middlewares // Use all the middlewares
router.Use(registerMiddlewares) router.Use(registerMiddlewares)

View File

@ -316,7 +316,7 @@ func (l *s3EncObjects) GetObjectNInfo(ctx context.Context, bucket, object string
} }
fn, off, length, err := minio.NewGetObjectReader(rs, objInfo, o) fn, off, length, err := minio.NewGetObjectReader(rs, objInfo, o)
if err != nil { if err != nil {
return nil, minio.ErrorRespToObjectError(err) return nil, minio.ErrorRespToObjectError(err, bucket, object)
} }
if l.isGWEncrypted(ctx, bucket, object) { if l.isGWEncrypted(ctx, bucket, object) {
object = getGWContentPath(object) object = getGWContentPath(object)

View File

@ -391,21 +391,21 @@ func (l *s3Objects) GetObjectNInfo(ctx context.Context, bucket, object string, r
return nil, minio.ErrorRespToObjectError(err, bucket, object) return nil, minio.ErrorRespToObjectError(err, bucket, object)
} }
var startOffset, length int64 fn, off, length, err := minio.NewGetObjectReader(rs, objInfo, opts)
startOffset, length, err = rs.GetOffsetLength(objInfo.Size)
if err != nil { if err != nil {
return nil, minio.ErrorRespToObjectError(err, bucket, object) return nil, minio.ErrorRespToObjectError(err, bucket, object)
} }
pr, pw := io.Pipe() pr, pw := io.Pipe()
go func() { go func() {
err := l.GetObject(ctx, bucket, object, startOffset, length, pw, objInfo.ETag, opts) err := l.GetObject(ctx, bucket, object, off, length, pw, objInfo.ETag, opts)
pw.CloseWithError(err) pw.CloseWithError(err)
}() }()
// Setup cleanup function to cause the above go-routine to // Setup cleanup function to cause the above go-routine to
// exit in case of partial read // exit in case of partial read
pipeCloser := func() { pr.Close() } pipeCloser := func() { pr.Close() }
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser) return fn(pr, h, opts.CheckPrecondFn, pipeCloser)
} }
// GetObject reads an object from S3. Supports additional // GetObject reads an object from S3. Supports additional
@ -745,7 +745,7 @@ func (l *s3Objects) IsCompressionSupported() bool {
// IsEncryptionSupported returns whether server side encryption is implemented for this layer. // IsEncryptionSupported returns whether server side encryption is implemented for this layer.
func (l *s3Objects) IsEncryptionSupported() bool { func (l *s3Objects) IsEncryptionSupported() bool {
return minio.GlobalKMS != nil || len(minio.GlobalGatewaySSE) > 0 return minio.GlobalKMS != nil || minio.GlobalGatewaySSE.IsSet()
} }
func (l *s3Objects) IsTaggingSupported() bool { func (l *s3Objects) IsTaggingSupported() bool {

View File

@ -99,11 +99,13 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) if crypto.S3.IsRequested(r.Header) || crypto.S3KMS.IsRequested(r.Header) { // If SSE-S3 or SSE-KMS present -> AWS fails with undefined error
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
return return
} }
@ -308,7 +310,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) { if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
return return
} }
@ -507,7 +509,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest)) writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) { if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
return return
} }
@ -781,11 +783,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) // SSE-KMS is not supported if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
@ -1297,14 +1301,17 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) && !api.AllowSSEKMS() {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) // SSE-KMS is not supported if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
return
}
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return
}
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object, err := url.PathUnescape(vars["object"]) object, err := url.PathUnescape(vars["object"])
@ -1429,7 +1436,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
// Check if bucket encryption is enabled // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) _, err = globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions // This request header needs to be set prior to setting ObjectOptions
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) { if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
} }
@ -1604,14 +1611,17 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) && !api.AllowSSEKMS() {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) // SSE-KMS is not supported if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
return
}
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return
}
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object, err := url.PathUnescape(vars["object"]) object, err := url.PathUnescape(vars["object"])
@ -1628,7 +1638,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
// Check if bucket encryption is enabled // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) _, err = globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions // This request header needs to be set prior to setting ObjectOptions
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) { if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
} }
@ -1729,11 +1739,13 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) // SSE-KMS is not supported if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
@ -2043,11 +2055,13 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.S3KMS.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) // SSE-KMS is not supported if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }
if !api.EncryptionEnabled() && crypto.IsRequested(r.Header) {
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
return return
} }

View File

@ -108,9 +108,8 @@ func configureServerHandler(endpointZones EndpointZones) (http.Handler, error) {
} }
} }
// Add API router, additionally all server mode support encryption // Add API router
// but don't allow SSE-KMS. registerAPIRouter(router)
registerAPIRouter(router, true, false)
router.Use(registerMiddlewares) router.Use(registerMiddlewares)

View File

@ -2058,7 +2058,7 @@ func registerBucketLevelFunc(bucket *mux.Router, api objectAPIHandlers, apiFunct
func registerAPIFunctions(muxRouter *mux.Router, objLayer ObjectLayer, apiFunctions ...string) { func registerAPIFunctions(muxRouter *mux.Router, objLayer ObjectLayer, apiFunctions ...string) {
if len(apiFunctions) == 0 { if len(apiFunctions) == 0 {
// Register all api endpoints by default. // Register all api endpoints by default.
registerAPIRouter(muxRouter, true, false) registerAPIRouter(muxRouter)
return return
} }
// API Router. // API Router.
@ -2088,7 +2088,6 @@ func registerAPIFunctions(muxRouter *mux.Router, objLayer ObjectLayer, apiFuncti
} }
return nil return nil
}, },
EncryptionEnabled: func() bool { return true },
} }
// Register ListBuckets handler. // Register ListBuckets handler.
@ -2109,7 +2108,7 @@ func initTestAPIEndPoints(objLayer ObjectLayer, apiFunctions []string) http.Hand
registerAPIFunctions(muxRouter, objLayer, apiFunctions...) registerAPIFunctions(muxRouter, objLayer, apiFunctions...)
return muxRouter return muxRouter
} }
registerAPIRouter(muxRouter, true, false) registerAPIRouter(muxRouter)
return muxRouter return muxRouter
} }