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 <alias>/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 <aead@mail.de>
This commit is contained in:
Andreas Auernhammer 2021-05-14 09:59:05 +02:00 committed by GitHub
parent 951acf561c
commit a1f70b106f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 36 deletions

View File

@ -53,7 +53,7 @@ func validateBucketSSEConfig(r io.Reader) (*bucketsse.BucketSSEConfig, error) {
return nil, err return nil, err
} }
if len(encConfig.Rules) == 1 && encConfig.Rules[0].DefaultEncryptionAction.Algorithm == bucketsse.AES256 { if len(encConfig.Rules) == 1 {
return encConfig, nil return encConfig, nil
} }

View File

@ -19,7 +19,6 @@ package cmd
import ( import (
"bytes" "bytes"
"errors"
"testing" "testing"
) )
@ -47,12 +46,12 @@ func TestValidateBucketSSEConfig(t *testing.T) {
<Rule> <Rule>
<ApplyServerSideEncryptionByDefault> <ApplyServerSideEncryptionByDefault>
<SSEAlgorithm>aws:kms</SSEAlgorithm> <SSEAlgorithm>aws:kms</SSEAlgorithm>
<KMSMasterKeyID>arn:aws:kms:us-east-1:1234/5678example</KMSMasterKeyID> <KMSMasterKeyID>my-key</KMSMasterKeyID>
</ApplyServerSideEncryptionByDefault> </ApplyServerSideEncryptionByDefault>
</Rule> </Rule>
</ServerSideEncryptionConfiguration>`, </ServerSideEncryptionConfiguration>`,
expectedErr: errors.New("Unsupported bucket encryption configuration"), expectedErr: nil,
shouldPass: false, shouldPass: true,
}, },
} }

View File

@ -992,12 +992,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
var objectEncryptionKey crypto.ObjectKey var objectEncryptionKey crypto.ObjectKey
// Check if bucket encryption is enabled // Check if bucket encryption is enabled
if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption { sseConfig, _ := globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions sseConfig.Apply(r.Header, globalAutoEncryption)
if !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
}
}
// get gateway encryption options // get gateway encryption options
var opts ObjectOptions var opts ObjectOptions

View File

@ -960,11 +960,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
} }
// Check if bucket encryption is enabled // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(dstBucket) sseConfig, _ := globalBucketSSEConfigSys.Get(dstBucket)
// This request header needs to be set prior to setting ObjectOptions sseConfig.Apply(r.Header, globalAutoEncryption)
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
}
var srcOpts, dstOpts ObjectOptions var srcOpts, dstOpts ObjectOptions
srcOpts, err = copySrcOpts(ctx, r, srcBucket, srcObject) 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 // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) sseConfig, _ := globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions sseConfig.Apply(r.Header, globalAutoEncryption)
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
}
actualSize := size actualSize := size
if objectAPI.IsCompressionSupported() && isCompressible(r.Header, object) && size > 0 { 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 // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) sseConfig, _ := globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions sseConfig.Apply(r.Header, globalAutoEncryption)
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
}
retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, iampolicy.PutObjectRetentionAction) retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, iampolicy.PutObjectRetentionAction)
holdPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, iampolicy.PutObjectLegalHoldAction) 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 // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) sseConfig, _ := globalBucketSSEConfigSys.Get(bucket)
// This request header needs to be set prior to setting ObjectOptions sseConfig.Apply(r.Header, globalAutoEncryption)
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
}
// Validate storage class metadata if present // Validate storage class metadata if present
if sc := r.Header.Get(xhttp.AmzStorageClass); sc != "" { if sc := r.Header.Get(xhttp.AmzStorageClass); sc != "" {

View File

@ -1201,10 +1201,8 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
} }
// Check if bucket encryption is enabled // Check if bucket encryption is enabled
_, err = globalBucketSSEConfigSys.Get(bucket) sseConfig, _ := globalBucketSSEConfigSys.Get(bucket)
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { sseConfig.Apply(r.Header, globalAutoEncryption)
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
}
// Require Content-Length to be set in the request // Require Content-Length to be set in the request
size := r.ContentLength size := r.ContentLength

View File

@ -21,6 +21,10 @@ import (
"encoding/xml" "encoding/xml"
"errors" "errors"
"io" "io"
"net/http"
"github.com/minio/minio/cmd/crypto"
xhttp "github.com/minio/minio/cmd/http"
) )
const ( const (
@ -107,6 +111,49 @@ func ParseBucketSSEConfig(r io.Reader) (*BucketSSEConfig, error) {
if config.XMLNS == "" { if config.XMLNS == "" {
config.XMLNS = xmlNS config.XMLNS = xmlNS
} }
return &config, nil 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 ""
}