mirror of
https://github.com/minio/minio.git
synced 2025-11-09 05:34:56 -05:00
Migrate all backend at .minio.sys/config to encrypted backend (#8474)
- Supports migrating only when the credential ENVs are set, so any FS mode deployments which do not have ENVs set will continue to remain as is. - Credential ENVs can be rotated using MINIO_ACCESS_KEY_OLD and MINIO_SECRET_KEY_OLD envs, in such scenarios it allowed to rotate the encrypted content to a new admin key.
This commit is contained in:
committed by
kannappanr
parent
fa325665b1
commit
d28bcb4f84
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/minio/minio-go/v6/pkg/set"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/certs"
|
||||
"github.com/minio/minio/pkg/env"
|
||||
)
|
||||
@@ -202,6 +203,18 @@ func handleCommonEnvVars() {
|
||||
// or is not set to 'off', if MINIO_UPDATE is set to 'off' then
|
||||
// in-place update is off.
|
||||
globalInplaceUpdateDisabled = strings.EqualFold(env.Get(config.EnvUpdate, config.StateOn), config.StateOff)
|
||||
|
||||
accessKey := env.Get(config.EnvAccessKey, "")
|
||||
secretKey := env.Get(config.EnvSecretKey, "")
|
||||
if accessKey != "" && secretKey != "" {
|
||||
cred, err := auth.CreateCredentials(accessKey, secretKey)
|
||||
if err != nil {
|
||||
logger.Fatal(config.ErrInvalidCredentials(err),
|
||||
"Unable to validate credentials inherited from the shell environment")
|
||||
}
|
||||
globalActiveCred = cred
|
||||
globalConfigEncrypted = true
|
||||
}
|
||||
}
|
||||
|
||||
func logStartupMessage(msg string) {
|
||||
|
||||
308
cmd/config-encrypted.go
Normal file
308
cmd/config-encrypted.go
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2019 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"
|
||||
"strings"
|
||||
|
||||
etcd "github.com/coreos/etcd/clientv3"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/env"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
)
|
||||
|
||||
func handleEncryptedConfigBackend(objAPI ObjectLayer, server bool) error {
|
||||
if !server {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If its server mode or nas gateway, migrate the backend.
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
var encrypted bool
|
||||
var err error
|
||||
|
||||
// Migrating Config backend needs a retry mechanism for
|
||||
// the following reasons:
|
||||
// - Read quorum is lost just after the initialization
|
||||
// of the object layer.
|
||||
for range newRetryTimerSimple(doneCh) {
|
||||
if encrypted, err = checkBackendEncrypted(objAPI); err != nil {
|
||||
if err == errDiskNotFound ||
|
||||
strings.Contains(err.Error(), InsufficientReadQuorum{}.Error()) ||
|
||||
strings.Contains(err.Error(), InsufficientWriteQuorum{}.Error()) {
|
||||
logger.Info("Waiting for config backend to be encrypted..")
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if encrypted {
|
||||
// backend is encrypted, but credentials are not specified
|
||||
// we shall fail right here. if not proceed forward.
|
||||
if !globalConfigEncrypted || !globalActiveCred.IsValid() {
|
||||
return config.ErrMissingCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
} else {
|
||||
// backend is not yet encrypted, check if encryption of
|
||||
// backend is requested if not return nil and proceed
|
||||
// forward.
|
||||
if !globalConfigEncrypted {
|
||||
return nil
|
||||
}
|
||||
if !globalActiveCred.IsValid() {
|
||||
return errInvalidArgument
|
||||
}
|
||||
}
|
||||
|
||||
accessKeyOld := env.Get(config.EnvAccessKeyOld, "")
|
||||
secretKeyOld := env.Get(config.EnvSecretKeyOld, "")
|
||||
var activeCredOld auth.Credentials
|
||||
if accessKeyOld != "" && secretKeyOld != "" {
|
||||
activeCredOld, err = auth.CreateCredentials(accessKeyOld, secretKeyOld)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Migrating Config backend needs a retry mechanism for
|
||||
// the following reasons:
|
||||
// - Read quorum is lost just after the initialization
|
||||
// of the object layer.
|
||||
for range newRetryTimerSimple(doneCh) {
|
||||
// Migrate IAM configuration
|
||||
if err = migrateConfigPrefixToEncrypted(objAPI, activeCredOld, encrypted); err != nil {
|
||||
if err == errDiskNotFound ||
|
||||
strings.Contains(err.Error(), InsufficientReadQuorum{}.Error()) ||
|
||||
strings.Contains(err.Error(), InsufficientWriteQuorum{}.Error()) {
|
||||
logger.Info("Waiting for config backend to be encrypted..")
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
backendEncryptedFile = "backend-encrypted"
|
||||
)
|
||||
|
||||
var (
|
||||
backendEncryptedFileValue = []byte("encrypted")
|
||||
)
|
||||
|
||||
func checkBackendEtcdEncrypted(ctx context.Context, client *etcd.Client) (bool, error) {
|
||||
data, err := readKeyEtcd(ctx, client, backendEncryptedFile)
|
||||
if err != nil && err != errConfigNotFound {
|
||||
return false, err
|
||||
}
|
||||
return err == nil && bytes.Equal(data, backendEncryptedFileValue), nil
|
||||
}
|
||||
|
||||
func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) {
|
||||
data, err := readConfig(context.Background(), objAPI, backendEncryptedFile)
|
||||
if err != nil && err != errConfigNotFound {
|
||||
return false, err
|
||||
}
|
||||
return err == nil && bytes.Equal(data, backendEncryptedFileValue), nil
|
||||
}
|
||||
|
||||
func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||
defer cancel()
|
||||
|
||||
encrypted, err := checkBackendEtcdEncrypted(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if encrypted {
|
||||
// backend is encrypted, but credentials are not specified
|
||||
// we shall fail right here. if not proceed forward.
|
||||
if !globalConfigEncrypted || !globalActiveCred.IsValid() {
|
||||
return config.ErrMissingCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
} else {
|
||||
// backend is not yet encrypted, check if encryption of
|
||||
// backend is requested if not return nil and proceed
|
||||
// forward.
|
||||
if !globalConfigEncrypted {
|
||||
return nil
|
||||
}
|
||||
if !globalActiveCred.IsValid() {
|
||||
return errInvalidArgument
|
||||
}
|
||||
}
|
||||
|
||||
accessKeyOld := env.Get(config.EnvAccessKeyOld, "")
|
||||
secretKeyOld := env.Get(config.EnvSecretKeyOld, "")
|
||||
var activeCredOld auth.Credentials
|
||||
if accessKeyOld != "" && secretKeyOld != "" {
|
||||
activeCredOld, err = auth.CreateCredentials(accessKeyOld, secretKeyOld)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if encrypted {
|
||||
// No key rotation requested, and backend is
|
||||
// already encrypted. We proceed without migration.
|
||||
if !activeCredOld.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No real reason to rotate if old and new creds are same.
|
||||
if activeCredOld.Equal(globalActiveCred) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !activeCredOld.IsValid() {
|
||||
logger.Info("Attempting a one time encrypt of all IAM users and policies on etcd")
|
||||
} else {
|
||||
logger.Info("Attempting a rotation of encrypted IAM users and policies on etcd with newly supplied credentials")
|
||||
}
|
||||
|
||||
r, err := client.Get(ctx, minioConfigPrefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, kv := range r.Kvs {
|
||||
var (
|
||||
cdata []byte
|
||||
cencdata []byte
|
||||
)
|
||||
cdata, err = readKeyEtcd(ctx, client, string(kv.Key))
|
||||
if err != nil {
|
||||
switch err {
|
||||
case errConfigNotFound:
|
||||
// Perhaps not present or someone deleted it.
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Is rotating of creds requested?
|
||||
if activeCredOld.IsValid() {
|
||||
cdata, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
||||
if err != nil {
|
||||
if err == madmin.ErrMaliciousData {
|
||||
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cencdata, err = madmin.EncryptData(globalActiveCred.String(), cdata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = saveKeyEtcd(ctx, client, string(kv.Key), cencdata); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return saveKeyEtcd(ctx, client, backendEncryptedFile, backendEncryptedFileValue)
|
||||
}
|
||||
|
||||
func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Credentials, encrypted bool) error {
|
||||
if encrypted {
|
||||
// No key rotation requested, and backend is
|
||||
// already encrypted. We proceed without migration.
|
||||
if !activeCredOld.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No real reason to rotate if old and new creds are same.
|
||||
if activeCredOld.Equal(globalActiveCred) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !activeCredOld.IsValid() {
|
||||
logger.Info("Attempting a one time encrypt of all config, IAM users and policies on MinIO backend")
|
||||
} else {
|
||||
logger.Info("Attempting a rotation of encrypted config, IAM users and policies on MinIO with newly supplied credentials")
|
||||
}
|
||||
|
||||
// Construct path to config/transaction.lock for locking
|
||||
transactionConfigPrefix := minioConfigPrefix + "/transaction.lock"
|
||||
|
||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||
// and saveConfig().
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigPrefix)
|
||||
if err := objLock.GetLock(globalOperationTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
defer objLock.Unlock()
|
||||
|
||||
var marker string
|
||||
for {
|
||||
res, err := objAPI.ListObjects(context.Background(), minioMetaBucket, minioConfigPrefix, marker, "", maxObjectList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, obj := range res.Objects {
|
||||
var (
|
||||
cdata []byte
|
||||
cencdata []byte
|
||||
)
|
||||
|
||||
cdata, err = readConfig(context.Background(), objAPI, obj.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Is rotating of creds requested?
|
||||
if activeCredOld.IsValid() {
|
||||
cdata, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
||||
if err != nil {
|
||||
if err == madmin.ErrMaliciousData {
|
||||
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cencdata, err = madmin.EncryptData(globalActiveCred.String(), cdata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = saveConfig(context.Background(), objAPI, obj.Name, cencdata); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !res.IsTruncated {
|
||||
break
|
||||
}
|
||||
|
||||
marker = res.NextMarker
|
||||
}
|
||||
|
||||
return saveConfig(context.Background(), objAPI, backendEncryptedFile, backendEncryptedFileValue)
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -38,6 +39,7 @@ import (
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/event/target"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
"github.com/minio/minio/pkg/quick"
|
||||
)
|
||||
@@ -2442,12 +2444,13 @@ func migrateConfigToMinioSys(objAPI ObjectLayer) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
transactionConfigFile := configFile + ".transaction"
|
||||
// Construct path to config/transaction.lock for locking
|
||||
transactionConfigPrefix := minioConfigPrefix + "/transaction.lock"
|
||||
|
||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||
// and saveConfig().
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigFile)
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigPrefix)
|
||||
if err = objLock.GetLock(globalOperationTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2496,13 +2499,13 @@ func migrateMinioSysConfig(objAPI ObjectLayer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct path to config.json for the given bucket.
|
||||
transactionConfigFile := configFile + ".transaction"
|
||||
// Construct path to config/transaction.lock for locking
|
||||
transactionConfigPrefix := minioConfigPrefix + "/transaction.lock"
|
||||
|
||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||
// and saveConfig().
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigFile)
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigPrefix)
|
||||
if err := objLock.GetLock(globalOperationTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2532,6 +2535,16 @@ func checkConfigVersion(objAPI ObjectLayer, configFile string, version string) (
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data))
|
||||
if err != nil {
|
||||
if err == madmin.ErrMaliciousData {
|
||||
return false, nil, config.ErrInvalidCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
return false, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var versionConfig struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
@@ -2793,13 +2806,13 @@ func migrateMinioSysConfigToKV(objAPI ObjectLayer) error {
|
||||
notify.SetNotifyWebhook(newCfg, k, args)
|
||||
}
|
||||
|
||||
// Construct path to config.json for the given bucket.
|
||||
transactionConfigFile := configFile + ".transaction"
|
||||
// Construct path to config/transaction.lock for locking
|
||||
transactionConfigPrefix := minioConfigPrefix + "/transaction.lock"
|
||||
|
||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||
// and saveConfig().
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigFile)
|
||||
objLock := globalNSMutex.NewNSLock(context.Background(), minioMetaBucket, transactionConfigPrefix)
|
||||
if err = objLock.GetLock(globalOperationTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -82,6 +83,11 @@ func readServerConfigHistory(ctx context.Context, objAPI ObjectLayer, uuidKV str
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data))
|
||||
}
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
@@ -89,6 +95,14 @@ func saveServerConfigHistory(ctx context.Context, objAPI ObjectLayer, kv []byte)
|
||||
uuidKV := mustGetUUID() + ".kv"
|
||||
historyFile := pathJoin(minioConfigHistoryPrefix, uuidKV)
|
||||
|
||||
var err error
|
||||
if globalConfigEncrypted {
|
||||
kv, err = madmin.EncryptData(globalActiveCred.String(), kv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Save the new config KV settings into the history path.
|
||||
return saveConfig(ctx, objAPI, historyFile, kv)
|
||||
}
|
||||
@@ -121,6 +135,12 @@ func saveServerConfig(ctx context.Context, objAPI ObjectLayer, config interface{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if globalConfigEncrypted {
|
||||
oldData, err = madmin.EncryptData(globalActiveCred.String(), oldData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No need to take backups for fresh setups.
|
||||
@@ -130,6 +150,13 @@ func saveServerConfig(ctx context.Context, objAPI ObjectLayer, config interface{
|
||||
}
|
||||
}
|
||||
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.EncryptData(globalActiveCred.String(), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Save the new config in the std config path
|
||||
return saveConfig(ctx, objAPI, configFile, data)
|
||||
}
|
||||
@@ -141,6 +168,16 @@ func readServerConfig(ctx context.Context, objAPI ObjectLayer) (config.Config, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if globalConfigEncrypted {
|
||||
configData, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(configData))
|
||||
if err != nil {
|
||||
if err == madmin.ErrMaliciousData {
|
||||
return nil, config.ErrInvalidCredentialsBackendEncrypted(nil)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var config = config.New()
|
||||
if err = json.Unmarshal(configData, &config); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -23,13 +23,15 @@ const (
|
||||
|
||||
// Top level common ENVs
|
||||
const (
|
||||
EnvAccessKey = "MINIO_ACCESS_KEY"
|
||||
EnvSecretKey = "MINIO_SECRET_KEY"
|
||||
EnvBrowser = "MINIO_BROWSER"
|
||||
EnvDomain = "MINIO_DOMAIN"
|
||||
EnvRegionName = "MINIO_REGION_NAME"
|
||||
EnvPublicIPs = "MINIO_PUBLIC_IPS"
|
||||
EnvEndpoints = "MINIO_ENDPOINTS"
|
||||
EnvAccessKey = "MINIO_ACCESS_KEY"
|
||||
EnvSecretKey = "MINIO_SECRET_KEY"
|
||||
EnvAccessKeyOld = "MINIO_ACCESS_KEY_OLD"
|
||||
EnvSecretKeyOld = "MINIO_SECRET_KEY_OLD"
|
||||
EnvBrowser = "MINIO_BROWSER"
|
||||
EnvDomain = "MINIO_DOMAIN"
|
||||
EnvRegionName = "MINIO_REGION_NAME"
|
||||
EnvPublicIPs = "MINIO_PUBLIC_IPS"
|
||||
EnvEndpoints = "MINIO_ENDPOINTS"
|
||||
|
||||
EnvUpdate = "MINIO_UPDATE"
|
||||
EnvWormState = "MINIO_WORM_STATE"
|
||||
|
||||
@@ -72,6 +72,24 @@ var (
|
||||
"MINIO_CACHE_ENCRYPTION_MASTER_KEY: For more information, please refer to https://docs.min.io/docs/minio-disk-cache-guide",
|
||||
)
|
||||
|
||||
ErrInvalidRotatingCredentialsBackendEncrypted = newErrFn(
|
||||
"Invalid rotating credentials",
|
||||
"Please set correct rotating credentials in the environment for decryption",
|
||||
`Detected encrypted config backend, correct old access and secret keys should be specified via environment variables MINIO_ACCESS_KEY_OLD and MINIO_SECRET_KEY_OLD to be able to re-encrypt the MinIO config, user IAM and policies with new credentials`,
|
||||
)
|
||||
|
||||
ErrInvalidCredentialsBackendEncrypted = newErrFn(
|
||||
"Invalid credentials",
|
||||
"Please set correct credentials in the environment for decryption",
|
||||
`Detected encrypted config backend, correct access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY to be able to decrypt the MinIO config, user IAM and policies`,
|
||||
)
|
||||
|
||||
ErrMissingCredentialsBackendEncrypted = newErrFn(
|
||||
"Credentials missing",
|
||||
"Please set your credentials in the environment",
|
||||
`Detected encrypted config backend, access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY to be able to decrypt the MinIO config, user IAM and policies`,
|
||||
)
|
||||
|
||||
ErrInvalidCredentials = newErrFn(
|
||||
"Invalid credentials",
|
||||
"Please provide correct credentials",
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/minio/minio/cmd/config"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/env"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
@@ -373,15 +372,14 @@ func parseGatewaySSE(s string) (gatewaySSE, error) {
|
||||
}
|
||||
|
||||
// handle gateway env vars
|
||||
func handleGatewayEnvVars() {
|
||||
accessKey := env.Get(config.EnvAccessKey, "")
|
||||
secretKey := env.Get(config.EnvSecretKey, "")
|
||||
cred, err := auth.CreateCredentials(accessKey, secretKey)
|
||||
if err != nil {
|
||||
logger.Fatal(config.ErrInvalidCredentials(err),
|
||||
func gatewayHandleEnvVars() {
|
||||
// Handle common env vars.
|
||||
handleCommonEnvVars()
|
||||
|
||||
if !globalActiveCred.IsValid() {
|
||||
logger.Fatal(config.ErrInvalidCredentials(nil),
|
||||
"Unable to validate credentials inherited from the shell environment")
|
||||
}
|
||||
globalActiveCred = cred
|
||||
|
||||
gwsseVal := env.Get("MINIO_GATEWAY_SSE", "")
|
||||
if len(gwsseVal) != 0 {
|
||||
|
||||
@@ -136,11 +136,8 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
||||
globalRootCAs, err = config.GetRootCAs(globalCertsCADir.Get())
|
||||
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
||||
|
||||
// Handle common env vars.
|
||||
handleCommonEnvVars()
|
||||
|
||||
// Handle gateway specific env
|
||||
handleGatewayEnvVars()
|
||||
gatewayHandleEnvVars()
|
||||
|
||||
// Set system resources to maximum.
|
||||
logger.LogIf(context.Background(), setMaxResources())
|
||||
@@ -230,6 +227,16 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
||||
initFederatorBackend(newObject)
|
||||
}
|
||||
|
||||
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
||||
// For "nas" gateway we need to specially handle the backend migration as well.
|
||||
// Internally code handles migrating etcd if enabled automatically.
|
||||
logger.FatalIf(handleEncryptedConfigBackend(newObject, enableConfigOps),
|
||||
"Unable to handle encrypted backend for config, iam and policies")
|
||||
|
||||
// **** WARNING ****
|
||||
// Migrating to encrypted backend should happen before initialization of any
|
||||
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||
|
||||
if enableConfigOps {
|
||||
// Create a new config system.
|
||||
globalConfigSys = NewConfigSys()
|
||||
@@ -246,6 +253,14 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
||||
globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
|
||||
logger.SetDeploymentID(globalDeploymentID)
|
||||
|
||||
if globalEtcdClient != nil {
|
||||
// **** WARNING ****
|
||||
// Migrating to encrypted backend on etcd should happen before initialization of
|
||||
// IAM sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||
logger.FatalIf(migrateIAMConfigsEtcdToEncrypted(globalEtcdClient),
|
||||
"Unable to handle encrypted backend for iam and policies")
|
||||
}
|
||||
|
||||
if globalCacheConfig.Enabled {
|
||||
// initialize the new disk cache objects.
|
||||
globalCacheObjectAPI, err = newServerCacheObjects(context.Background(), globalCacheConfig)
|
||||
|
||||
@@ -186,6 +186,9 @@ var (
|
||||
|
||||
globalActiveCred auth.Credentials
|
||||
|
||||
// Indicates if config is to be encrypted
|
||||
globalConfigEncrypted bool
|
||||
|
||||
globalPublicCerts []*x509.Certificate
|
||||
|
||||
globalDomainNames []string // Root domains for virtual host style requests
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
)
|
||||
|
||||
var defaultContextTimeout = 30 * time.Second
|
||||
@@ -109,6 +111,12 @@ func (ies *IAMEtcdStore) saveIAMConfig(item interface{}, path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.EncryptData(globalActiveCred.String(), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return saveKeyEtcd(ies.getContext(), ies.client, path, data)
|
||||
}
|
||||
|
||||
@@ -118,6 +126,13 @@ func (ies *IAMEtcdStore) loadIAMConfig(item interface{}, path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if globalConfigEncrypted {
|
||||
pdata, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(pdata))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return json.Unmarshal(pdata, item)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
)
|
||||
|
||||
// IAMObjectStore implements IAMStorageAPI
|
||||
@@ -215,6 +217,12 @@ func (iamOS *IAMObjectStore) saveIAMConfig(item interface{}, path string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.EncryptData(globalActiveCred.String(), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return saveConfig(context.Background(), objectAPI, path, data)
|
||||
}
|
||||
|
||||
@@ -224,6 +232,12 @@ func (iamOS *IAMObjectStore) loadIAMConfig(item interface{}, path string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if globalConfigEncrypted {
|
||||
data, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return json.Unmarshal(data, item)
|
||||
}
|
||||
|
||||
|
||||
@@ -178,18 +178,6 @@ func serverHandleCmdArgs(ctx *cli.Context) {
|
||||
func serverHandleEnvVars() {
|
||||
// Handle common environment variables.
|
||||
handleCommonEnvVars()
|
||||
|
||||
accessKey := env.Get(config.EnvAccessKey, "")
|
||||
secretKey := env.Get(config.EnvSecretKey, "")
|
||||
if accessKey != "" && secretKey != "" {
|
||||
cred, err := auth.CreateCredentials(accessKey, secretKey)
|
||||
if err != nil {
|
||||
logger.Fatal(config.ErrInvalidCredentials(err),
|
||||
"Unable to validate credentials inherited from the shell environment")
|
||||
}
|
||||
globalActiveCred = cred
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func initAllSubsystems(newObject ObjectLayer) {
|
||||
@@ -350,9 +338,25 @@ func serverMain(ctx *cli.Context) {
|
||||
// Re-enable logging
|
||||
logger.Disable = false
|
||||
|
||||
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
||||
logger.FatalIf(handleEncryptedConfigBackend(newObject, true),
|
||||
"Unable to handle encrypted backend for config, iam and policies")
|
||||
|
||||
// **** WARNING ****
|
||||
// Migrating to encrypted backend should happen before initialization of any
|
||||
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||
|
||||
// Validate and initialize all subsystems.
|
||||
initAllSubsystems(newObject)
|
||||
|
||||
if globalEtcdClient != nil {
|
||||
// **** WARNING ****
|
||||
// Migrating to encrypted backend on etcd should happen before initialization of
|
||||
// IAM sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||
logger.FatalIf(migrateIAMConfigsEtcdToEncrypted(globalEtcdClient),
|
||||
"Unable to handle encrypted backend for iam and policies")
|
||||
}
|
||||
|
||||
if globalCacheConfig.Enabled {
|
||||
logger.StartupMessage(color.Red(color.Bold("Disk caching is recommended only for gateway deployments")))
|
||||
|
||||
|
||||
@@ -523,13 +523,13 @@ func newTestConfig(bucketLocation string, obj ObjectLayer) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Disable = true
|
||||
|
||||
globalActiveCred = auth.Credentials{
|
||||
AccessKey: auth.DefaultAccessKey,
|
||||
SecretKey: auth.DefaultSecretKey,
|
||||
}
|
||||
|
||||
globalConfigEncrypted = true
|
||||
|
||||
// Set a default region.
|
||||
config.SetRegion(globalServerConfig, bucketLocation)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user