storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/config-encrypted.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"path"
    23  	"unicode/utf8"
    24  
    25  	"storj.io/minio/cmd/config"
    26  	"storj.io/minio/cmd/logger"
    27  	"storj.io/minio/pkg/auth"
    28  	"storj.io/minio/pkg/kms"
    29  	"storj.io/minio/pkg/madmin"
    30  )
    31  
    32  func handleEncryptedConfigBackend(objAPI ObjectLayer) error {
    33  	encrypted, err := checkBackendEncrypted(objAPI)
    34  	if err != nil {
    35  		return fmt.Errorf("Unable to encrypt config %w", err)
    36  	}
    37  	if err = migrateConfigPrefixToEncrypted(objAPI, encrypted); err != nil {
    38  		return fmt.Errorf("Unable to migrate all config at .minio.sys/config/: %w", err)
    39  	}
    40  	return nil
    41  }
    42  
    43  const backendEncryptedFile = "backend-encrypted"
    44  
    45  var backendEncryptedMigrationComplete = []byte("encrypted")
    46  
    47  func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) {
    48  	data, err := readConfig(GlobalContext, objAPI, backendEncryptedFile)
    49  	if err != nil && err != errConfigNotFound {
    50  		return false, err
    51  	}
    52  	return err == nil && bytes.Equal(data, backendEncryptedMigrationComplete), nil
    53  }
    54  
    55  // decryptData - decrypts input data with more that one credentials,
    56  func decryptData(edata []byte, creds ...auth.Credentials) ([]byte, error) {
    57  	var err error
    58  	var data []byte
    59  	for _, cred := range creds {
    60  		data, err = madmin.DecryptData(cred.String(), bytes.NewReader(edata))
    61  		if err != nil {
    62  			if err == madmin.ErrMaliciousData {
    63  				continue
    64  			}
    65  			return nil, err
    66  		}
    67  		break
    68  	}
    69  	return data, err
    70  }
    71  
    72  func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, encrypted bool) error {
    73  	if !encrypted {
    74  		return nil
    75  	}
    76  	if encrypted {
    77  		if GlobalKMS != nil {
    78  			stat, err := GlobalKMS.Stat()
    79  			if err != nil {
    80  				return err
    81  			}
    82  			logger.Info("Attempting to re-encrypt config, IAM users and policies on MinIO with %q (%s)", stat.DefaultKey, stat.Name)
    83  		} else {
    84  			logger.Info("Attempting to migrate encrypted config, IAM users and policies on MinIO to a plaintext format. To encrypt all MinIO config data a KMS is needed")
    85  		}
    86  	}
    87  
    88  	var marker string
    89  	for {
    90  		res, err := objAPI.ListObjects(GlobalContext, minioMetaBucket, minioConfigPrefix, marker, "", maxObjectList)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		for _, obj := range res.Objects {
    95  			data, err := readConfig(GlobalContext, objAPI, obj.Name)
    96  			if err != nil {
    97  				return err
    98  			}
    99  
   100  			if !utf8.Valid(data) {
   101  				data, err = decryptData(data, globalActiveCred)
   102  				if err != nil {
   103  					return fmt.Errorf("Decrypting config failed %w, possibly credentials are incorrect", err)
   104  				}
   105  			}
   106  			if GlobalKMS != nil {
   107  				data, err = config.EncryptBytes(GlobalKMS, data, kms.Context{
   108  					obj.Bucket: path.Join(obj.Bucket, obj.Name),
   109  				})
   110  				if err != nil {
   111  					return err
   112  				}
   113  			}
   114  			if err = saveConfig(GlobalContext, objAPI, obj.Name, data); err != nil {
   115  				return err
   116  			}
   117  		}
   118  
   119  		if !res.IsTruncated {
   120  			break
   121  		}
   122  		marker = res.NextMarker
   123  	}
   124  	if encrypted {
   125  		if GlobalKMS != nil {
   126  			logger.Info("Migration of encrypted config data completed. All config data is now encrypted with the KMS")
   127  		} else {
   128  			logger.Info("Migration of encrypted config data completed. All config data is now stored in plaintext")
   129  		}
   130  	}
   131  	return deleteConfig(GlobalContext, globalObjectAPI, backendEncryptedFile)
   132  }