mirror of
https://github.com/minio/minio.git
synced 2025-04-18 01:40:11 -04:00
Add support for bucket encryption feature (#8890)
- pkg/bucket/encryption provides support for handling bucket encryption configuration - changes under cmd/ provide support for AES256 algorithm only Co-Authored-By: Poorna <poornas@users.noreply.github.com> Co-authored-by: Harshavardhana <harsha@minio.io>
This commit is contained in:
parent
f91c072f61
commit
026265f8f7
@ -96,6 +96,7 @@ const (
|
|||||||
ErrNoSuchBucket
|
ErrNoSuchBucket
|
||||||
ErrNoSuchBucketPolicy
|
ErrNoSuchBucketPolicy
|
||||||
ErrNoSuchBucketLifecycle
|
ErrNoSuchBucketLifecycle
|
||||||
|
ErrNoSuchBucketSSEConfig
|
||||||
ErrNoSuchKey
|
ErrNoSuchKey
|
||||||
ErrNoSuchUpload
|
ErrNoSuchUpload
|
||||||
ErrNoSuchVersion
|
ErrNoSuchVersion
|
||||||
@ -487,6 +488,11 @@ var errorCodes = errorCodeMap{
|
|||||||
Description: "The bucket lifecycle configuration does not exist",
|
Description: "The bucket lifecycle configuration does not exist",
|
||||||
HTTPStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
|
ErrNoSuchBucketSSEConfig: {
|
||||||
|
Code: "ServerSideEncryptionConfigurationNotFoundError",
|
||||||
|
Description: "The server side encryption configuration was not found",
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
},
|
||||||
ErrNoSuchKey: {
|
ErrNoSuchKey: {
|
||||||
Code: "NoSuchKey",
|
Code: "NoSuchKey",
|
||||||
Description: "The specified key does not exist.",
|
Description: "The specified key does not exist.",
|
||||||
@ -1719,6 +1725,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
|
|||||||
apiErr = ErrNoSuchBucketPolicy
|
apiErr = ErrNoSuchBucketPolicy
|
||||||
case BucketLifecycleNotFound:
|
case BucketLifecycleNotFound:
|
||||||
apiErr = ErrNoSuchBucketLifecycle
|
apiErr = ErrNoSuchBucketLifecycle
|
||||||
|
case BucketSSEConfigNotFound:
|
||||||
|
apiErr = ErrNoSuchBucketSSEConfig
|
||||||
case *event.ErrInvalidEventName:
|
case *event.ErrInvalidEventName:
|
||||||
apiErr = ErrEventNotification
|
apiErr = ErrEventNotification
|
||||||
case *event.ErrInvalidARN:
|
case *event.ErrInvalidARN:
|
||||||
|
@ -138,6 +138,8 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool)
|
|||||||
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketpolicy", httpTraceAll(api.GetBucketPolicyHandler))).Queries("policy", "")
|
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketpolicy", httpTraceAll(api.GetBucketPolicyHandler))).Queries("policy", "")
|
||||||
// GetBucketLifecycle
|
// GetBucketLifecycle
|
||||||
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketlifecycle", httpTraceAll(api.GetBucketLifecycleHandler))).Queries("lifecycle", "")
|
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketlifecycle", httpTraceAll(api.GetBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||||
|
// GetBucketEncryption
|
||||||
|
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketencryption", httpTraceAll(api.GetBucketEncryptionHandler))).Queries("encryption", "")
|
||||||
|
|
||||||
// Dummy Bucket Calls
|
// Dummy Bucket Calls
|
||||||
// GetBucketACL -- this is a dummy call.
|
// GetBucketACL -- this is a dummy call.
|
||||||
@ -183,6 +185,9 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool)
|
|||||||
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listobjectsv1", httpTraceAll(api.ListObjectsV1Handler)))
|
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listobjectsv1", httpTraceAll(api.ListObjectsV1Handler)))
|
||||||
// PutBucketLifecycle
|
// PutBucketLifecycle
|
||||||
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketlifecycle", httpTraceAll(api.PutBucketLifecycleHandler))).Queries("lifecycle", "")
|
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketlifecycle", httpTraceAll(api.PutBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||||
|
// PutBucketEncryption
|
||||||
|
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketencryption", httpTraceAll(api.PutBucketEncryptionHandler))).Queries("encryption", "")
|
||||||
|
|
||||||
// PutBucketPolicy
|
// PutBucketPolicy
|
||||||
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketpolicy", httpTraceAll(api.PutBucketPolicyHandler))).Queries("policy", "")
|
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketpolicy", httpTraceAll(api.PutBucketPolicyHandler))).Queries("policy", "")
|
||||||
|
|
||||||
@ -204,6 +209,8 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool)
|
|||||||
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucketpolicy", httpTraceAll(api.DeleteBucketPolicyHandler))).Queries("policy", "")
|
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucketpolicy", httpTraceAll(api.DeleteBucketPolicyHandler))).Queries("policy", "")
|
||||||
// DeleteBucketLifecycle
|
// DeleteBucketLifecycle
|
||||||
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucketlifecycle", httpTraceAll(api.DeleteBucketLifecycleHandler))).Queries("lifecycle", "")
|
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucketlifecycle", httpTraceAll(api.DeleteBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||||
|
// DeleteBucketEncryption
|
||||||
|
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucketencryption", httpTraceAll(api.DeleteBucketEncryptionHandler))).Queries("encryption", "")
|
||||||
// DeleteBucket
|
// DeleteBucket
|
||||||
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucket", httpTraceAll(api.DeleteBucketHandler)))
|
bucket.Methods(http.MethodDelete).HandlerFunc(collectAPIStats("deletebucket", httpTraceAll(api.DeleteBucketHandler)))
|
||||||
}
|
}
|
||||||
|
180
cmd/bucket-encryption-handlers.go
Normal file
180
cmd/bucket-encryption-handlers.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Bucket Encryption configuration file name.
|
||||||
|
bucketSSEConfig = "bucket-encryption.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutBucketEncryptionHandler - Stores given bucket encryption configuration
|
||||||
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html
|
||||||
|
func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "PutBucketEncryption")
|
||||||
|
|
||||||
|
defer logger.AuditLog(w, r, "PutBucketEncryption", mustGetClaimsFromToken(r))
|
||||||
|
|
||||||
|
objAPI := api.ObjectAPI()
|
||||||
|
if objAPI == nil {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketEncryptionAction, bucket, ""); s3Error != ErrNone {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bucket exists.
|
||||||
|
if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBucketEncyrption API requires Content-Md5
|
||||||
|
if _, ok := r.Header["Content-Md5"]; !ok {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse bucket encryption xml
|
||||||
|
encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize))
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return error if KMS is not initialized
|
||||||
|
if GlobalKMS == nil {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the bucket encryption configuration in the object layer
|
||||||
|
if err = objAPI.SetBucketSSEConfig(ctx, bucket, encConfig); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the in-memory bucket encryption cache
|
||||||
|
globalBucketSSEConfigSys.Set(bucket, *encConfig)
|
||||||
|
|
||||||
|
// Update peer MinIO servers of the updated bucket encryption config
|
||||||
|
globalNotificationSys.SetBucketSSEConfig(ctx, bucket, encConfig)
|
||||||
|
|
||||||
|
writeSuccessResponseHeadersOnly(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBucketEncryptionHandler - Returns bucket policy configuration
|
||||||
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html
|
||||||
|
func (api objectAPIHandlers) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "GetBucketEncryption")
|
||||||
|
|
||||||
|
defer logger.AuditLog(w, r, "GetBucketEncryption", mustGetClaimsFromToken(r))
|
||||||
|
|
||||||
|
objAPI := api.ObjectAPI()
|
||||||
|
if objAPI == nil {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketEncryptionAction, bucket, ""); s3Error != ErrNone {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bucket exists
|
||||||
|
var err error
|
||||||
|
if _, err = objAPI.GetBucketInfo(ctx, bucket); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch bucket encryption configuration from object layer
|
||||||
|
var encConfig *bucketsse.BucketSSEConfig
|
||||||
|
if encConfig, err = objAPI.GetBucketSSEConfig(ctx, bucket); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var encConfigData []byte
|
||||||
|
if encConfigData, err = xml.Marshal(encConfig); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bucket encryption configuration to client
|
||||||
|
writeSuccessResponseXML(w, encConfigData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketEncryptionHandler - Removes bucket encryption configuration
|
||||||
|
func (api objectAPIHandlers) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "DeleteBucketEncryption")
|
||||||
|
|
||||||
|
defer logger.AuditLog(w, r, "DeleteBucketEncryption", mustGetClaimsFromToken(r))
|
||||||
|
|
||||||
|
objAPI := api.ObjectAPI()
|
||||||
|
if objAPI == nil {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketEncryptionAction, bucket, ""); s3Error != ErrNone {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bucket exists
|
||||||
|
var err error
|
||||||
|
if _, err = objAPI.GetBucketInfo(ctx, bucket); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete bucket encryption config from object layer
|
||||||
|
if err = objAPI.DeleteBucketSSEConfig(ctx, bucket); err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove entry from the in-memory bucket encryption cache
|
||||||
|
globalBucketSSEConfigSys.Remove(bucket)
|
||||||
|
// Update peer MinIO servers of the updated bucket encryption config
|
||||||
|
globalNotificationSys.RemoveBucketSSEConfig(ctx, bucket)
|
||||||
|
|
||||||
|
writeSuccessNoContent(w)
|
||||||
|
}
|
169
cmd/bucket-encryption.go
Normal file
169
cmd/bucket-encryption.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketSSEConfigSys - in-memory cache of bucket encryption config
|
||||||
|
type BucketSSEConfigSys struct {
|
||||||
|
sync.RWMutex
|
||||||
|
bucketSSEConfigMap map[string]bucketsse.BucketSSEConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBucketSSEConfigSys - Creates an empty in-memory bucket encryption configuration cache
|
||||||
|
func NewBucketSSEConfigSys() *BucketSSEConfigSys {
|
||||||
|
return &BucketSSEConfigSys{
|
||||||
|
bucketSSEConfigMap: make(map[string]bucketsse.BucketSSEConfig),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load - Loads the bucket encryption configuration for the given list of buckets
|
||||||
|
func (sys *BucketSSEConfigSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
|
||||||
|
for _, bucket := range buckets {
|
||||||
|
config, err := objAPI.GetBucketSSEConfig(context.Background(), bucket.Name)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(BucketSSEConfigNotFound); ok {
|
||||||
|
sys.Remove(bucket.Name)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sys.Set(bucket.Name, *config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init - Initializes in-memory bucket encryption config cache for the given list of buckets
|
||||||
|
func (sys *BucketSSEConfigSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
|
||||||
|
if objAPI == nil {
|
||||||
|
return errServerNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't cache bucket encryption config in gateway mode, nothing to do.
|
||||||
|
if globalIsGateway {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load bucket encryption config cache once during boot.
|
||||||
|
return sys.load(buckets, objAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get - gets bucket encryption config for the given bucket.
|
||||||
|
func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSEConfig, ok bool) {
|
||||||
|
// We don't cache bucket encryption config in gateway mode.
|
||||||
|
if globalIsGateway {
|
||||||
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
|
if objAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := objAPI.GetBucketSSEConfig(context.Background(), bucket)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *cfg, true
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.Lock()
|
||||||
|
defer sys.Unlock()
|
||||||
|
config, ok = sys.bucketSSEConfigMap[bucket]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set - sets bucket encryption config to given bucket name.
|
||||||
|
func (sys *BucketSSEConfigSys) Set(bucket string, config bucketsse.BucketSSEConfig) {
|
||||||
|
// We don't cache bucket encryption config in gateway mode.
|
||||||
|
if globalIsGateway {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.Lock()
|
||||||
|
defer sys.Unlock()
|
||||||
|
sys.bucketSSEConfigMap[bucket] = config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove - removes bucket encryption config for given bucket.
|
||||||
|
func (sys *BucketSSEConfigSys) Remove(bucket string) {
|
||||||
|
sys.Lock()
|
||||||
|
defer sys.Unlock()
|
||||||
|
|
||||||
|
delete(sys.bucketSSEConfigMap, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveBucketSSEConfig - save bucket encryption config for given bucket.
|
||||||
|
func saveBucketSSEConfig(ctx context.Context, objAPI ObjectLayer, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
data, err := xml.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path to store bucket encryption config for the given bucket.
|
||||||
|
configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig)
|
||||||
|
return saveConfig(ctx, objAPI, configFile, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBucketSSEConfig - get bucket encryption config for given bucket.
|
||||||
|
func getBucketSSEConfig(objAPI ObjectLayer, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
// Path to bucket-encryption.xml for the given bucket.
|
||||||
|
configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig)
|
||||||
|
configData, err := readConfig(context.Background(), objAPI, configFile)
|
||||||
|
if err != nil {
|
||||||
|
if err == errConfigNotFound {
|
||||||
|
err = BucketSSEConfigNotFound{Bucket: bucket}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bucketsse.ParseBucketSSEConfig(bytes.NewReader(configData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeBucketSSEConfig - removes bucket encryption config for given bucket.
|
||||||
|
func removeBucketSSEConfig(ctx context.Context, objAPI ObjectLayer, bucket string) error {
|
||||||
|
// Path to bucket-encryption.xml for the given bucket.
|
||||||
|
configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig)
|
||||||
|
|
||||||
|
if err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile); err != nil {
|
||||||
|
if _, ok := err.(ObjectNotFound); ok {
|
||||||
|
return BucketSSEConfigNotFound{Bucket: bucket}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateBucketSSEConfig parses bucket encryption configuration and validates if it is supported by MinIO.
|
||||||
|
func validateBucketSSEConfig(r io.Reader) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
encConfig, err := bucketsse.ParseBucketSSEConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(encConfig.Rules) == 1 && encConfig.Rules[0].DefaultEncryptionAction.Algorithm == bucketsse.AES256 {
|
||||||
|
return encConfig, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("Unsupported bucket encryption configuration")
|
||||||
|
}
|
70
cmd/bucket-encryption_test.go
Normal file
70
cmd/bucket-encryption_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateBucketSSEConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inputXML string
|
||||||
|
expectedErr error
|
||||||
|
shouldPass bool
|
||||||
|
}{
|
||||||
|
// MinIO supported XML
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: nil,
|
||||||
|
shouldPass: true,
|
||||||
|
},
|
||||||
|
// Unsupported XML
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>aws:kms</SSEAlgorithm>
|
||||||
|
<KMSMasterKeyID>arn:aws:kms:us-east-1:1234/5678example</KMSMasterKeyID>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: errors.New("Unsupported bucket encryption configuration"),
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
_, err := validateBucketSSEConfig(bytes.NewReader([]byte(tc.inputXML)))
|
||||||
|
if tc.shouldPass && err != nil {
|
||||||
|
t.Fatalf("Test case %d: Expected to succeed but got %s", i+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.shouldPass {
|
||||||
|
if err == nil || err != nil && err.Error() != tc.expectedErr.Error() {
|
||||||
|
t.Fatalf("Test case %d: Expected %s but got %s", i+1, tc.expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -757,8 +757,10 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
pReader := NewPutObjReader(rawReader, nil, nil)
|
pReader := NewPutObjReader(rawReader, nil, nil)
|
||||||
var objectEncryptionKey []byte
|
var objectEncryptionKey []byte
|
||||||
|
|
||||||
|
// Check if bucket encryption is enabled
|
||||||
|
_, encEnabled := 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 && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || encEnabled) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
||||||
}
|
}
|
||||||
// get gateway encryption options
|
// get gateway encryption options
|
||||||
|
18
cmd/fs-v1.go
18
cmd/fs-v1.go
@ -37,9 +37,12 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
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"
|
||||||
|
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/lock"
|
"github.com/minio/minio/pkg/lock"
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
"github.com/minio/minio/pkg/mimedb"
|
"github.com/minio/minio/pkg/mimedb"
|
||||||
@ -1292,6 +1295,21 @@ func (fs *FSObjects) DeleteBucketLifecycle(ctx context.Context, bucket string) e
|
|||||||
return removeLifecycleConfig(ctx, fs, bucket)
|
return removeLifecycleConfig(ctx, fs, bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBucketSSEConfig returns bucket encryption config on given bucket
|
||||||
|
func (fs *FSObjects) GetBucketSSEConfig(ctx context.Context, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
return getBucketSSEConfig(fs, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig sets bucket encryption config on given bucket
|
||||||
|
func (fs *FSObjects) SetBucketSSEConfig(ctx context.Context, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
return saveBucketSSEConfig(ctx, fs, bucket, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
|
||||||
|
func (fs *FSObjects) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
|
||||||
|
return removeBucketSSEConfig(ctx, fs, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
// ListObjectsV2 lists all blobs in bucket filtered by prefix
|
// ListObjectsV2 lists all blobs in bucket filtered by prefix
|
||||||
func (fs *FSObjects) ListObjectsV2(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) {
|
func (fs *FSObjects) ListObjectsV2(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) {
|
||||||
marker := continuationToken
|
marker := continuationToken
|
||||||
|
@ -21,9 +21,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -128,6 +131,21 @@ func (a GatewayUnsupported) DeleteBucketLifecycle(ctx context.Context, bucket st
|
|||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBucketSSEConfig returns bucket encryption config on given bucket
|
||||||
|
func (a GatewayUnsupported) GetBucketSSEConfig(ctx context.Context, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
return nil, NotImplemented{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig sets bucket encryption config on given bucket
|
||||||
|
func (a GatewayUnsupported) SetBucketSSEConfig(ctx context.Context, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
return NotImplemented{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
|
||||||
|
func (a GatewayUnsupported) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
|
||||||
|
return NotImplemented{}
|
||||||
|
}
|
||||||
|
|
||||||
// ReloadFormat - Not implemented stub.
|
// ReloadFormat - Not implemented stub.
|
||||||
func (a GatewayUnsupported) ReloadFormat(ctx context.Context, dryRun bool) error {
|
func (a GatewayUnsupported) ReloadFormat(ctx context.Context, dryRun bool) error {
|
||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
|
@ -95,6 +95,9 @@ const (
|
|||||||
|
|
||||||
// Limit of location constraint XML for unauthenticted PUT bucket operations.
|
// Limit of location constraint XML for unauthenticted PUT bucket operations.
|
||||||
maxLocationConstraintSize = 3 * humanize.MiByte
|
maxLocationConstraintSize = 3 * humanize.MiByte
|
||||||
|
|
||||||
|
// Maximum size of default bucket encryption configuration allowed
|
||||||
|
maxBucketSSEConfigSize = 1 * humanize.MiByte
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalCLIContext = struct {
|
var globalCLIContext = struct {
|
||||||
@ -144,7 +147,8 @@ var (
|
|||||||
globalPolicySys *PolicySys
|
globalPolicySys *PolicySys
|
||||||
globalIAMSys *IAMSys
|
globalIAMSys *IAMSys
|
||||||
|
|
||||||
globalLifecycleSys *LifecycleSys
|
globalLifecycleSys *LifecycleSys
|
||||||
|
globalBucketSSEConfigSys *BucketSSEConfigSys
|
||||||
|
|
||||||
globalStorageClass storageclass.Config
|
globalStorageClass storageclass.Config
|
||||||
globalLDAPConfig xldap.Config
|
globalLDAPConfig xldap.Config
|
||||||
|
@ -52,7 +52,7 @@ func (sys *LifecycleSys) Set(bucketName string, lifecycle lifecycle.Lifecycle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get - gets lifecycle config associated to a given bucket name.
|
// Get - gets lifecycle config associated to a given bucket name.
|
||||||
func (sys *LifecycleSys) Get(bucketName string) (lifecycle lifecycle.Lifecycle, ok bool) {
|
func (sys *LifecycleSys) Get(bucketName string) (lc lifecycle.Lifecycle, ok bool) {
|
||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
// When gateway is enabled, no cached value
|
// When gateway is enabled, no cached value
|
||||||
// is used to validate life cycle policies.
|
// is used to validate life cycle policies.
|
||||||
@ -60,14 +60,18 @@ func (sys *LifecycleSys) Get(bucketName string) (lifecycle lifecycle.Lifecycle,
|
|||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := objAPI.GetBucketLifecycle(context.Background(), bucketName)
|
l, err := objAPI.GetBucketLifecycle(context.Background(), bucketName)
|
||||||
return *l, err == nil
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *l, true
|
||||||
}
|
}
|
||||||
|
|
||||||
sys.Lock()
|
sys.Lock()
|
||||||
defer sys.Unlock()
|
defer sys.Unlock()
|
||||||
|
lc, ok = sys.bucketLifecycleMap[bucketName]
|
||||||
l, ok := sys.bucketLifecycleMap[bucketName]
|
return
|
||||||
return l, ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName string, bucketLifecycle *lifecycle.Lifecycle) error {
|
func saveLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName string, bucketLifecycle *lifecycle.Lifecycle) error {
|
||||||
@ -149,7 +153,7 @@ func (sys *LifecycleSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove - removes policy for given bucket name.
|
// Remove - removes lifecycle config for given bucket name.
|
||||||
func (sys *LifecycleSys) Remove(bucketName string) {
|
func (sys *LifecycleSys) Remove(bucketName string) {
|
||||||
sys.Lock()
|
sys.Lock()
|
||||||
defer sys.Unlock()
|
defer sys.Unlock()
|
||||||
|
@ -32,9 +32,12 @@ import (
|
|||||||
"github.com/klauspost/compress/zip"
|
"github.com/klauspost/compress/zip"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
"github.com/minio/minio/cmd/crypto"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
@ -576,6 +579,41 @@ func (sys *NotificationSys) RemoveBucketLifecycle(ctx context.Context, bucketNam
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig - calls SetBucketSSEConfig on all peers.
|
||||||
|
func (sys *NotificationSys) SetBucketSSEConfig(ctx context.Context, bucketName string,
|
||||||
|
encConfig *bucketsse.BucketSSEConfig) {
|
||||||
|
go func() {
|
||||||
|
ng := WithNPeers(len(sys.peerClients))
|
||||||
|
for idx, client := range sys.peerClients {
|
||||||
|
if client == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
client := client
|
||||||
|
ng.Go(ctx, func() error {
|
||||||
|
return client.SetBucketSSEConfig(bucketName, encConfig)
|
||||||
|
}, idx, *client.host)
|
||||||
|
}
|
||||||
|
ng.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveBucketSSEConfig - calls RemoveBucketSSEConfig on all peers.
|
||||||
|
func (sys *NotificationSys) RemoveBucketSSEConfig(ctx context.Context, bucketName string) {
|
||||||
|
go func() {
|
||||||
|
ng := WithNPeers(len(sys.peerClients))
|
||||||
|
for idx, client := range sys.peerClients {
|
||||||
|
if client == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
client := client
|
||||||
|
ng.Go(ctx, func() error {
|
||||||
|
return client.RemoveBucketSSEConfig(bucketName)
|
||||||
|
}, idx, *client.host)
|
||||||
|
}
|
||||||
|
ng.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// PutBucketNotification - calls PutBucketNotification RPC call on all peers.
|
// PutBucketNotification - calls PutBucketNotification RPC call on all peers.
|
||||||
func (sys *NotificationSys) PutBucketNotification(ctx context.Context, bucketName string, rulesMap event.RulesMap) {
|
func (sys *NotificationSys) PutBucketNotification(ctx context.Context, bucketName string, rulesMap event.RulesMap) {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -262,6 +262,13 @@ func (e BucketLifecycleNotFound) Error() string {
|
|||||||
return "No bucket life cycle found for bucket : " + e.Bucket
|
return "No bucket life cycle found for bucket : " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BucketSSEConfigNotFound - no bucket encryption config found
|
||||||
|
type BucketSSEConfigNotFound GenericError
|
||||||
|
|
||||||
|
func (e BucketSSEConfigNotFound) Error() string {
|
||||||
|
return "No bucket encryption found for bucket: " + e.Bucket
|
||||||
|
}
|
||||||
|
|
||||||
/// Bucket related errors.
|
/// Bucket related errors.
|
||||||
|
|
||||||
// BucketNameInvalid - bucketname provided is invalid.
|
// BucketNameInvalid - bucketname provided is invalid.
|
||||||
|
@ -22,9 +22,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/minio/minio-go/v6/pkg/encrypt"
|
"github.com/minio/minio-go/v6/pkg/encrypt"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -121,6 +123,11 @@ type ObjectLayer interface {
|
|||||||
GetBucketLifecycle(context.Context, string) (*lifecycle.Lifecycle, error)
|
GetBucketLifecycle(context.Context, string) (*lifecycle.Lifecycle, error)
|
||||||
DeleteBucketLifecycle(context.Context, string) error
|
DeleteBucketLifecycle(context.Context, string) error
|
||||||
|
|
||||||
|
// Bucket Encryption operations
|
||||||
|
SetBucketSSEConfig(context.Context, string, *bucketsse.BucketSSEConfig) error
|
||||||
|
GetBucketSSEConfig(context.Context, string) (*bucketsse.BucketSSEConfig, error)
|
||||||
|
DeleteBucketSSEConfig(context.Context, string) error
|
||||||
|
|
||||||
// Backend related metrics
|
// Backend related metrics
|
||||||
GetMetrics(ctx context.Context) (*Metrics, error)
|
GetMetrics(ctx context.Context) (*Metrics, error)
|
||||||
|
|
||||||
|
@ -766,8 +766,10 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if bucket encryption is enabled
|
||||||
|
_, encEnabled := 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 && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || encEnabled) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,8 +1261,10 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if bucket encryption is enabled
|
||||||
|
_, encEnabled := 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 && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) {
|
if (globalAutoEncryption || encEnabled) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) {
|
||||||
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1426,9 +1430,10 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Check if bucket encryption is enabled
|
||||||
|
_, encEnabled := 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 && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) {
|
if (globalAutoEncryption || encEnabled) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) {
|
||||||
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/minio/minio/cmd/http"
|
"github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/cmd/rest"
|
"github.com/minio/minio/cmd/rest"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
@ -411,6 +412,37 @@ func (client *peerRESTClient) SetBucketLifecycle(bucket string, bucketLifecycle
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveBucketSSEConfig - Remove bucket encryption configuration on the peer node
|
||||||
|
func (client *peerRESTClient) RemoveBucketSSEConfig(bucket string) error {
|
||||||
|
values := make(url.Values)
|
||||||
|
values.Set(peerRESTBucket, bucket)
|
||||||
|
respBody, err := client.call(peerRESTMethodBucketEncryptionRemove, values, nil, -1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer http.DrainBody(respBody)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig - Set bucket encryption configuration on the peer node
|
||||||
|
func (client *peerRESTClient) SetBucketSSEConfig(bucket string, encConfig *bucketsse.BucketSSEConfig) error {
|
||||||
|
values := make(url.Values)
|
||||||
|
values.Set(peerRESTBucket, bucket)
|
||||||
|
|
||||||
|
var reader bytes.Buffer
|
||||||
|
err := gob.NewEncoder(&reader).Encode(encConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
respBody, err := client.call(peerRESTMethodBucketEncryptionSet, values, &reader, -1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer http.DrainBody(respBody)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PutBucketNotification - Put bucket notification on the peer node.
|
// PutBucketNotification - Put bucket notification on the peer node.
|
||||||
func (client *peerRESTClient) PutBucketNotification(bucket string, rulesMap event.RulesMap) error {
|
func (client *peerRESTClient) PutBucketNotification(bucket string, rulesMap event.RulesMap) error {
|
||||||
values := make(url.Values)
|
values := make(url.Values)
|
||||||
|
@ -56,6 +56,8 @@ const (
|
|||||||
peerRESTMethodListen = "/listen"
|
peerRESTMethodListen = "/listen"
|
||||||
peerRESTMethodBucketLifecycleSet = "/setbucketlifecycle"
|
peerRESTMethodBucketLifecycleSet = "/setbucketlifecycle"
|
||||||
peerRESTMethodBucketLifecycleRemove = "/removebucketlifecycle"
|
peerRESTMethodBucketLifecycleRemove = "/removebucketlifecycle"
|
||||||
|
peerRESTMethodBucketEncryptionSet = "/setbucketencryption"
|
||||||
|
peerRESTMethodBucketEncryptionRemove = "/removebucketencryption"
|
||||||
peerRESTMethodLog = "/log"
|
peerRESTMethodLog = "/log"
|
||||||
peerRESTMethodHardwareCPUInfo = "/cpuhardwareinfo"
|
peerRESTMethodHardwareCPUInfo = "/cpuhardwareinfo"
|
||||||
peerRESTMethodHardwareNetworkInfo = "/networkhardwareinfo"
|
peerRESTMethodHardwareNetworkInfo = "/networkhardwareinfo"
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
@ -693,6 +694,49 @@ func (s *peerRESTServer) SetBucketLifecycleHandler(w http.ResponseWriter, r *htt
|
|||||||
w.(http.Flusher).Flush()
|
w.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveBucketSSEConfigHandler - Remove bucket encryption.
|
||||||
|
func (s *peerRESTServer) RemoveBucketSSEConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !s.IsValid(w, r) {
|
||||||
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucketName := vars[peerRESTBucket]
|
||||||
|
if bucketName == "" {
|
||||||
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
globalBucketSSEConfigSys.Remove(bucketName)
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfigHandler - Set bucket encryption.
|
||||||
|
func (s *peerRESTServer) SetBucketSSEConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucketName := vars[peerRESTBucket]
|
||||||
|
if bucketName == "" {
|
||||||
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var encConfig bucketsse.BucketSSEConfig
|
||||||
|
if r.ContentLength < 0 {
|
||||||
|
s.writeErrorResponse(w, errInvalidArgument)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := gob.NewDecoder(r.Body).Decode(&encConfig)
|
||||||
|
if err != nil {
|
||||||
|
s.writeErrorResponse(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
globalBucketSSEConfigSys.Set(bucketName, encConfig)
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
type remoteTargetExistsResp struct {
|
type remoteTargetExistsResp struct {
|
||||||
Exists bool
|
Exists bool
|
||||||
}
|
}
|
||||||
@ -1179,6 +1223,8 @@ func registerPeerRESTHandlers(router *mux.Router) {
|
|||||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadFormat).HandlerFunc(httpTraceHdrs(server.ReloadFormatHandler)).Queries(restQueries(peerRESTDryRun)...)
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadFormat).HandlerFunc(httpTraceHdrs(server.ReloadFormatHandler)).Queries(restQueries(peerRESTDryRun)...)
|
||||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketLifecycleSet).HandlerFunc(httpTraceHdrs(server.SetBucketLifecycleHandler)).Queries(restQueries(peerRESTBucket)...)
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketLifecycleSet).HandlerFunc(httpTraceHdrs(server.SetBucketLifecycleHandler)).Queries(restQueries(peerRESTBucket)...)
|
||||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketLifecycleRemove).HandlerFunc(httpTraceHdrs(server.RemoveBucketLifecycleHandler)).Queries(restQueries(peerRESTBucket)...)
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketLifecycleRemove).HandlerFunc(httpTraceHdrs(server.RemoveBucketLifecycleHandler)).Queries(restQueries(peerRESTBucket)...)
|
||||||
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketEncryptionSet).HandlerFunc(httpTraceHdrs(server.SetBucketSSEConfigHandler)).Queries(restQueries(peerRESTBucket)...)
|
||||||
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBucketEncryptionRemove).HandlerFunc(httpTraceHdrs(server.RemoveBucketSSEConfigHandler)).Queries(restQueries(peerRESTBucket)...)
|
||||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBackgroundOpsStatus).HandlerFunc(server.BackgroundOpsStatusHandler)
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBackgroundOpsStatus).HandlerFunc(server.BackgroundOpsStatusHandler)
|
||||||
|
|
||||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodTrace).HandlerFunc(server.TraceHandler)
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodTrace).HandlerFunc(server.TraceHandler)
|
||||||
|
@ -159,6 +159,9 @@ func newAllSubsystems() {
|
|||||||
|
|
||||||
// Create new lifecycle system.
|
// Create new lifecycle system.
|
||||||
globalLifecycleSys = NewLifecycleSys()
|
globalLifecycleSys = NewLifecycleSys()
|
||||||
|
|
||||||
|
// Create new bucket encryption subsystem
|
||||||
|
globalBucketSSEConfigSys = NewBucketSSEConfigSys()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSafeMode(buckets []BucketInfo) (err error) {
|
func initSafeMode(buckets []BucketInfo) (err error) {
|
||||||
@ -280,6 +283,10 @@ func initAllSubsystems(buckets []BucketInfo, newObject ObjectLayer) (err error)
|
|||||||
return fmt.Errorf("Unable to initialize lifecycle system: %w", err)
|
return fmt.Errorf("Unable to initialize lifecycle system: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize bucket encryption subsystem.
|
||||||
|
if err = globalBucketSSEConfigSys.Init(buckets, newObject); err != nil {
|
||||||
|
return fmt.Errorf("Unable to initialize bucket encryption subsystem: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,6 +374,9 @@ func UnstartedTestServer(t TestErrHandler, instanceType string) TestServer {
|
|||||||
globalLifecycleSys = NewLifecycleSys()
|
globalLifecycleSys = NewLifecycleSys()
|
||||||
globalLifecycleSys.Init(buckets, objLayer)
|
globalLifecycleSys.Init(buckets, objLayer)
|
||||||
|
|
||||||
|
globalBucketSSEConfigSys = NewBucketSSEConfigSys()
|
||||||
|
globalBucketSSEConfigSys.Init(buckets, objLayer)
|
||||||
|
|
||||||
return testServer
|
return testServer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1977,6 +1980,9 @@ func ExecObjectLayerTest(t TestErrHandler, objTest objTestType) {
|
|||||||
globalPolicySys = NewPolicySys()
|
globalPolicySys = NewPolicySys()
|
||||||
globalPolicySys.Init(buckets, objLayer)
|
globalPolicySys.Init(buckets, objLayer)
|
||||||
|
|
||||||
|
globalBucketSSEConfigSys = NewBucketSSEConfigSys()
|
||||||
|
globalBucketSSEConfigSys.Init(buckets, objLayer)
|
||||||
|
|
||||||
// Executing the object layer tests for single node setup.
|
// Executing the object layer tests for single node setup.
|
||||||
objTest(objLayer, FSTestStr, t)
|
objTest(objLayer, FSTestStr, t)
|
||||||
|
|
||||||
|
@ -987,7 +987,9 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalAutoEncryption && !crypto.SSEC.IsRequested(r.Header) {
|
// Check if bucket encryption is enabled
|
||||||
|
_, encEnabled := globalBucketSSEConfigSys.Get(bucket)
|
||||||
|
if (globalAutoEncryption || encEnabled) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,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/bpool"
|
"github.com/minio/minio/pkg/bpool"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
@ -597,6 +598,21 @@ func (s *xlSets) DeleteBucketLifecycle(ctx context.Context, bucket string) error
|
|||||||
return removeLifecycleConfig(ctx, s, bucket)
|
return removeLifecycleConfig(ctx, s, bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBucketSSEConfig returns bucket encryption config on given bucket
|
||||||
|
func (s *xlSets) GetBucketSSEConfig(ctx context.Context, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
return getBucketSSEConfig(s, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig sets bucket encryption config on given bucket
|
||||||
|
func (s *xlSets) SetBucketSSEConfig(ctx context.Context, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
return saveBucketSSEConfig(ctx, s, bucket, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
|
||||||
|
func (s *xlSets) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
|
||||||
|
return removeBucketSSEConfig(ctx, s, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
||||||
func (s *xlSets) IsNotificationSupported() bool {
|
func (s *xlSets) IsNotificationSupported() bool {
|
||||||
return s.getHashedSet("").IsNotificationSupported()
|
return s.getHashedSet("").IsNotificationSupported()
|
||||||
|
@ -22,8 +22,10 @@ import (
|
|||||||
|
|
||||||
"github.com/minio/minio-go/v6/pkg/s3utils"
|
"github.com/minio/minio-go/v6/pkg/s3utils"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -293,6 +295,21 @@ func (xl xlObjects) DeleteBucketLifecycle(ctx context.Context, bucket string) er
|
|||||||
return removeLifecycleConfig(ctx, xl, bucket)
|
return removeLifecycleConfig(ctx, xl, bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBucketSSEConfig returns bucket encryption config on given bucket
|
||||||
|
func (xl xlObjects) GetBucketSSEConfig(ctx context.Context, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
return getBucketSSEConfig(xl, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig sets bucket encryption config on given bucket
|
||||||
|
func (xl xlObjects) SetBucketSSEConfig(ctx context.Context, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
return saveBucketSSEConfig(ctx, xl, bucket, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
|
||||||
|
func (xl xlObjects) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
|
||||||
|
return removeBucketSSEConfig(ctx, xl, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
||||||
func (xl xlObjects) IsNotificationSupported() bool {
|
func (xl xlObjects) IsNotificationSupported() bool {
|
||||||
return true
|
return true
|
||||||
|
@ -28,6 +28,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"
|
||||||
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/object/tagging"
|
"github.com/minio/minio/pkg/bucket/object/tagging"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
@ -1147,6 +1148,21 @@ func (z *xlZones) DeleteBucketLifecycle(ctx context.Context, bucket string) erro
|
|||||||
return removeLifecycleConfig(ctx, z, bucket)
|
return removeLifecycleConfig(ctx, z, bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBucketSSEConfig returns bucket encryption config on given bucket
|
||||||
|
func (z *xlZones) GetBucketSSEConfig(ctx context.Context, bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||||
|
return getBucketSSEConfig(z, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBucketSSEConfig sets bucket encryption config on given bucket
|
||||||
|
func (z *xlZones) SetBucketSSEConfig(ctx context.Context, bucket string, config *bucketsse.BucketSSEConfig) error {
|
||||||
|
return saveBucketSSEConfig(ctx, z, bucket, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
|
||||||
|
func (z *xlZones) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
|
||||||
|
return removeBucketSSEConfig(ctx, z, bucket)
|
||||||
|
}
|
||||||
|
|
||||||
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
||||||
func (z *xlZones) IsNotificationSupported() bool {
|
func (z *xlZones) IsNotificationSupported() bool {
|
||||||
return true
|
return true
|
||||||
|
103
pkg/bucket/encryption/bucket-sse-config.go
Normal file
103
pkg/bucket/encryption/bucket-sse-config.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AES256 is used with SSE-S3
|
||||||
|
AES256 SSEAlgorithm = "AES256"
|
||||||
|
// AWSKms is used with SSE-KMS
|
||||||
|
AWSKms SSEAlgorithm = "aws:kms"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSEAlgorithm - represents valid SSE algorithms supported; currently only AES256 is supported
|
||||||
|
type SSEAlgorithm string
|
||||||
|
|
||||||
|
// UnmarshalXML - Unmarshals XML tag to valid SSE algorithm
|
||||||
|
func (alg *SSEAlgorithm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
var s string
|
||||||
|
if err := d.DecodeElement(&s, &start); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case string(AES256):
|
||||||
|
*alg = AES256
|
||||||
|
case string(AWSKms):
|
||||||
|
*alg = AWSKms
|
||||||
|
default:
|
||||||
|
return errors.New("Unknown SSE algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalXML - Marshals given SSE algorithm to valid XML
|
||||||
|
func (alg *SSEAlgorithm) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||||
|
return e.EncodeElement(string(*alg), start)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptionAction - for ApplyServerSideEncryptionByDefault XML tag
|
||||||
|
type EncryptionAction struct {
|
||||||
|
Algorithm SSEAlgorithm `xml:"SSEAlgorithm"`
|
||||||
|
MasterKeyID string `xml:"KMSMasterKeyID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSERule - for ServerSideEncryptionConfiguration XML tag
|
||||||
|
type SSERule struct {
|
||||||
|
DefaultEncryptionAction EncryptionAction `xml:"ApplyServerSideEncryptionByDefault"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BucketSSEConfig - represents default bucket encryption configuration
|
||||||
|
type BucketSSEConfig struct {
|
||||||
|
XMLName xml.Name `xml:"ServerSideEncryptionConfiguration"`
|
||||||
|
Rules []SSERule `xml:"Rule"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBucketSSEConfig - Decodes given XML to a valid default bucket encryption config
|
||||||
|
func ParseBucketSSEConfig(r io.Reader) (*BucketSSEConfig, error) {
|
||||||
|
var config BucketSSEConfig
|
||||||
|
err := xml.NewDecoder(r).Decode(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates server-side encryption config rules
|
||||||
|
// Only one rule is allowed on AWS S3
|
||||||
|
if len(config.Rules) != 1 {
|
||||||
|
return nil, errors.New("Only one server-side encryption rule is allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range config.Rules {
|
||||||
|
switch rule.DefaultEncryptionAction.Algorithm {
|
||||||
|
case AES256:
|
||||||
|
if rule.DefaultEncryptionAction.MasterKeyID != "" {
|
||||||
|
return nil, errors.New("MasterKeyID is allowed with aws:kms only")
|
||||||
|
}
|
||||||
|
case AWSKms:
|
||||||
|
if rule.DefaultEncryptionAction.MasterKeyID == "" {
|
||||||
|
return nil, errors.New("MasterKeyID is missing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &config, nil
|
||||||
|
}
|
159
pkg/bucket/encryption/bucket-sse-config_test.go
Normal file
159
pkg/bucket/encryption/bucket-sse-config_test.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestParseBucketSSEConfig performs basic sanity tests on ParseBucketSSEConfig
|
||||||
|
func TestParseBucketSSEConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inputXML string
|
||||||
|
expectedErr error
|
||||||
|
shouldPass bool
|
||||||
|
}{
|
||||||
|
// 1. Valid XML SSE-S3
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: nil,
|
||||||
|
shouldPass: true,
|
||||||
|
},
|
||||||
|
// 2. Valid XML SSE-KMS
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>aws:kms</SSEAlgorithm>
|
||||||
|
<KMSMasterKeyID>arn:aws:kms:us-east-1:1234/5678example</KMSMasterKeyID>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: nil,
|
||||||
|
shouldPass: true,
|
||||||
|
},
|
||||||
|
// 3. Invalid - more than one rule
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: errors.New("Only one server-side encryption rule is allowed"),
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
// 4. Invalid XML - master key ID present in AES256
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
<KMSMasterKeyID>arn:aws:kms:us-east-1:1234/5678example</KMSMasterKeyID>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: errors.New("MasterKeyID is allowed with aws:kms only"),
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
// 5. Invalid XML - master key ID not found in aws:kms algorithm
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>aws:kms</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: errors.New("MasterKeyID is missing"),
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
// 6. Invalid Algorithm
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>InvalidAlgorithm</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: errors.New("Unknown SSE algorithm"),
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
// 7. Allow missing namespace
|
||||||
|
{
|
||||||
|
inputXML: `<ServerSideEncryptionConfiguration>
|
||||||
|
<Rule>
|
||||||
|
<ApplyServerSideEncryptionByDefault>
|
||||||
|
<SSEAlgorithm>AES256</SSEAlgorithm>
|
||||||
|
</ApplyServerSideEncryptionByDefault>
|
||||||
|
</Rule>
|
||||||
|
</ServerSideEncryptionConfiguration>`,
|
||||||
|
expectedErr: nil,
|
||||||
|
shouldPass: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualConfig := &BucketSSEConfig{
|
||||||
|
XMLName: xml.Name{
|
||||||
|
Local: "ServerSideEncryptionConfiguration",
|
||||||
|
},
|
||||||
|
Rules: []SSERule{
|
||||||
|
{
|
||||||
|
DefaultEncryptionAction: EncryptionAction{
|
||||||
|
Algorithm: AES256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
_, err := ParseBucketSSEConfig(bytes.NewReader([]byte(tc.inputXML)))
|
||||||
|
if tc.shouldPass && err != nil {
|
||||||
|
t.Fatalf("Test case %d: Expected to succeed but got %s", i+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.shouldPass {
|
||||||
|
if err == nil || err != nil && err.Error() != tc.expectedErr.Error() {
|
||||||
|
t.Fatalf("Test case %d: Expected %s but got %s", i+1, tc.expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.shouldPass {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if actualXML, err := xml.Marshal(actualConfig); err != nil && bytes.Equal(actualXML, []byte(tc.inputXML)) {
|
||||||
|
t.Fatalf("Test case %d: Expected config %s but got %s", i+1, string(actualXML), tc.inputXML)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -113,6 +113,11 @@ const (
|
|||||||
PutObjectTaggingAction = "s3:PutObjectTagging"
|
PutObjectTaggingAction = "s3:PutObjectTagging"
|
||||||
// DeleteObjectTaggingAction - Delete Object Tags API action
|
// DeleteObjectTaggingAction - Delete Object Tags API action
|
||||||
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
||||||
|
|
||||||
|
// PutBucketEncryptionAction - PutBucketEncryption REST API action
|
||||||
|
PutBucketEncryptionAction = "s3:PutEncryptionConfiguration"
|
||||||
|
// GetBucketEncryptionAction - GetBucketEncryption REST API action
|
||||||
|
GetBucketEncryptionAction = "s3:GetEncryptionConfiguration"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isObjectAction - returns whether action is object type or not.
|
// isObjectAction - returns whether action is object type or not.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user