| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	"crypto/rand" | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 05:46:50 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v2" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/tags" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	bucketsse "github.com/minio/minio/internal/bucket/encryption" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/lifecycle" | 
					
						
							|  |  |  | 	objectlock "github.com/minio/minio/internal/bucket/object/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/replication" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/versioning" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/crypto" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/event" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/fips" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	"github.com/minio/pkg/bucket/policy" | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	"github.com/minio/sio" | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	legacyBucketObjectLockEnabledConfigFile = "object-lock-enabled.json" | 
					
						
							|  |  |  | 	legacyBucketObjectLockEnabledConfig     = `{"x-amz-bucket-object-lock-enabled":true}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucketMetadataFile    = ".metadata.bin" | 
					
						
							|  |  |  | 	bucketMetadataFormat  = 1 | 
					
						
							|  |  |  | 	bucketMetadataVersion = 1 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 	enabledBucketObjectLockConfig = []byte(`<ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><ObjectLockEnabled>Enabled</ObjectLockEnabled></ObjectLockConfiguration>`) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	enabledBucketVersioningConfig = []byte(`<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Status>Enabled</Status></VersioningConfiguration>`) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //go:generate msgp -file $GOFILE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BucketMetadata contains bucket metadata.
 | 
					
						
							|  |  |  | // When adding/removing fields, regenerate the marshal code using the go generate above.
 | 
					
						
							|  |  |  | // Only changing meaning of fields requires a version bump.
 | 
					
						
							|  |  |  | // bucketMetadataFormat refers to the format.
 | 
					
						
							|  |  |  | // bucketMetadataVersion can be used to track a rolling upgrade of a field.
 | 
					
						
							|  |  |  | type BucketMetadata struct { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	Name                        string | 
					
						
							|  |  |  | 	Created                     time.Time | 
					
						
							|  |  |  | 	LockEnabled                 bool // legacy not used anymore.
 | 
					
						
							|  |  |  | 	PolicyConfigJSON            []byte | 
					
						
							|  |  |  | 	NotificationConfigXML       []byte | 
					
						
							|  |  |  | 	LifecycleConfigXML          []byte | 
					
						
							|  |  |  | 	ObjectLockConfigXML         []byte | 
					
						
							|  |  |  | 	VersioningConfigXML         []byte | 
					
						
							|  |  |  | 	EncryptionConfigXML         []byte | 
					
						
							|  |  |  | 	TaggingConfigXML            []byte | 
					
						
							|  |  |  | 	QuotaConfigJSON             []byte | 
					
						
							|  |  |  | 	ReplicationConfigXML        []byte | 
					
						
							|  |  |  | 	BucketTargetsConfigJSON     []byte | 
					
						
							|  |  |  | 	BucketTargetsConfigMetaJSON []byte | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 	PolicyConfigUpdatedAt       time.Time | 
					
						
							|  |  |  | 	ObjectLockConfigUpdatedAt   time.Time | 
					
						
							|  |  |  | 	EncryptionConfigUpdatedAt   time.Time | 
					
						
							|  |  |  | 	TaggingConfigUpdatedAt      time.Time | 
					
						
							|  |  |  | 	QuotaConfigUpdatedAt        time.Time | 
					
						
							|  |  |  | 	ReplicationConfigUpdatedAt  time.Time | 
					
						
							| 
									
										
										
										
											2022-05-31 17:57:57 +08:00
										 |  |  | 	VersioningConfigUpdatedAt   time.Time | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Unexported fields. Must be updated atomically.
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	policyConfig           *policy.Policy | 
					
						
							|  |  |  | 	notificationConfig     *event.Config | 
					
						
							|  |  |  | 	lifecycleConfig        *lifecycle.Lifecycle | 
					
						
							|  |  |  | 	objectLockConfig       *objectlock.Config | 
					
						
							|  |  |  | 	versioningConfig       *versioning.Versioning | 
					
						
							|  |  |  | 	sseConfig              *bucketsse.BucketSSEConfig | 
					
						
							|  |  |  | 	taggingConfig          *tags.Tags | 
					
						
							|  |  |  | 	quotaConfig            *madmin.BucketQuota | 
					
						
							|  |  |  | 	replicationConfig      *replication.Config | 
					
						
							|  |  |  | 	bucketTargetConfig     *madmin.BucketTargets | 
					
						
							|  |  |  | 	bucketTargetConfigMeta map[string]string | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newBucketMetadata creates BucketMetadata with the supplied name and Created to Now.
 | 
					
						
							|  |  |  | func newBucketMetadata(name string) BucketMetadata { | 
					
						
							|  |  |  | 	return BucketMetadata{ | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 		Name: name, | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 		notificationConfig: &event.Config{ | 
					
						
							|  |  |  | 			XMLNS: "http://s3.amazonaws.com/doc/2006-03-01/", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		quotaConfig: &madmin.BucketQuota{}, | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		versioningConfig: &versioning.Versioning{ | 
					
						
							|  |  |  | 			XMLNS: "http://s3.amazonaws.com/doc/2006-03-01/", | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 		bucketTargetConfig:     &madmin.BucketTargets{}, | 
					
						
							|  |  |  | 		bucketTargetConfigMeta: make(map[string]string), | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | // SetCreatedAt preserves the CreatedAt time for bucket across sites in site replication. It defaults to
 | 
					
						
							|  |  |  | // creation time of bucket on this cluster in all other cases.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) SetCreatedAt(createdAt time.Time) { | 
					
						
							|  |  |  | 	if b.Created.IsZero() { | 
					
						
							|  |  |  | 		b.Created = UTCNow() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !createdAt.IsZero() { | 
					
						
							|  |  |  | 		b.Created = createdAt.UTC() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | // Load - loads the metadata of bucket by name from ObjectLayer api.
 | 
					
						
							|  |  |  | // If an error is returned the returned metadata will be default initialized.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) Load(ctx context.Context, api ObjectLayer, name string) error { | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 	if name == "" { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, errors.New("bucket name cannot be empty")) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 		return errInvalidArgument | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-01-04 02:22:58 +08:00
										 |  |  | 	configFile := path.Join(bucketMetaPrefix, name, bucketMetadataFile) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	data, err := readConfig(ctx, api, configFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(data) <= 4 { | 
					
						
							|  |  |  | 		return fmt.Errorf("loadBucketMetadata: no data") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Read header
 | 
					
						
							|  |  |  | 	switch binary.LittleEndian.Uint16(data[0:2]) { | 
					
						
							|  |  |  | 	case bucketMetadataFormat: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return fmt.Errorf("loadBucketMetadata: unknown format: %d", binary.LittleEndian.Uint16(data[0:2])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch binary.LittleEndian.Uint16(data[2:4]) { | 
					
						
							|  |  |  | 	case bucketMetadataVersion: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return fmt.Errorf("loadBucketMetadata: unknown version: %d", binary.LittleEndian.Uint16(data[2:4])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// OK, parse data.
 | 
					
						
							|  |  |  | 	_, err = b.UnmarshalMsg(data[4:]) | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 	b.Name = name // in-case parsing failed for some reason, make sure bucket name is not empty.
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | func loadBucketMetadataParse(ctx context.Context, objectAPI ObjectLayer, bucket string, parse bool) (BucketMetadata, error) { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	b := newBucketMetadata(bucket) | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 	err := b.Load(ctx, objectAPI, b.Name) | 
					
						
							| 
									
										
										
										
											2021-09-23 11:06:17 +08:00
										 |  |  | 	if err != nil && !errors.Is(err, errConfigNotFound) { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		return b, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		b.defaultTimestamps() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	configs, err := b.getAllLegacyConfigs(ctx, objectAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return b, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(configs) == 0 { | 
					
						
							|  |  |  | 		if parse { | 
					
						
							|  |  |  | 			// nothing to update, parse and proceed.
 | 
					
						
							|  |  |  | 			err = b.parseAllConfigs(ctx, objectAPI) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// Old bucket without bucket metadata. Hence we migrate existing settings.
 | 
					
						
							|  |  |  | 		err = b.convertLegacyConfigs(ctx, objectAPI, configs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 		return b, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	// migrate unencrypted remote targets
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if err := b.migrateTargetConfig(ctx, objectAPI); err != nil { | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 		return b, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 	return b, nil | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | // loadBucketMetadata loads and migrates to bucket metadata.
 | 
					
						
							|  |  |  | func loadBucketMetadata(ctx context.Context, objectAPI ObjectLayer, bucket string) (BucketMetadata, error) { | 
					
						
							|  |  |  | 	return loadBucketMetadataParse(ctx, objectAPI, bucket, true) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | // parseAllConfigs will parse all configs and populate the private fields.
 | 
					
						
							|  |  |  | // The first error encountered is returned.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) parseAllConfigs(ctx context.Context, objectAPI ObjectLayer) (err error) { | 
					
						
							|  |  |  | 	if len(b.PolicyConfigJSON) != 0 { | 
					
						
							|  |  |  | 		b.policyConfig, err = policy.ParseConfig(bytes.NewReader(b.PolicyConfigJSON), b.Name) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.policyConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 	if len(b.NotificationConfigXML) != 0 { | 
					
						
							|  |  |  | 		if err = xml.Unmarshal(b.NotificationConfigXML, b.notificationConfig); err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(b.LifecycleConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.lifecycleConfig, err = lifecycle.ParseLifecycleConfig(bytes.NewReader(b.LifecycleConfigXML)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.lifecycleConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(b.EncryptionConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.sseConfig, err = bucketsse.ParseBucketSSEConfig(bytes.NewReader(b.EncryptionConfigXML)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.sseConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(b.TaggingConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.taggingConfig, err = tags.ParseBucketXML(bytes.NewReader(b.TaggingConfigXML)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.taggingConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 06:30:06 +08:00
										 |  |  | 	if bytes.Equal(b.ObjectLockConfigXML, enabledBucketObjectLockConfig) { | 
					
						
							|  |  |  | 		b.VersioningConfigXML = enabledBucketVersioningConfig | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 	if len(b.ObjectLockConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.objectLockConfig, err = objectlock.ParseObjectLockConfig(bytes.NewReader(b.ObjectLockConfigXML)) | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.objectLockConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if len(b.VersioningConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.versioningConfig, err = versioning.ParseConfig(bytes.NewReader(b.VersioningConfigXML)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	if len(b.QuotaConfigJSON) != 0 { | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 		b.quotaConfig, err = parseBucketQuota(b.Name, b.QuotaConfigJSON) | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if len(b.ReplicationConfigXML) != 0 { | 
					
						
							|  |  |  | 		b.replicationConfig, err = replication.ParseConfig(bytes.NewReader(b.ReplicationConfigXML)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		b.replicationConfig = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 	if len(b.BucketTargetsConfigJSON) != 0 { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 		b.bucketTargetConfig, err = parseBucketTargetConfig(b.Name, b.BucketTargetsConfigJSON, b.BucketTargetsConfigMetaJSON) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		b.bucketTargetConfig = &madmin.BucketTargets{} | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | func (b *BucketMetadata) getAllLegacyConfigs(ctx context.Context, objectAPI ObjectLayer) (map[string][]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	legacyConfigs := []string{ | 
					
						
							|  |  |  | 		legacyBucketObjectLockEnabledConfigFile, | 
					
						
							|  |  |  | 		bucketPolicyConfig, | 
					
						
							|  |  |  | 		bucketNotificationConfig, | 
					
						
							|  |  |  | 		bucketLifecycleConfig, | 
					
						
							|  |  |  | 		bucketQuotaConfigFile, | 
					
						
							|  |  |  | 		bucketSSEConfig, | 
					
						
							| 
									
										
										
										
											2020-06-16 13:09:39 +08:00
										 |  |  | 		bucketTaggingConfig, | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		bucketReplicationConfig, | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 		bucketTargetsFile, | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		objectLockConfig, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | 	configs := make(map[string][]byte, len(legacyConfigs)) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Handle migration from lockEnabled to newer format.
 | 
					
						
							|  |  |  | 	if b.LockEnabled { | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 		configs[objectLockConfig] = enabledBucketObjectLockConfig | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		b.LockEnabled = false // legacy value unset it
 | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 		// we are only interested in b.ObjectLockConfigXML or objectLockConfig value
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, legacyFile := range legacyConfigs { | 
					
						
							| 
									
										
										
										
											2022-01-04 02:22:58 +08:00
										 |  |  | 		configFile := path.Join(bucketMetaPrefix, b.Name, legacyFile) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		configData, err := readConfig(ctx, objectAPI, configFile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-03-07 00:56:10 +08:00
										 |  |  | 			if _, ok := err.(ObjectExistsAsDirectory); ok { | 
					
						
							| 
									
										
										
										
											2020-08-04 04:20:49 +08:00
										 |  |  | 				// in FS mode it possible that we have actual
 | 
					
						
							|  |  |  | 				// files in this folder with `.minio.sys/buckets/bucket/configFile`
 | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 			if errors.Is(err, errConfigNotFound) { | 
					
						
							|  |  |  | 				// legacy file config not found, proceed to look for new metadata.
 | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		configs[legacyFile] = configData | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | 	return configs, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 08:55:09 +08:00
										 |  |  | func (b *BucketMetadata) convertLegacyConfigs(ctx context.Context, objectAPI ObjectLayer, configs map[string][]byte) error { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	for legacyFile, configData := range configs { | 
					
						
							|  |  |  | 		switch legacyFile { | 
					
						
							|  |  |  | 		case legacyBucketObjectLockEnabledConfigFile: | 
					
						
							|  |  |  | 			if string(configData) == legacyBucketObjectLockEnabledConfig { | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 				b.ObjectLockConfigXML = enabledBucketObjectLockConfig | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				b.VersioningConfigXML = enabledBucketVersioningConfig | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 				b.LockEnabled = false // legacy value unset it
 | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 				// we are only interested in b.ObjectLockConfigXML
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case bucketPolicyConfig: | 
					
						
							|  |  |  | 			b.PolicyConfigJSON = configData | 
					
						
							|  |  |  | 		case bucketNotificationConfig: | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 			b.NotificationConfigXML = configData | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		case bucketLifecycleConfig: | 
					
						
							|  |  |  | 			b.LifecycleConfigXML = configData | 
					
						
							|  |  |  | 		case bucketSSEConfig: | 
					
						
							|  |  |  | 			b.EncryptionConfigXML = configData | 
					
						
							| 
									
										
										
										
											2020-06-16 13:09:39 +08:00
										 |  |  | 		case bucketTaggingConfig: | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 			b.TaggingConfigXML = configData | 
					
						
							|  |  |  | 		case objectLockConfig: | 
					
						
							| 
									
										
										
										
											2020-05-22 02:03:59 +08:00
										 |  |  | 			b.ObjectLockConfigXML = configData | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			b.VersioningConfigXML = enabledBucketVersioningConfig | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		case bucketQuotaConfigFile: | 
					
						
							|  |  |  | 			b.QuotaConfigJSON = configData | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		case bucketReplicationConfig: | 
					
						
							|  |  |  | 			b.ReplicationConfigXML = configData | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 		case bucketTargetsFile: | 
					
						
							|  |  |  | 			b.BucketTargetsConfigJSON = configData | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	b.defaultTimestamps() | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err := b.Save(ctx, objectAPI); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for legacyFile := range configs { | 
					
						
							| 
									
										
										
										
											2022-01-04 02:22:58 +08:00
										 |  |  | 		configFile := path.Join(bucketMetaPrefix, b.Name, legacyFile) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		if err := deleteConfig(ctx, objectAPI, configFile); err != nil && !errors.Is(err, errConfigNotFound) { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | // default timestamps to metadata Created timestamp if unset.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) defaultTimestamps() { | 
					
						
							|  |  |  | 	if b.PolicyConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.PolicyConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-31 17:57:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 	if b.EncryptionConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.EncryptionConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if b.TaggingConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.TaggingConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if b.ObjectLockConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.ObjectLockConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if b.QuotaConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.QuotaConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if b.ReplicationConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.ReplicationConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-31 17:57:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if b.VersioningConfigUpdatedAt.IsZero() { | 
					
						
							|  |  |  | 		b.VersioningConfigUpdatedAt = b.Created | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | // Save config to supplied ObjectLayer api.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) Save(ctx context.Context, api ObjectLayer) error { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	if err := b.parseAllConfigs(ctx, api); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	data := make([]byte, 4, b.Msgsize()+4) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize the header.
 | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint16(data[0:2], bucketMetadataFormat) | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint16(data[2:4], bucketMetadataVersion) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal the bucket metadata
 | 
					
						
							|  |  |  | 	data, err := b.MarshalMsg(data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-04 02:22:58 +08:00
										 |  |  | 	configFile := path.Join(bucketMetaPrefix, b.Name, bucketMetadataFile) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	return saveConfig(ctx, api, configFile, data) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | // migrate config for remote targets by encrypting data if currently unencrypted and kms is configured.
 | 
					
						
							|  |  |  | func (b *BucketMetadata) migrateTargetConfig(ctx context.Context, objectAPI ObjectLayer) error { | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	// early return if no targets or already encrypted
 | 
					
						
							|  |  |  | 	if len(b.BucketTargetsConfigJSON) == 0 || GlobalKMS == nil || len(b.BucketTargetsConfigMetaJSON) != 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:54:27 +08:00
										 |  |  | 	encBytes, metaBytes, err := encryptBucketMetadata(ctx, b.Name, b.BucketTargetsConfigJSON, kms.Context{b.Name: b.Name, bucketTargetsFile: bucketTargetsFile}) | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.BucketTargetsConfigJSON = encBytes | 
					
						
							|  |  |  | 	b.BucketTargetsConfigMetaJSON = metaBytes | 
					
						
							|  |  |  | 	return b.Save(ctx, objectAPI) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // encrypt bucket metadata if kms is configured.
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:54:27 +08:00
										 |  |  | func encryptBucketMetadata(ctx context.Context, bucket string, input []byte, kmsContext kms.Context) (output, metabytes []byte, err error) { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if GlobalKMS == nil { | 
					
						
							|  |  |  | 		output = input | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	metadata := make(map[string]string) | 
					
						
							| 
									
										
										
										
											2022-07-19 09:54:27 +08:00
										 |  |  | 	key, err := GlobalKMS.GenerateKey(ctx, "", kmsContext) | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	outbuf := bytes.NewBuffer(nil) | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) | 
					
						
							|  |  |  | 	sealedKey := objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, "") | 
					
						
							|  |  |  | 	crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey) | 
					
						
							| 
									
										
										
										
											2022-06-21 22:54:48 +08:00
										 |  |  | 	_, err = sio.Encrypt(outbuf, bytes.NewBuffer(input), sio.Config{Key: objectKey[:], MinVersion: sio.Version20, CipherSuites: fips.DARECiphers()}) | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return output, metabytes, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	metabytes, err = json.Marshal(metadata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return outbuf.Bytes(), metabytes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // decrypt bucket metadata if kms is configured.
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | func decryptBucketMetadata(input []byte, bucket string, meta map[string]string, kmsContext kms.Context) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if GlobalKMS == nil { | 
					
						
							|  |  |  | 		return nil, errKMSNotConfigured | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(meta) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	extKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, kmsContext) | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var objectKey crypto.ObjectKey | 
					
						
							|  |  |  | 	if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, ""); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	outbuf := bytes.NewBuffer(nil) | 
					
						
							| 
									
										
										
										
											2022-06-21 22:54:48 +08:00
										 |  |  | 	_, err = sio.Decrypt(outbuf, bytes.NewBuffer(input), sio.Config{Key: objectKey[:], MinVersion: sio.Version20, CipherSuites: fips.DARECiphers()}) | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	return outbuf.Bytes(), err | 
					
						
							|  |  |  | } |