mirror of
https://github.com/minio/minio.git
synced 2024-12-25 22:55:54 -05:00
add SSE-KMS support and use SSE-KMS for auto encryption (#11767)
This commit adds basic SSE-KMS support. Now, a client can specify the SSE-KMS headers (algorithm, optional key-id, optional context) such that the object gets encrypted using the SSE-KMS method. Further, auto-encryption now defaults to SSE-KMS. This commit does not try to do any refactoring and instead tries to implement SSE-KMS as a minimal change to the code base. However, refactoring the entire crypto-related code is planned - but needs a separate effort. Signed-off-by: Andreas Auernhammer <aead@mail.de> Co-authored-by: Klaus Post <klauspost@gmail.com>
This commit is contained in:
parent
3a0e7347ca
commit
26f1fcab7d
@ -1011,16 +1011,28 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var reader io.Reader
|
var (
|
||||||
var key []byte
|
reader io.Reader
|
||||||
if crypto.SSEC.IsRequested(formValues) {
|
keyID string
|
||||||
|
key []byte
|
||||||
|
kmsCtx crypto.Context
|
||||||
|
)
|
||||||
|
kind, _ := crypto.IsRequested(formValues)
|
||||||
|
switch kind {
|
||||||
|
case crypto.SSEC:
|
||||||
key, err = ParseSSECustomerHeader(formValues)
|
key, err = ParseSSECustomerHeader(formValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case crypto.S3KMS:
|
||||||
|
keyID, kmsCtx, err = crypto.S3KMS.ParseHTTP(formValues)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
reader, objectEncryptionKey, err = newEncryptReader(hashReader, key, bucket, object, metadata, crypto.S3.IsRequested(formValues))
|
}
|
||||||
|
reader, objectEncryptionKey, err = newEncryptReader(hashReader, kind, keyID, key, bucket, object, metadata, kmsCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
@ -96,27 +97,27 @@ var kmsParseHTTPTests = []struct {
|
|||||||
{Header: http.Header{
|
{Header: http.Header{
|
||||||
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
||||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
||||||
"X-Amz-Server-Side-Encryption-Context": []string{"{}"},
|
"X-Amz-Server-Side-Encryption-Context": []string{base64.StdEncoding.EncodeToString([]byte("{}"))},
|
||||||
}, ShouldFail: false}, // 3
|
}, ShouldFail: false}, // 3
|
||||||
{Header: http.Header{
|
{Header: http.Header{
|
||||||
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
||||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
||||||
"X-Amz-Server-Side-Encryption-Context": []string{"{\"bucket\": \"some-bucket\"}"},
|
"X-Amz-Server-Side-Encryption-Context": []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
|
||||||
}, ShouldFail: false}, // 4
|
}, ShouldFail: false}, // 4
|
||||||
{Header: http.Header{
|
{Header: http.Header{
|
||||||
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
||||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
||||||
"X-Amz-Server-Side-Encryption-Context": []string{"{\"bucket\": \"some-bucket\"}"},
|
"X-Amz-Server-Side-Encryption-Context": []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
|
||||||
}, ShouldFail: false}, // 5
|
}, ShouldFail: false}, // 5
|
||||||
{Header: http.Header{
|
{Header: http.Header{
|
||||||
"X-Amz-Server-Side-Encryption": []string{"AES256"},
|
"X-Amz-Server-Side-Encryption": []string{"AES256"},
|
||||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
||||||
"X-Amz-Server-Side-Encryption-Context": []string{"{\"bucket\": \"some-bucket\"}"},
|
"X-Amz-Server-Side-Encryption-Context": []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
|
||||||
}, ShouldFail: true}, // 6
|
}, ShouldFail: true}, // 6
|
||||||
{Header: http.Header{
|
{Header: http.Header{
|
||||||
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
"X-Amz-Server-Side-Encryption": []string{"aws:kms"},
|
||||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
|
||||||
"X-Amz-Server-Side-Encryption-Context": []string{"{\"bucket\": \"some-bucket\""}, // invalid JSON
|
"X-Amz-Server-Side-Encryption-Context": []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"`))}, // invalid JSON
|
||||||
}, ShouldFail: true}, // 7
|
}, ShouldFail: true}, // 7
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,13 @@ func (ssekms) ParseHTTP(h http.Header) (string, Context, error) {
|
|||||||
|
|
||||||
var ctx Context
|
var ctx Context
|
||||||
if context, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok {
|
if context, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(context[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
if err := json.Unmarshal([]byte(context[0]), &ctx); err != nil {
|
if err := json.Unmarshal(b, &ctx); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,7 +114,7 @@ func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, ob
|
|||||||
// the modified metadata. If the keyID and the kmsKey is not empty it encodes
|
// the modified metadata. If the keyID and the kmsKey is not empty it encodes
|
||||||
// both into the metadata as well. It allocates a new metadata map if metadata
|
// both into the metadata as well. It allocates a new metadata map if metadata
|
||||||
// is nil.
|
// is nil.
|
||||||
func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey, ctx Context) map[string]string {
|
||||||
if sealedKey.Algorithm != SealAlgorithm {
|
if sealedKey.Algorithm != SealAlgorithm {
|
||||||
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
|
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
|
||||||
}
|
}
|
||||||
@ -132,6 +137,10 @@ func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []
|
|||||||
metadata[MetaAlgorithm] = sealedKey.Algorithm
|
metadata[MetaAlgorithm] = sealedKey.Algorithm
|
||||||
metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
||||||
metadata[MetaSealedKeyKMS] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
metadata[MetaSealedKeyKMS] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
||||||
|
if len(ctx) > 0 {
|
||||||
|
b, _ := ctx.MarshalText()
|
||||||
|
metadata[MetaContext] = base64.StdEncoding.EncodeToString(b)
|
||||||
|
}
|
||||||
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
|
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
|
||||||
metadata[MetaKeyID] = keyID
|
metadata[MetaKeyID] = keyID
|
||||||
metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey)
|
metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey)
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
@ -36,6 +37,7 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/fips"
|
"github.com/minio/minio/pkg/fips"
|
||||||
|
"github.com/minio/minio/pkg/kms"
|
||||||
"github.com/minio/sio"
|
"github.com/minio/sio"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,11 +118,71 @@ func ParseSSECustomerHeader(header http.Header) (key []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function rotates old to new key.
|
// This function rotates old to new key.
|
||||||
func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map[string]string) error {
|
func rotateKey(oldKey []byte, newKeyID string, newKey []byte, bucket, object string, metadata map[string]string, ctx crypto.Context) error {
|
||||||
switch {
|
kind, _ := crypto.IsEncrypted(metadata)
|
||||||
default:
|
switch kind {
|
||||||
return errObjectTampered
|
case crypto.S3:
|
||||||
case crypto.SSEC.IsEncrypted(metadata):
|
if GlobalKMS == nil {
|
||||||
|
return errKMSNotConfigured
|
||||||
|
}
|
||||||
|
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, kms.Context{bucket: path.Join(bucket, object)})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var objectKey crypto.ObjectKey
|
||||||
|
if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newKey, err := GlobalKMS.GenerateKey("", kms.Context{bucket: path.Join(bucket, object)})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
|
||||||
|
crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey)
|
||||||
|
return nil
|
||||||
|
case crypto.S3KMS:
|
||||||
|
if GlobalKMS == nil {
|
||||||
|
return errKMSNotConfigured
|
||||||
|
}
|
||||||
|
objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ctx) == 0 {
|
||||||
|
_, _, _, ctx, err = crypto.S3KMS.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the context does not contain the bucket key
|
||||||
|
// we must add it for key generation. However,
|
||||||
|
// the context must be stored exactly like the
|
||||||
|
// client provided it. Therefore, we delete the
|
||||||
|
// bucket key, if added by us, after generating
|
||||||
|
// the key.
|
||||||
|
_, ctxContainsBucket := ctx[bucket]
|
||||||
|
if !ctxContainsBucket {
|
||||||
|
ctx[bucket] = path.Join(bucket, object)
|
||||||
|
}
|
||||||
|
newKey, err := GlobalKMS.GenerateKey(newKeyID, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ctxContainsBucket {
|
||||||
|
delete(ctx, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealedKey := objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object)
|
||||||
|
crypto.S3KMS.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey, ctx)
|
||||||
|
return nil
|
||||||
|
case crypto.SSEC:
|
||||||
sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
|
sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -140,40 +202,19 @@ func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map
|
|||||||
sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object)
|
sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object)
|
||||||
crypto.SSEC.CreateMetadata(metadata, sealedKey)
|
crypto.SSEC.CreateMetadata(metadata, sealedKey)
|
||||||
return nil
|
return nil
|
||||||
case crypto.S3.IsEncrypted(metadata):
|
default:
|
||||||
if GlobalKMS == nil {
|
return errObjectTampered
|
||||||
return errKMSNotConfigured
|
|
||||||
}
|
|
||||||
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var objectKey crypto.ObjectKey
|
|
||||||
if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newKey, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
|
|
||||||
crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (crypto.ObjectKey, error) {
|
func newEncryptMetadata(kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, ctx kms.Context) (crypto.ObjectKey, error) {
|
||||||
var sealedKey crypto.SealedKey
|
var sealedKey crypto.SealedKey
|
||||||
if sseS3 {
|
switch kind {
|
||||||
|
case crypto.S3:
|
||||||
if GlobalKMS == nil {
|
if GlobalKMS == nil {
|
||||||
return crypto.ObjectKey{}, errKMSNotConfigured
|
return crypto.ObjectKey{}, errKMSNotConfigured
|
||||||
}
|
}
|
||||||
key, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)})
|
key, err := GlobalKMS.GenerateKey("", kms.Context{bucket: path.Join(bucket, object)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crypto.ObjectKey{}, err
|
return crypto.ObjectKey{}, err
|
||||||
}
|
}
|
||||||
@ -182,15 +223,45 @@ func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]s
|
|||||||
sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
|
sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
|
||||||
crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey)
|
crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey)
|
||||||
return objectKey, nil
|
return objectKey, nil
|
||||||
|
case crypto.S3KMS:
|
||||||
|
if GlobalKMS == nil {
|
||||||
|
return crypto.ObjectKey{}, errKMSNotConfigured
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the context does not contain the bucket key
|
||||||
|
// we must add it for key generation. However,
|
||||||
|
// the context must be stored exactly like the
|
||||||
|
// client provided it. Therefore, we delete the
|
||||||
|
// bucket key, if added by us, after generating
|
||||||
|
// the key.
|
||||||
|
_, ctxContainsBucket := ctx[bucket]
|
||||||
|
if !ctxContainsBucket {
|
||||||
|
ctx[bucket] = path.Join(bucket, object)
|
||||||
|
}
|
||||||
|
key, err := GlobalKMS.GenerateKey(keyID, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return crypto.ObjectKey{}, err
|
||||||
|
}
|
||||||
|
if !ctxContainsBucket {
|
||||||
|
delete(ctx, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader)
|
||||||
|
sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object)
|
||||||
|
crypto.S3KMS.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey, ctx)
|
||||||
|
return objectKey, nil
|
||||||
|
case crypto.SSEC:
|
||||||
objectKey := crypto.GenerateKey(key, rand.Reader)
|
objectKey := crypto.GenerateKey(key, rand.Reader)
|
||||||
sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object)
|
sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object)
|
||||||
crypto.SSEC.CreateMetadata(metadata, sealedKey)
|
crypto.SSEC.CreateMetadata(metadata, sealedKey)
|
||||||
return objectKey, nil
|
return objectKey, nil
|
||||||
|
default:
|
||||||
|
return crypto.ObjectKey{}, fmt.Errorf("encryption type '%v' not supported", kind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (io.Reader, crypto.ObjectKey, error) {
|
func newEncryptReader(content io.Reader, kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, ctx crypto.Context) (io.Reader, crypto.ObjectKey, error) {
|
||||||
objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3)
|
objectEncryptionKey, err := newEncryptMetadata(kind, keyID, key, bucket, object, metadata, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, crypto.ObjectKey{}, err
|
return nil, crypto.ObjectKey{}, err
|
||||||
}
|
}
|
||||||
@ -208,14 +279,23 @@ func newEncryptReader(content io.Reader, key []byte, bucket, object string, meta
|
|||||||
func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) {
|
func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) {
|
||||||
var (
|
var (
|
||||||
key []byte
|
key []byte
|
||||||
|
keyID string
|
||||||
|
ctx crypto.Context
|
||||||
)
|
)
|
||||||
if crypto.SSEC.IsRequested(r.Header) {
|
kind, _ := crypto.IsRequested(r.Header)
|
||||||
|
switch kind {
|
||||||
|
case crypto.SSEC:
|
||||||
key, err = ParseSSECustomerRequest(r)
|
key, err = ParseSSECustomerRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
|
}
|
||||||
|
case crypto.S3KMS:
|
||||||
|
keyID, ctx, err = crypto.S3KMS.ParseHTTP(r.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = newEncryptMetadata(key, bucket, object, metadata, crypto.S3.IsRequested(r.Header))
|
_, err = newEncryptMetadata(kind, keyID, key, bucket, object, metadata, ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,24 +303,32 @@ func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[
|
|||||||
// with the client provided key. It also marks the object as client-side-encrypted
|
// with the client provided key. It also marks the object as client-side-encrypted
|
||||||
// and sets the correct headers.
|
// and sets the correct headers.
|
||||||
func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) {
|
func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) {
|
||||||
if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) {
|
|
||||||
return nil, crypto.ObjectKey{}, crypto.ErrIncompatibleEncryptionMethod
|
|
||||||
}
|
|
||||||
if r.ContentLength > encryptBufferThreshold {
|
if r.ContentLength > encryptBufferThreshold {
|
||||||
// The encryption reads in blocks of 64KB.
|
// The encryption reads in blocks of 64KB.
|
||||||
// We add a buffer on bigger files to reduce the number of syscalls upstream.
|
// We add a buffer on bigger files to reduce the number of syscalls upstream.
|
||||||
content = bufio.NewReaderSize(content, encryptBufferSize)
|
content = bufio.NewReaderSize(content, encryptBufferSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
var key []byte
|
var (
|
||||||
if crypto.SSEC.IsRequested(r.Header) {
|
key []byte
|
||||||
var err error
|
keyID string
|
||||||
|
ctx crypto.Context
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
kind, _ := crypto.IsRequested(r.Header)
|
||||||
|
if kind == crypto.SSEC {
|
||||||
key, err = ParseSSECustomerRequest(r)
|
key, err = ParseSSECustomerRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, crypto.ObjectKey{}, err
|
return nil, crypto.ObjectKey{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header))
|
if kind == crypto.S3KMS {
|
||||||
|
keyID, ctx, err = crypto.S3KMS.ParseHTTP(r.Header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, crypto.ObjectKey{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newEncryptReader(content, kind, keyID, key, bucket, object, metadata, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
||||||
@ -590,7 +678,7 @@ func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool)
|
|||||||
// Since server side copy with same source and dest just replaces the ETag, we save
|
// Since server side copy with same source and dest just replaces the ETag, we save
|
||||||
// encrypted content MD5Sum as ETag for both SSE-C and SSE-S3, we standardize the ETag
|
// encrypted content MD5Sum as ETag for both SSE-C and SSE-S3, we standardize the ETag
|
||||||
// encryption across SSE-C and SSE-S3, and only return last 32 bytes for SSE-C
|
// encryption across SSE-C and SSE-S3, and only return last 32 bytes for SSE-C
|
||||||
if crypto.SSEC.IsEncrypted(objInfo.UserDefined) && !copySource {
|
if (crypto.SSEC.IsEncrypted(objInfo.UserDefined) || crypto.S3KMS.IsEncrypted(objInfo.UserDefined)) && !copySource {
|
||||||
return objInfo.ETag[len(objInfo.ETag)-32:]
|
return objInfo.ETag[len(objInfo.ETag)-32:]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +862,7 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
|
|||||||
// disallow X-Amz-Server-Side-Encryption header on HEAD and GET
|
// disallow X-Amz-Server-Side-Encryption header on HEAD and GET
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet, http.MethodHead:
|
case http.MethodGet, http.MethodHead:
|
||||||
if crypto.S3.IsRequested(headers) {
|
if crypto.S3.IsRequested(headers) || crypto.S3KMS.IsRequested(headers) {
|
||||||
return false, errInvalidEncryptionParameters
|
return false, errInvalidEncryptionParameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -797,6 +885,12 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if crypto.S3KMS.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" {
|
||||||
|
if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) {
|
||||||
|
return encrypted, errEncryptedObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err = info.DecryptedSize(); err != nil {
|
if _, err = info.DecryptedSize(); err != nil {
|
||||||
return encrypted, err
|
return encrypted, err
|
||||||
}
|
}
|
||||||
|
@ -481,6 +481,11 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
case crypto.S3:
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
|
case crypto.S3KMS:
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
|
if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok {
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx)
|
||||||
|
}
|
||||||
case crypto.SSEC:
|
case crypto.SSEC:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
@ -705,6 +710,11 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
case crypto.S3:
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
|
case crypto.S3KMS:
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
|
if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok {
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx)
|
||||||
|
}
|
||||||
case crypto.SSEC:
|
case crypto.SSEC:
|
||||||
// Validate the SSE-C Key set in the header.
|
// Validate the SSE-C Key set in the header.
|
||||||
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
||||||
@ -869,11 +879,6 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
|
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := crypto.IsRequested(r.Header); ok {
|
if _, ok := crypto.IsRequested(r.Header); ok {
|
||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
||||||
@ -957,7 +962,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
_, err = globalBucketSSEConfigSys.Get(dstBucket)
|
_, err = globalBucketSSEConfigSys.Get(dstBucket)
|
||||||
// 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) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcOpts, dstOpts ObjectOptions
|
var srcOpts, dstOpts ObjectOptions
|
||||||
@ -1114,14 +1119,18 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
var oldKey, newKey []byte
|
var oldKey, newKey []byte
|
||||||
|
var newKeyID string
|
||||||
|
var kmsCtx crypto.Context
|
||||||
var objEncKey crypto.ObjectKey
|
var objEncKey crypto.ObjectKey
|
||||||
|
sseCopyKMS := crypto.S3KMS.IsEncrypted(srcInfo.UserDefined)
|
||||||
sseCopyS3 := crypto.S3.IsEncrypted(srcInfo.UserDefined)
|
sseCopyS3 := crypto.S3.IsEncrypted(srcInfo.UserDefined)
|
||||||
sseCopyC := crypto.SSEC.IsEncrypted(srcInfo.UserDefined) && crypto.SSECopy.IsRequested(r.Header)
|
sseCopyC := crypto.SSEC.IsEncrypted(srcInfo.UserDefined) && crypto.SSECopy.IsRequested(r.Header)
|
||||||
sseC := crypto.SSEC.IsRequested(r.Header)
|
sseC := crypto.SSEC.IsRequested(r.Header)
|
||||||
sseS3 := crypto.S3.IsRequested(r.Header)
|
sseS3 := crypto.S3.IsRequested(r.Header)
|
||||||
|
sseKMS := crypto.S3KMS.IsRequested(r.Header)
|
||||||
|
|
||||||
isSourceEncrypted := sseCopyC || sseCopyS3
|
isSourceEncrypted := sseCopyC || sseCopyS3 || sseCopyKMS
|
||||||
isTargetEncrypted := sseC || sseS3
|
isTargetEncrypted := sseC || sseS3 || sseKMS
|
||||||
|
|
||||||
if sseC {
|
if sseC {
|
||||||
newKey, err = ParseSSECustomerRequest(r)
|
newKey, err = ParseSSECustomerRequest(r)
|
||||||
@ -1130,6 +1139,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if crypto.S3KMS.IsRequested(r.Header) {
|
||||||
|
newKeyID, kmsCtx, err = crypto.S3KMS.ParseHTTP(r.Header)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If src == dst and either
|
// If src == dst and either
|
||||||
// - the object is encrypted using SSE-C and two different SSE-C keys are present
|
// - the object is encrypted using SSE-C and two different SSE-C keys are present
|
||||||
@ -1149,8 +1165,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case of SSE-S3 oldKey and newKey aren't used - the KMS manages the keys.
|
if err = rotateKey(oldKey, newKeyID, newKey, srcBucket, srcObject, encMetadata, kmsCtx); err != nil {
|
||||||
if err = rotateKey(oldKey, newKey, srcBucket, srcObject, encMetadata); err != nil {
|
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1187,7 +1202,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
if isTargetEncrypted {
|
if isTargetEncrypted {
|
||||||
var encReader io.Reader
|
var encReader io.Reader
|
||||||
encReader, objEncKey, err = newEncryptReader(srcInfo.Reader, newKey, dstBucket, dstObject, encMetadata, sseS3)
|
kind, _ := crypto.IsRequested(r.Header)
|
||||||
|
encReader, objEncKey, err = newEncryptReader(srcInfo.Reader, kind, newKeyID, newKey, dstBucket, dstObject, encMetadata, kmsCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -1415,11 +1431,6 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
|
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := crypto.IsRequested(r.Header); ok {
|
if _, ok := crypto.IsRequested(r.Header); ok {
|
||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
||||||
@ -1557,7 +1568,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
_, 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) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualSize := size
|
actualSize := size
|
||||||
@ -1688,6 +1699,14 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
case crypto.S3:
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag})
|
objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag})
|
||||||
|
case crypto.S3KMS:
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
|
if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok {
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx)
|
||||||
|
}
|
||||||
|
if len(objInfo.ETag) >= 32 && strings.Count(objInfo.ETag, "-") != 1 {
|
||||||
|
objInfo.ETag = objInfo.ETag[len(objInfo.ETag)-32:]
|
||||||
|
}
|
||||||
case crypto.SSEC:
|
case crypto.SSEC:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
@ -2027,11 +2046,6 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
|
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := crypto.IsRequested(r.Header); ok {
|
if _, ok := crypto.IsRequested(r.Header); ok {
|
||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
||||||
@ -2063,7 +2077,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
_, 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) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate storage class metadata if present
|
// Validate storage class metadata if present
|
||||||
@ -2487,11 +2501,6 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
|
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := crypto.IsRequested(r.Header); ok {
|
if _, ok := crypto.IsRequested(r.Header); ok {
|
||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
if crypto.SSEC.IsRequested(r.Header) && !objectAPI.IsEncryptionSupported() {
|
||||||
|
@ -28,7 +28,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/crypto"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"github.com/minio/minio/pkg/hash"
|
||||||
|
"github.com/minio/minio/pkg/kms"
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -236,9 +238,7 @@ func (config *TierConfigMgr) configReader() (*PutObjReader, *ObjectOptions, erro
|
|||||||
|
|
||||||
// Encrypt json encoded tier configurations
|
// Encrypt json encoded tier configurations
|
||||||
metadata := make(map[string]string)
|
metadata := make(map[string]string)
|
||||||
sseS3 := true
|
encBr, oek, err := newEncryptReader(hr, crypto.S3KMS, "", nil, minioMetaBucket, tierConfigPath, metadata, kms.Context{})
|
||||||
var extKey [32]byte
|
|
||||||
encBr, oek, err := newEncryptReader(hr, extKey[:], minioMetaBucket, tierConfigPath, metadata, sseS3)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -1203,7 +1203,7 @@ 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)
|
_, err = globalBucketSSEConfigSys.Get(bucket)
|
||||||
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
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
|
||||||
@ -1333,6 +1333,11 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
case crypto.S3:
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
|
case crypto.S3KMS:
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
|
if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok {
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx)
|
||||||
|
}
|
||||||
case crypto.SSEC:
|
case crypto.SSEC:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
@ -1494,6 +1499,11 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
case crypto.S3:
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
|
case crypto.S3KMS:
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS)
|
||||||
|
if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok {
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx)
|
||||||
|
}
|
||||||
case crypto.SSEC:
|
case crypto.SSEC:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
|
Loading…
Reference in New Issue
Block a user