| 
									
										
										
										
											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/>.
 | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-12-25 05:49:48 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2020-04-27 12:42:41 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-11-12 10:42:10 +08:00
										 |  |  | 	"unicode/utf8" | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-05-29 01:31:42 +08:00
										 |  |  | 	etcd "go.etcd.io/etcd/client/v3" | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-25 18:29:28 +08:00
										 |  |  | func handleEncryptedConfigBackend(objAPI ObjectLayer) error { | 
					
						
							| 
									
										
										
										
											2020-05-05 11:04:06 +08:00
										 |  |  | 	encrypted, err := checkBackendEncrypted(objAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("Unable to encrypt config %w", err) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 	if err = migrateConfigPrefixToEncrypted(objAPI, encrypted); err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-05 11:04:06 +08:00
										 |  |  | 		return fmt.Errorf("Unable to migrate all config at .minio.sys/config/: %w", err) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-05 11:04:06 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | const backendEncryptedFile = "backend-encrypted" | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | var backendEncryptedMigrationComplete = []byte("encrypted") | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func checkBackendEtcdEncrypted(ctx context.Context, client *etcd.Client) (bool, error) { | 
					
						
							|  |  |  | 	data, err := readKeyEtcd(ctx, client, backendEncryptedFile) | 
					
						
							|  |  |  | 	if err != nil && err != errConfigNotFound { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	return err == nil && bytes.Equal(data, backendEncryptedMigrationComplete), nil | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 	data, err := readConfig(GlobalContext, objAPI, backendEncryptedFile) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	if err != nil && err != errConfigNotFound { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	return err == nil && bytes.Equal(data, backendEncryptedMigrationComplete), nil | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 05:26:39 +08:00
										 |  |  | func migrateIAMConfigsEtcdToEncrypted(ctx context.Context, client *etcd.Client) error { | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	encrypted, err := checkBackendEtcdEncrypted(ctx, client) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 	if encrypted && GlobalKMS != nil { | 
					
						
							|  |  |  | 		stat, err := GlobalKMS.Stat() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 		logger.Info("Attempting to re-encrypt IAM users and policies on etcd with %q (%s)", stat.DefaultKey, stat.Name) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-27 12:42:41 +08:00
										 |  |  | 	listCtx, cancel := context.WithTimeout(ctx, 1*time.Minute) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r, err := client.Get(listCtx, minioConfigPrefix, etcd.WithPrefix(), etcd.WithKeysOnly()) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	for _, kv := range r.Kvs { | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		data, err := readKeyEtcd(ctx, client, string(kv.Key)) | 
					
						
							|  |  |  | 		if err == errConfigNotFound { // Perhaps not present or someone deleted it.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		if !utf8.Valid(data) { | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 			pdata, err := madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data)) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-25 07:24:12 +08:00
										 |  |  | 				if GlobalKMS != nil { | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 					pdata, err = config.DecryptBytes(GlobalKMS, data, kms.Context{ | 
					
						
							| 
									
										
										
										
											2021-06-25 07:24:12 +08:00
										 |  |  | 						minioMetaBucket: path.Join(minioMetaBucket, string(kv.Key)), | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 					}) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-25 07:24:12 +08:00
										 |  |  | 						pdata, err = config.DecryptBytes(GlobalKMS, data, kms.Context{ | 
					
						
							|  |  |  | 							minioMetaBucket: string(kv.Key), | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 						if err != nil { | 
					
						
							|  |  |  | 							return fmt.Errorf("Decrypting IAM config failed %w, possibly credentials are incorrect", err) | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 			data = pdata | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		if GlobalKMS != nil { | 
					
						
							|  |  |  | 			data, err = config.EncryptBytes(GlobalKMS, data, kms.Context{ | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 				minioMetaBucket: path.Join(minioMetaBucket, string(kv.Key)), | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		if err = saveKeyEtcd(ctx, client, string(kv.Key), data); err != nil { | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 	if encrypted && GlobalKMS != nil { | 
					
						
							|  |  |  | 		logger.Info("Migration of encrypted IAM config data completed. All data is now encrypted on etcd.") | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 	return deleteKeyEtcd(ctx, client, backendEncryptedFile) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, encrypted bool) error { | 
					
						
							|  |  |  | 	if !encrypted { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 	if encrypted && GlobalKMS != nil { | 
					
						
							|  |  |  | 		stat, err := GlobalKMS.Stat() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 		logger.Info("Attempting to re-encrypt config, IAM users and policies on MinIO with %q (%s)", stat.DefaultKey, stat.Name) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var marker string | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		res, err := objAPI.ListObjects(GlobalContext, minioMetaBucket, minioConfigPrefix, marker, "", maxObjectList) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, obj := range res.Objects { | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 			data, err := readConfig(GlobalContext, objAPI, obj.Name) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 			if !utf8.Valid(data) { | 
					
						
							| 
									
										
										
										
											2021-05-19 06:19:20 +08:00
										 |  |  | 				data, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data)) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 					return fmt.Errorf("Decrypting config failed %w, possibly credentials are incorrect", err) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 			if GlobalKMS != nil { | 
					
						
							|  |  |  | 				data, err = config.EncryptBytes(GlobalKMS, data, kms.Context{ | 
					
						
							|  |  |  | 					obj.Bucket: path.Join(obj.Bucket, obj.Name), | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 			if err = saveConfig(GlobalContext, objAPI, obj.Name, data); err != nil { | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !res.IsTruncated { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		marker = res.NextMarker | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-05 02:15:13 +08:00
										 |  |  | 	if encrypted && GlobalKMS != nil { | 
					
						
							|  |  |  | 		logger.Info("Migration of encrypted config data completed. All config data is now encrypted.") | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-24 03:09:10 +08:00
										 |  |  | 	return deleteConfig(GlobalContext, objAPI, backendEncryptedFile) | 
					
						
							| 
									
										
										
										
											2019-11-02 06:53:16 +08:00
										 |  |  | } |