add support for SSE-S3 bulk ETag decryption (#14627)

This commit adds support for bulk ETag
decryption for SSE-S3 encrypted objects.

If KES supports a bulk decryption API, then
MinIO will check whether its policy grants
access to this API. If so, MinIO will use
a bulk API call instead of sending encrypted
ETags serially to KES.

Note that MinIO will not use the KES bulk API
if its client certificate is an admin identity.

MinIO will process object listings in batches.
A batch has a configurable size that can be set
via `MINIO_KMS_KES_BULK_API_BATCH_SIZE=N`.
It defaults to `500`.

This env. variable is experimental and may be
renamed / removed in the future.

Signed-off-by: Andreas Auernhammer <hi@aead.dev>
This commit is contained in:
Andreas Auernhammer
2022-03-25 23:01:41 +01:00
committed by GitHub
parent 3970204009
commit 4d2fc530d0
9 changed files with 252 additions and 60 deletions

View File

@@ -84,6 +84,63 @@ func (s3 sses3) UnsealObjectKey(KMS kms.KMS, metadata map[string]string, bucket,
return key, err
}
// UnsealObjectsKeys extracts and decrypts all sealed object keys
// from the metadata using the KMS and returns the decrypted object
// keys.
//
// The metadata, buckets and objects slices must have the same length.
func (s3 sses3) UnsealObjectKeys(KMS kms.KMS, metadata []map[string]string, buckets, objects []string) ([]ObjectKey, error) {
if len(metadata) != len(buckets) || len(metadata) != len(objects) {
return nil, Errorf("invalid metadata/object count: %d != %d != %d", len(metadata), len(buckets), len(objects))
}
keyIDs := make([]string, 0, len(metadata))
kmsKeys := make([][]byte, 0, len(metadata))
sealedKeys := make([]SealedKey, 0, len(metadata))
sameKeyID := true
for i := range metadata {
keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata[i])
if err != nil {
return nil, err
}
keyIDs = append(keyIDs, keyID)
kmsKeys = append(kmsKeys, kmsKey)
sealedKeys = append(sealedKeys, sealedKey)
if i > 0 && keyID != keyIDs[i-1] {
sameKeyID = false
}
}
if sameKeyID {
contexts := make([]kms.Context, 0, len(keyIDs))
for i := range buckets {
contexts = append(contexts, kms.Context{buckets[i]: path.Join(buckets[i], objects[i])})
}
unsealKeys, err := KMS.DecryptAll(keyIDs[0], kmsKeys, contexts)
if err != nil {
return nil, err
}
keys := make([]ObjectKey, len(unsealKeys))
for i := range keys {
if err := keys[i].Unseal(unsealKeys[i], sealedKeys[i], s3.String(), buckets[i], objects[i]); err != nil {
return nil, err
}
}
return keys, nil
}
keys := make([]ObjectKey, 0, len(keyIDs))
for i := range keyIDs {
key, err := s3.UnsealObjectKey(KMS, metadata[i], buckets[i], objects[i])
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
}
// CreateMetadata encodes the sealed object key into the metadata and returns
// 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