From a1f70b106fe6c82172762269f39a523d3a55c44a Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Fri, 14 May 2021 09:59:05 +0200 Subject: [PATCH] sse: add support for SSE-KMS bucket configurations (#12295) This commit adds support for SSE-KMS bucket configurations. Before, the MinIO server did not support SSE-KMS, and therefore, it was not possible to specify an SSE-KMS bucket config. Now, this is possible. For example: ``` mc encrypt set sse-kms some-key /my-bucket ``` Further, this commit fixes an issue caused by not supporting SSE-KMS bucket configuration and switching to SSE-KMS as default SSE method. Before, the server just checked whether an SSE bucket config was present (not which type of SSE config) and applied the default SSE method (which was switched from SSE-S3 to SSE-KMS). This caused objects to get encrypted with SSE-KMS even though a SSE-S3 bucket config was present. This issue is fixed as a side-effect of this commit. Signed-off-by: Andreas Auernhammer --- cmd/bucket-encryption.go | 2 +- cmd/bucket-encryption_test.go | 7 ++-- cmd/bucket-handlers.go | 8 +--- cmd/object-handlers.go | 28 ++++--------- cmd/web-handlers.go | 6 +-- pkg/bucket/encryption/bucket-sse-config.go | 49 +++++++++++++++++++++- 6 files changed, 64 insertions(+), 36 deletions(-) diff --git a/cmd/bucket-encryption.go b/cmd/bucket-encryption.go index bd71c2104..d1f2d0e1a 100644 --- a/cmd/bucket-encryption.go +++ b/cmd/bucket-encryption.go @@ -53,7 +53,7 @@ func validateBucketSSEConfig(r io.Reader) (*bucketsse.BucketSSEConfig, error) { return nil, err } - if len(encConfig.Rules) == 1 && encConfig.Rules[0].DefaultEncryptionAction.Algorithm == bucketsse.AES256 { + if len(encConfig.Rules) == 1 { return encConfig, nil } diff --git a/cmd/bucket-encryption_test.go b/cmd/bucket-encryption_test.go index 867a879be..3d5ff48d4 100644 --- a/cmd/bucket-encryption_test.go +++ b/cmd/bucket-encryption_test.go @@ -19,7 +19,6 @@ package cmd import ( "bytes" - "errors" "testing" ) @@ -47,12 +46,12 @@ func TestValidateBucketSSEConfig(t *testing.T) { aws:kms - arn:aws:kms:us-east-1:1234/5678example + my-key `, - expectedErr: errors.New("Unsupported bucket encryption configuration"), - shouldPass: false, + expectedErr: nil, + shouldPass: true, }, } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 651eed362..42ddb58be 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -992,12 +992,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h var objectEncryptionKey crypto.ObjectKey // Check if bucket encryption is enabled - if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption { - // This request header needs to be set prior to setting ObjectOptions - if !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) - } - } + sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) + sseConfig.Apply(r.Header, globalAutoEncryption) // get gateway encryption options var opts ObjectOptions diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index c4739ac03..012f3fe47 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -960,11 +960,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re } // Check if bucket encryption is enabled - _, err = globalBucketSSEConfigSys.Get(dstBucket) - // This request header needs to be set prior to setting ObjectOptions - if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) - } + sseConfig, _ := globalBucketSSEConfigSys.Get(dstBucket) + sseConfig.Apply(r.Header, globalAutoEncryption) var srcOpts, dstOpts ObjectOptions srcOpts, err = copySrcOpts(ctx, r, srcBucket, srcObject) @@ -1566,11 +1563,8 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } // Check if bucket encryption is enabled - _, err = globalBucketSSEConfigSys.Get(bucket) - // This request header needs to be set prior to setting ObjectOptions - if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) - } + sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) + sseConfig.Apply(r.Header, globalAutoEncryption) actualSize := size if objectAPI.IsCompressionSupported() && isCompressible(r.Header, object) && size > 0 { @@ -1885,11 +1879,8 @@ func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *h } // Check if bucket encryption is enabled - _, err = globalBucketSSEConfigSys.Get(bucket) - // This request header needs to be set prior to setting ObjectOptions - if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) - } + sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) + sseConfig.Apply(r.Header, globalAutoEncryption) retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, iampolicy.PutObjectRetentionAction) holdPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, iampolicy.PutObjectLegalHoldAction) @@ -2076,11 +2067,8 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r } // Check if bucket encryption is enabled - _, err = globalBucketSSEConfigSys.Get(bucket) - // This request header needs to be set prior to setting ObjectOptions - if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) - } + sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) + sseConfig.Apply(r.Header, globalAutoEncryption) // Validate storage class metadata if present if sc := r.Header.Get(xhttp.AmzStorageClass); sc != "" { diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index db042d6bb..bfe56e7c5 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -1201,10 +1201,8 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { } // Check if bucket encryption is enabled - _, err = globalBucketSSEConfigSys.Get(bucket) - if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) - } + sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) + sseConfig.Apply(r.Header, globalAutoEncryption) // Require Content-Length to be set in the request size := r.ContentLength diff --git a/pkg/bucket/encryption/bucket-sse-config.go b/pkg/bucket/encryption/bucket-sse-config.go index f47a5f1d7..bfb287554 100644 --- a/pkg/bucket/encryption/bucket-sse-config.go +++ b/pkg/bucket/encryption/bucket-sse-config.go @@ -21,6 +21,10 @@ import ( "encoding/xml" "errors" "io" + "net/http" + + "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" ) const ( @@ -107,6 +111,49 @@ func ParseBucketSSEConfig(r io.Reader) (*BucketSSEConfig, error) { if config.XMLNS == "" { config.XMLNS = xmlNS } - return &config, nil } + +// Apply applies the SSE bucket configuration on the given HTTP headers and +// sets the specified SSE headers. +// +// Apply does not overwrite any existing SSE headers. Further, it will +// set minimal SSE-KMS headers if autoEncrypt is true and the BucketSSEConfig +// is nil. +func (b *BucketSSEConfig) Apply(headers http.Header, autoEncrypt bool) { + if _, ok := crypto.IsRequested(headers); ok { + return + } + if b == nil { + if autoEncrypt { + headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) + } + return + } + + switch b.Algorithm() { + case xhttp.AmzEncryptionAES: + headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) + case xhttp.AmzEncryptionKMS: + headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) + headers.Set(xhttp.AmzServerSideEncryptionKmsID, b.KeyID()) + } +} + +// Algorithm returns the SSE algorithm specified by the SSE configuration. +func (b *BucketSSEConfig) Algorithm() SSEAlgorithm { + for _, rule := range b.Rules { + return rule.DefaultEncryptionAction.Algorithm + } + return "" +} + +// KeyID returns the KMS key ID specified by the SSE configuration. +// If the SSE configuration does not specify SSE-KMS it returns an +// empty key ID. +func (b *BucketSSEConfig) KeyID() string { + for _, rule := range b.Rules { + return rule.DefaultEncryptionAction.MasterKeyID + } + return "" +}