From e0b0a351c671142d46263418313b6763093626a7 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Fri, 5 Aug 2022 17:53:23 -0700 Subject: [PATCH] remove IAM old migration code (#15476) ``` commit 7bdaf9bc50eb49061e8a71b9cf471eae5db654ff Author: Aditya Manthramurthy Date: Wed Jul 24 17:34:23 2019 -0700 Update on-disk storage format for users system (#7949) ``` Bonus: fixes a bug when etcd keys were being re-encrypted. --- cmd/config-encrypted.go | 73 ++++++++++++++-------- cmd/iam-dummy-store.go | 4 -- cmd/iam-etcd-store.go | 116 ----------------------------------- cmd/iam-object-store.go | 130 ---------------------------------------- cmd/iam-store.go | 28 ++++++++- cmd/iam.go | 34 +---------- 6 files changed, 76 insertions(+), 309 deletions(-) diff --git a/cmd/config-encrypted.go b/cmd/config-encrypted.go index a248c06aa..099a0de6a 100644 --- a/cmd/config-encrypted.go +++ b/cmd/config-encrypted.go @@ -69,6 +69,12 @@ func migrateIAMConfigsEtcdToEncrypted(ctx context.Context, client *etcd.Client) return err } + // If backend doesn't have this file means we have already + // attempted then migration + if !encrypted { + return nil + } + if encrypted && GlobalKMS != nil { stat, err := GlobalKMS.Stat(ctx) if err != nil { @@ -109,6 +115,8 @@ func migrateIAMConfigsEtcdToEncrypted(ctx context.Context, client *etcd.Client) return fmt.Errorf("Decrypting IAM config failed %w, possibly credentials are incorrect", err) } } + } else { + return fmt.Errorf("Decrypting IAM config failed %w, possibly credentials are incorrect", err) } } data = pdata @@ -131,6 +139,7 @@ func migrateIAMConfigsEtcdToEncrypted(ctx context.Context, client *etcd.Client) if encrypted && GlobalKMS != nil { logger.Info("Migration of encrypted IAM config data completed. All data is now encrypted on etcd.") } + return deleteKeyEtcd(ctx, client, backendEncryptedFile) } @@ -146,44 +155,56 @@ func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, encrypted bool) error { logger.Info(fmt.Sprintf("Attempting to re-encrypt config, IAM users and policies on MinIO with %q (%s)", stat.DefaultKey, stat.Name)) } - var marker string - for { - res, err := objAPI.ListObjects(GlobalContext, minioMetaBucket, minioConfigPrefix, marker, "", maxObjectList) + results := make(chan ObjectInfo) + if err := objAPI.Walk(GlobalContext, minioMetaBucket, minioConfigPrefix, results, ObjectOptions{}); err != nil { + return err + } + + for obj := range results { + data, err := readConfig(GlobalContext, objAPI, obj.Name) if err != nil { return err } - for _, obj := range res.Objects { - data, err := readConfig(GlobalContext, objAPI, obj.Name) + + if !utf8.Valid(data) { + pdata, err := madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data)) + if err != nil { + if GlobalKMS != nil { + pdata, err = config.DecryptBytes(GlobalKMS, data, kms.Context{ + minioMetaBucket: path.Join(minioMetaBucket, obj.Name), + }) + if err != nil { + pdata, err = config.DecryptBytes(GlobalKMS, data, kms.Context{ + minioMetaBucket: obj.Name, + }) + if err != nil { + return fmt.Errorf("Decrypting IAM config failed %w, possibly credentials are incorrect", err) + } + } + } else { + return fmt.Errorf("Decrypting IAM config failed %w, possibly credentials are incorrect", err) + } + } + data = pdata + } + + if GlobalKMS != nil { + data, err = config.EncryptBytes(GlobalKMS, data, kms.Context{ + obj.Bucket: path.Join(obj.Bucket, obj.Name), + }) if err != nil { return err } - - if !utf8.Valid(data) { - data, err = madmin.DecryptData(globalActiveCred.String(), bytes.NewReader(data)) - if err != nil { - return fmt.Errorf("Decrypting config failed %w, possibly credentials are incorrect", err) - } - } - if GlobalKMS != nil { - data, err = config.EncryptBytes(GlobalKMS, data, kms.Context{ - obj.Bucket: path.Join(obj.Bucket, obj.Name), - }) - if err != nil { - return err - } - } - if err = saveConfig(GlobalContext, objAPI, obj.Name, data); err != nil { - return err - } } - if !res.IsTruncated { - break + if err = saveConfig(GlobalContext, objAPI, obj.Name, data); err != nil { + return err } - marker = res.NextMarker } + if encrypted && GlobalKMS != nil { logger.Info("Migration of encrypted config data completed. All config data is now encrypted.") } + return deleteConfig(GlobalContext, objAPI, backendEncryptedFile) } diff --git a/cmd/iam-dummy-store.go b/cmd/iam-dummy-store.go index ce173cdcc..455ed17c1 100644 --- a/cmd/iam-dummy-store.go +++ b/cmd/iam-dummy-store.go @@ -57,10 +57,6 @@ func (ids *iamDummyStore) getUsersSysType() UsersSysType { return ids.usersSysType } -func (ids *iamDummyStore) migrateBackendFormat(context.Context) error { - return nil -} - func (ids *iamDummyStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error { v, ok := ids.iamPolicyDocsMap[policy] if !ok { diff --git a/cmd/iam-etcd-store.go b/cmd/iam-etcd-store.go index 4efeff3b2..af42dfaa0 100644 --- a/cmd/iam-etcd-store.go +++ b/cmd/iam-etcd-store.go @@ -28,7 +28,6 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/minio/minio-go/v7/pkg/set" - "github.com/minio/minio/internal/auth" "github.com/minio/minio/internal/config" "github.com/minio/minio/internal/kms" "github.com/minio/minio/internal/logger" @@ -163,121 +162,6 @@ func (ies *IAMEtcdStore) deleteIAMConfig(ctx context.Context, path string) error return deleteKeyEtcd(ctx, ies.client, path) } -func (ies *IAMEtcdStore) migrateUsersConfigToV1(ctx context.Context) error { - basePrefix := iamConfigUsersPrefix - ctx, cancel := context.WithTimeout(ctx, defaultContextTimeout) - defer cancel() - r, err := ies.client.Get(ctx, basePrefix, etcd.WithPrefix(), etcd.WithKeysOnly()) - if err != nil { - return err - } - - users := etcdKvsToSet(basePrefix, r.Kvs) - for _, user := range users.ToSlice() { - { - // 1. check if there is a policy file in the old loc. - oldPolicyPath := pathJoin(basePrefix, user, iamPolicyFile) - var policyName string - err := ies.loadIAMConfig(ctx, &policyName, oldPolicyPath) - if err != nil { - switch err { - case errConfigNotFound: - // No mapped policy or already migrated. - default: - // corrupt data/read error, etc - } - goto next - } - - // 2. copy policy to new loc. - mp := newMappedPolicy(policyName) - userType := regUser - path := getMappedPolicyPath(user, userType, false) - if err := ies.saveIAMConfig(ctx, mp, path); err != nil { - return err - } - - // 3. delete policy file in old loc. - deleteKeyEtcd(ctx, ies.client, oldPolicyPath) - } - - next: - // 4. check if user identity has old format. - identityPath := pathJoin(basePrefix, user, iamIdentityFile) - var cred auth.Credentials - if err := ies.loadIAMConfig(ctx, &cred, identityPath); err != nil { - switch err { - case errConfigNotFound: - // This case should not happen. - default: - // corrupt file or read error - } - continue - } - - // If the file is already in the new format, - // then the parsed auth.Credentials will have - // the zero value for the struct. - var zeroCred auth.Credentials - if cred.Equal(zeroCred) { - // nothing to do - continue - } - - // Found a id file in old format. Copy value - // into new format and save it. - cred.AccessKey = user - u := newUserIdentity(cred) - if err := ies.saveIAMConfig(ctx, u, identityPath); err != nil { - logger.LogIf(ctx, err) - return err - } - - // Nothing to delete as identity file location - // has not changed. - } - return nil -} - -func (ies *IAMEtcdStore) migrateToV1(ctx context.Context) error { - var iamFmt iamFormat - path := getIAMFormatFilePath() - if err := ies.loadIAMConfig(ctx, &iamFmt, path); err != nil { - switch err { - case errConfigNotFound: - // Need to migrate to V1. - default: - // if IAM format - return err - } - } - - if iamFmt.Version >= iamFormatVersion1 { - // Nothing to do. - return nil - } - - if err := ies.migrateUsersConfigToV1(ctx); err != nil { - logger.LogIf(ctx, err) - return err - } - - // Save iam format to version 1. - if err := ies.saveIAMConfig(ctx, newIAMFormatVersion1(), path); err != nil { - logger.LogIf(ctx, err) - return err - } - - return nil -} - -// Should be called under config migration lock -func (ies *IAMEtcdStore) migrateBackendFormat(ctx context.Context) error { - ies.Lock() - defer ies.Unlock() - return ies.migrateToV1(ctx) -} - func (ies *IAMEtcdStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error { data, err := ies.loadIAMConfigBytes(ctx, getPolicyDocPath(policy)) if err != nil { diff --git a/cmd/iam-object-store.go b/cmd/iam-object-store.go index 83d242bdf..617a007a4 100644 --- a/cmd/iam-object-store.go +++ b/cmd/iam-object-store.go @@ -26,7 +26,6 @@ import ( "unicode/utf8" jsoniter "github.com/json-iterator/go" - "github.com/minio/minio/internal/auth" "github.com/minio/minio/internal/config" "github.com/minio/minio/internal/kms" "github.com/minio/minio/internal/logger" @@ -74,135 +73,6 @@ func (iamOS *IAMObjectStore) getUsersSysType() UsersSysType { return iamOS.usersSysType } -// Migrate users directory in a single scan. -// -// 1. Migrate user policy from: -// -// `iamConfigUsersPrefix + "/policy.json"` -// -// to: -// -// `iamConfigPolicyDBUsersPrefix + ".json"`. -// -// 2. Add versioning to the policy json file in the new -// location. -// -// 3. Migrate user identity json file to include version info. -func (iamOS *IAMObjectStore) migrateUsersConfigToV1(ctx context.Context) error { - basePrefix := iamConfigUsersPrefix - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - for item := range listIAMConfigItems(ctx, iamOS.objAPI, basePrefix) { - if item.Err != nil { - return item.Err - } - - user := path.Dir(item.Item) - { - // 1. check if there is policy file in old location. - oldPolicyPath := pathJoin(basePrefix, user, iamPolicyFile) - var policyName string - if err := iamOS.loadIAMConfig(ctx, &policyName, oldPolicyPath); err != nil { - switch err { - case errConfigNotFound: - // This case means it is already - // migrated or there is no policy on - // user. - default: - // File may be corrupt or network error - } - - // Nothing to do on the policy file, - // so move on to check the id file. - goto next - } - - // 2. copy policy file to new location. - mp := newMappedPolicy(policyName) - userType := regUser - if err := iamOS.saveMappedPolicy(ctx, user, userType, false, mp); err != nil { - return err - } - - // 3. delete policy file from old - // location. Ignore error. - iamOS.deleteIAMConfig(ctx, oldPolicyPath) - } - next: - // 4. check if user identity has old format. - identityPath := pathJoin(basePrefix, user, iamIdentityFile) - cred := auth.Credentials{ - AccessKey: user, - } - if err := iamOS.loadIAMConfig(ctx, &cred, identityPath); err != nil { - switch err { - case errConfigNotFound: - // This should not happen. - default: - // File may be corrupt or network error - } - continue - } - - // If the file is already in the new format, - // then the parsed auth.Credentials will have - // the zero value for the struct. - if !cred.IsValid() { - // nothing to do - continue - } - - u := newUserIdentity(cred) - if err := iamOS.saveIAMConfig(ctx, u, identityPath); err != nil { - logger.LogIf(ctx, err) - return err - } - - // Nothing to delete as identity file location - // has not changed. - } - return nil -} - -func (iamOS *IAMObjectStore) migrateToV1(ctx context.Context) error { - var iamFmt iamFormat - path := getIAMFormatFilePath() - if err := iamOS.loadIAMConfig(ctx, &iamFmt, path); err != nil { - switch err { - case errConfigNotFound: - // Need to migrate to V1. - default: - // if IAM format - return err - } - } - - if iamFmt.Version >= iamFormatVersion1 { - // Nothing to do. - return nil - } - - if err := iamOS.migrateUsersConfigToV1(ctx); err != nil { - logger.LogIf(ctx, err) - return err - } - - // Save iam format to version 1. - if err := iamOS.saveIAMConfig(ctx, newIAMFormatVersion1(), path); err != nil { - logger.LogIf(ctx, err) - return err - } - return nil -} - -// Should be called under config migration lock -func (iamOS *IAMObjectStore) migrateBackendFormat(ctx context.Context) error { - iamOS.Lock() - defer iamOS.Unlock() - return iamOS.migrateToV1(ctx) -} - func (iamOS *IAMObjectStore) saveIAMConfig(ctx context.Context, item interface{}, objPath string, opts ...options) error { json := jsoniter.ConfigCompatibleWithStandardLibrary data, err := json.Marshal(item) diff --git a/cmd/iam-store.go b/cmd/iam-store.go index ea8291ab3..2ec36a8e7 100644 --- a/cmd/iam-store.go +++ b/cmd/iam-store.go @@ -102,6 +102,33 @@ func getUserIdentityPath(user string, userType IAMUserType) string { return pathJoin(basePath, user, iamIdentityFile) } +func saveIAMFormat(ctx context.Context, store IAMStorageAPI) error { + var iamFmt iamFormat + path := getIAMFormatFilePath() + if err := store.loadIAMConfig(ctx, &iamFmt, path); err != nil { + switch err { + case errConfigNotFound: + // Need to migrate to V1. + default: + // if IAM format + return err + } + } + + if iamFmt.Version >= iamFormatVersion1 { + // Nothing to do. + return nil + } + + // Save iam format to version 1. + if err := store.saveIAMConfig(ctx, newIAMFormatVersion1(), path); err != nil { + logger.LogIf(ctx, err) + return err + } + + return nil +} + func getGroupInfoPath(group string) string { return pathJoin(iamConfigGroupsPrefix, group, iamGroupMembersFile) } @@ -374,7 +401,6 @@ type IAMStorageAPI interface { unlock() rlock() *iamCache runlock() - migrateBackendFormat(context.Context) error getUsersSysType() UsersSysType loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error diff --git a/cmd/iam.go b/cmd/iam.go index d8720d456..f4e6c8839 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -161,11 +161,6 @@ func (sys *IAMSys) LoadServiceAccount(ctx context.Context, accessKey string) err return sys.store.UserNotificationHandler(ctx, accessKey, svcUser) } -// Perform IAM configuration migration. -func (sys *IAMSys) doIAMConfigMigration(ctx context.Context) error { - return sys.store.migrateBackendFormat(ctx) -} - // initStore initializes IAM stores func (sys *IAMSys) initStore(objAPI ObjectLayer, etcdClient *etcd.Client) { if sys.ldapConfig.Enabled { @@ -238,31 +233,15 @@ func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer, etcdClient *etc // Indicate to our routine to exit cleanly upon return. defer cancel() - // allocate dynamic timeout once before the loop - iamLockTimeout := newDynamicTimeout(5*time.Second, 3*time.Second) - r := rand.New(rand.NewSource(time.Now().UnixNano())) // Migrate storage format if needed. for { - // Hold the lock for migration only. - txnLk := objAPI.NewNSLock(minioMetaBucket, minioConfigPrefix+"/iam.lock") - - // let one of the server acquire the lock, if not let them timeout. - // which shall be retried again by this loop. - lkctx, err := txnLk.GetLock(retryCtx, iamLockTimeout) - if err != nil { - logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. trying to acquire lock") - time.Sleep(time.Duration(r.Float64() * float64(5*time.Second))) - continue - } - if etcdClient != nil { // **** WARNING **** // Migrating to encrypted backend on etcd should happen before initialization of // IAM sub-system, make sure that we do not move the above codeblock elsewhere. if err := migrateIAMConfigsEtcdToEncrypted(retryCtx, etcdClient); err != nil { - txnLk.Unlock(lkctx.Cancel) if errors.Is(err, errEtcdUnreachable) { logger.Info("Connection to etcd timed out. Retrying..") continue @@ -273,25 +252,16 @@ func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer, etcdClient *etc } } - // These messages only meant primarily for distributed setup, so only log during distributed setup. - if globalIsDistErasure { - logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. lock acquired") - } - // Migrate IAM configuration, if necessary. - if err := sys.doIAMConfigMigration(retryCtx); err != nil { - txnLk.Unlock(lkctx.Cancel) + if err := saveIAMFormat(retryCtx, sys.store); err != nil { if configRetriableErrors(err) { logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err) continue } - logger.LogIf(ctx, fmt.Errorf("Unable to migrate IAM users and policies to new format: %w", err)) - logger.LogIf(ctx, errors.New("IAM sub-system is partially initialized, some users may not be available")) + logger.LogIf(ctx, errors.New("IAM sub-system is partially initialized, unable to write the IAM format")) return } - // Successfully migrated, proceed to load the users. - txnLk.Unlock(lkctx.Cancel) break }