diff --git a/pkg/agent/clean/ad_unmigration/migrate.go b/pkg/agent/clean/ad_unmigration/migrate.go index db626955bfa..dce78f9bdaf 100644 --- a/pkg/agent/clean/ad_unmigration/migrate.go +++ b/pkg/agent/clean/ad_unmigration/migrate.go @@ -28,6 +28,7 @@ const ( migrateTokensOperation = "migrate-ad-tokens" migrateCrtbsOperation = "migrate-ad-crtbs" migratePrtbsOperation = "migrate-ad-prtbs" + migrateGrbsOperation = "migrate-ad-grbs" activeDirectoryPrefix = "activedirectory_user://" localPrefix = "local://" adGUIDMigrationLabel = "ad-guid-migration" @@ -42,16 +43,21 @@ const ( ) type migrateUserWorkUnit struct { - distinguishedName string - guid string - originalUser *v3.User - duplicateUsers []*v3.User - guidCRTBs []v3.ClusterRoleTemplateBinding + distinguishedName string + guid string + originalUser *v3.User + duplicateUsers []*v3.User + + activeDirectoryCRTBs []v3.ClusterRoleTemplateBinding duplicateLocalCRTBs []v3.ClusterRoleTemplateBinding - guidPRTBs []v3.ProjectRoleTemplateBinding + + activeDirectoryPRTBs []v3.ProjectRoleTemplateBinding duplicateLocalPRTBs []v3.ProjectRoleTemplateBinding - guidTokens []v3.Token - duplicateLocalTokens []v3.Token + + duplicateLocalGRBs []v3.GlobalRoleBinding + + activeDirectoryTokens []v3.Token + duplicateLocalTokens []v3.Token } type missingUserWorkUnit struct { @@ -152,6 +158,10 @@ func UnmigrateAdGUIDUsers(clientConfig *restclient.Config, dryRun bool, deleteMi if err != nil { return err } + err = collectGRBs(&usersToMigrate, sc) + if err != nil { + return err + } for _, user := range skippedUsers { logrus.Errorf("[%v] unable to migrate user '%v' due to a connection failure; this user will be skipped", migrateAdUserOperation, user.originalUser.Name) @@ -190,6 +200,11 @@ func UnmigrateAdGUIDUsers(clientConfig *restclient.Config, dryRun bool, deleteMi logrus.Errorf("[%v] unable to migrate PRTBs for user '%v': %v", migrateAdUserOperation, userToMigrate.originalUser.Name, err) continue } + err = migrateGRBs(&userToMigrate, sc, dryRun) + if err != nil { + logrus.Errorf("[%v] unable to migrate GRBs for user '%v': %v", migrateAdUserOperation, userToMigrate.originalUser.Name, err) + continue + } replaceGUIDPrincipalWithDn(userToMigrate.originalUser, userToMigrate.distinguishedName, userToMigrate.guid, dryRun) if dryRun { diff --git a/pkg/agent/clean/ad_unmigration/rtbs.go b/pkg/agent/clean/ad_unmigration/rtbs.go index e939fb1eb14..7e383079d4c 100644 --- a/pkg/agent/clean/ad_unmigration/rtbs.go +++ b/pkg/agent/clean/ad_unmigration/rtbs.go @@ -9,6 +9,27 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// principalsToMigrate collects workunits whose resources we wish to migrate into two groups: +// +// adWorkUnitsByPrincipal - resources should be migrated to an ActiveDirectory principal with a Distinguished Name +// duplicateLocalWorkUnitsByPrincipal - resources should be migrated to the local ID of the original (kept) user +func principalsToMigrate(workunits *[]migrateUserWorkUnit) (adWorkUnitsByPrincipal map[string]int, duplicateLocalWorkUnitsByPrincipal map[string]int) { + // first build a map of guid-principalid -> work unit, which will make the following logic more efficient + adWorkUnitsByPrincipal = map[string]int{} + duplicateLocalWorkUnitsByPrincipal = map[string]int{} + + for i, workunit := range *workunits { + adWorkUnitsByPrincipal[activeDirectoryPrefix+workunit.guid] = i + for j := range workunit.duplicateUsers { + duplicateLocalWorkUnitsByPrincipal[activeDirectoryPrefix+workunit.guid] = j + duplicateLocalWorkUnitsByPrincipal[activeDirectoryPrefix+workunit.distinguishedName] = j + duplicateLocalWorkUnitsByPrincipal[localPrefix+workunit.duplicateUsers[j].Name] = j + } + } + + return adWorkUnitsByPrincipal, duplicateLocalWorkUnitsByPrincipal +} + func collectCRTBs(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) error { crtbInterface := sc.Management.ClusterRoleTemplateBindings("") crtbList, err := crtbInterface.List(metav1.ListOptions{}) @@ -17,25 +38,17 @@ func collectCRTBs(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) er return err } - // first build a map of guid-principalid -> work unit, which will make the following logic more efficient - originalGUIDWorkUnits := map[string]int{} - duplicateGUIDWorkUnits := map[string]int{} - for i, workunit := range *workunits { - originalGUIDWorkUnits[activeDirectoryPrefix+workunit.guid] = i - for j := range workunit.duplicateUsers { - duplicateGUIDWorkUnits[activeDirectoryPrefix+workunit.guid] = j - } - } + adWorkUnitsByPrincipal, duplicateLocalWorkUnitsByPrincipal := principalsToMigrate(workunits) for _, crtb := range crtbList.Items { - if index, exists := originalGUIDWorkUnits[crtb.UserPrincipalName]; exists { + if index, exists := adWorkUnitsByPrincipal[crtb.UserPrincipalName]; exists { if workUnitContainsName(&(*workunits)[index], crtb.UserName) { - (*workunits)[index].guidCRTBs = append((*workunits)[index].guidCRTBs, crtb) + (*workunits)[index].activeDirectoryCRTBs = append((*workunits)[index].activeDirectoryCRTBs, crtb) } else { logrus.Warnf("[%v] found CRTB for user with guid-based principal '%v' and name '%v', but no user object with that name matches the GUID or its associated DN. refusing to process", identifyAdUserOperation, crtb.UserPrincipalName, crtb.UserName) } - } else if index, exists = duplicateGUIDWorkUnits[crtb.UserPrincipalName]; exists { + } else if index, exists = duplicateLocalWorkUnitsByPrincipal[crtb.UserPrincipalName]; exists { if workUnitContainsName(&(*workunits)[index], crtb.UserName) { (*workunits)[index].duplicateLocalCRTBs = append((*workunits)[index].duplicateLocalCRTBs, crtb) } else { @@ -56,25 +69,17 @@ func collectPRTBs(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) er return err } - // first build a map of guid-principalid -> work unit, which will make the following logic more efficient - originalGUIDWorkUnits := map[string]int{} - duplicateGUIDWorkUnits := map[string]int{} - for i, workunit := range *workunits { - originalGUIDWorkUnits[activeDirectoryPrefix+workunit.guid] = i - for j := range workunit.duplicateUsers { - duplicateGUIDWorkUnits[activeDirectoryPrefix+workunit.guid] = j - } - } + adWorkUnitsByPrincipal, duplicateLocalWorkUnitsByPrincipal := principalsToMigrate(workunits) for _, prtb := range prtbList.Items { - if index, exists := originalGUIDWorkUnits[prtb.UserPrincipalName]; exists { + if index, exists := adWorkUnitsByPrincipal[prtb.UserPrincipalName]; exists { if workUnitContainsName(&(*workunits)[index], prtb.UserName) { - (*workunits)[index].guidPRTBs = append((*workunits)[index].guidPRTBs, prtb) + (*workunits)[index].activeDirectoryPRTBs = append((*workunits)[index].activeDirectoryPRTBs, prtb) } else { logrus.Warnf("[%v] found PRTB for user with guid-based principal '%v' and name '%v', but no user object with that name matches the GUID or its associated DN. refusing to process", identifyAdUserOperation, prtb.UserPrincipalName, prtb.UserName) } - } else if index, exists = duplicateGUIDWorkUnits[prtb.UserPrincipalName]; exists { + } else if index, exists = duplicateLocalWorkUnitsByPrincipal[prtb.UserPrincipalName]; exists { if workUnitContainsName(&(*workunits)[index], prtb.UserName) { (*workunits)[index].duplicateLocalPRTBs = append((*workunits)[index].duplicateLocalPRTBs, prtb) } else { @@ -87,11 +92,36 @@ func collectPRTBs(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) er return nil } +func collectGRBs(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) error { + grbInterface := sc.Management.GlobalRoleBindings("") + grbList, err := grbInterface.List(metav1.ListOptions{}) + if err != nil { + logrus.Errorf("[%v] unable to fetch GRB objects: %v", migrateAdUserOperation, err) + return err + } + + duplicateLocalWorkUnitsByName := map[string]int{} + + for _, workunit := range *workunits { + for j := range workunit.duplicateUsers { + duplicateLocalWorkUnitsByName[workunit.duplicateUsers[j].Name] = j + } + } + + for _, grb := range grbList.Items { + if index, exists := duplicateLocalWorkUnitsByName[grb.UserName]; exists { + (*workunits)[index].duplicateLocalGRBs = append((*workunits)[index].duplicateLocalGRBs, grb) + } + } + + return nil +} + func migrateCRTBs(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryRun bool) error { crtbInterface := sc.Management.ClusterRoleTemplateBindings("") // First convert all GUID-based CRTBs to their equivalent Distinguished Name variants dnPrincipalID := activeDirectoryPrefix + workunit.distinguishedName - for _, oldCrtb := range workunit.guidCRTBs { + for _, oldCrtb := range workunit.activeDirectoryCRTBs { if dryRun { logrus.Infof("[%v] DRY RUN: would migrate CRTB '%v' from GUID principal '%v' to DN principal '%v'. "+ "Additionally, an annotation, %v, would be added containing the principal being migrated from and"+ @@ -183,7 +213,7 @@ func migratePRTBs(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryRu prtbInterface := sc.Management.ProjectRoleTemplateBindings("") // First convert all GUID-based PRTBs to their equivalent Distinguished Name variants dnPrincipalID := activeDirectoryPrefix + workunit.distinguishedName - for _, oldPrtb := range workunit.guidPRTBs { + for _, oldPrtb := range workunit.activeDirectoryPRTBs { if dryRun { logrus.Infof("[%v] DRY RUN: would migrate PRTB '%v' from GUID principal '%v' to DN principal '%v'. "+ "Additionally, an annotation, %v, would be added containing the principal being migrated from and"+ @@ -272,3 +302,43 @@ func migratePRTBs(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryRu } return nil } + +func migrateGRBs(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryRun bool) error { + grbInterface := sc.Management.GlobalRoleBindings("") + + for _, oldGrb := range workunit.duplicateLocalGRBs { + if dryRun { + logrus.Infof("[%v] DRY RUN: would migrate GRB '%v' from duplicate local user '%v' to original user '%v'"+ + "Additionally, labels %v and %v will be added. These contain the name of the previous GRB and indicate that this GRB has been migrated.", + migrateGrbsOperation, oldGrb.Name, oldGrb.UserName, workunit.originalUser.Name, migrationPreviousName, adGUIDMigrationLabel) + } else { + newLabels := oldGrb.Labels + if newLabels == nil { + newLabels = make(map[string]string) + } + newLabels[migrationPreviousName] = oldGrb.Name + newLabels[adGUIDMigrationLabel] = migratedLabelValue + + newGrb := &v3.GlobalRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "", + GenerateName: "grb-", + Annotations: oldGrb.Annotations, + Labels: newLabels, + }, + GlobalRoleName: oldGrb.GlobalRoleName, + GroupPrincipalName: oldGrb.GroupPrincipalName, + UserName: workunit.originalUser.Name, + } + _, err := grbInterface.Create(newGrb) + if err != nil { + return fmt.Errorf("[%v] unable to create new GRB: %w", migrateGrbsOperation, err) + } + err = sc.Management.GlobalRoleBindings("").Delete(oldGrb.Name, &metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("[%v] unable to delete GRB: %w", migrateGrbsOperation, err) + } + } + } + return nil +} \ No newline at end of file diff --git a/pkg/agent/clean/ad_unmigration/tokens.go b/pkg/agent/clean/ad_unmigration/tokens.go index 1cd330566b8..32514acbc91 100644 --- a/pkg/agent/clean/ad_unmigration/tokens.go +++ b/pkg/agent/clean/ad_unmigration/tokens.go @@ -9,10 +9,37 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func collectTokens(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) error { + tokenInterface := sc.Management.Tokens("") + tokenList, err := tokenInterface.List(metav1.ListOptions{}) + if err != nil { + logrus.Errorf("[%v] unable to fetch token objects: %v", migrateAdUserOperation, err) + return err + } + + for i, workunit := range *workunits { + guidPrincipal := activeDirectoryPrefix + workunit.guid + for _, token := range tokenList.Items { + if guidPrincipal == token.UserPrincipal.Name || workunit.originalUser.Name == token.UserID { + workunit.activeDirectoryTokens = append(workunit.activeDirectoryTokens, token) + } else { + for _, duplicateLocalUser := range workunit.duplicateUsers { + if localPrincipalID(duplicateLocalUser) == token.UserPrincipal.Name { + workunit.duplicateLocalTokens = append(workunit.duplicateLocalTokens, token) + } + } + } + } + (*workunits)[i] = workunit + } + + return nil +} + func migrateTokens(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryRun bool) error { tokenInterface := sc.Management.Tokens("") dnPrincipalID := activeDirectoryPrefix + workunit.distinguishedName - for _, userToken := range workunit.guidTokens { + for _, userToken := range workunit.activeDirectoryTokens { if dryRun { logrus.Infof("[%v] DRY RUN: would migrate token '%v' from GUID principal '%v' to DN principal '%v'. "+ "Additionally, it would add an annotation, %v, indicating the former principalID of this token "+ @@ -72,30 +99,3 @@ func migrateTokens(workunit *migrateUserWorkUnit, sc *config.ScaledContext, dryR } return nil } - -func collectTokens(workunits *[]migrateUserWorkUnit, sc *config.ScaledContext) error { - tokenInterface := sc.Management.Tokens("") - tokenList, err := tokenInterface.List(metav1.ListOptions{}) - if err != nil { - logrus.Errorf("[%v] unable to fetch token objects: %v", migrateAdUserOperation, err) - return err - } - - for i, workunit := range *workunits { - guidPrincipal := activeDirectoryPrefix + workunit.guid - for _, token := range tokenList.Items { - if guidPrincipal == token.UserPrincipal.Name || workunit.originalUser.Name == token.UserID { - workunit.guidTokens = append(workunit.guidTokens, token) - } else { - for _, duplicateLocalUser := range workunit.duplicateUsers { - if localPrincipalID(duplicateLocalUser) == token.UserPrincipal.Name { - workunit.duplicateLocalTokens = append(workunit.duplicateLocalTokens, token) - } - } - } - } - (*workunits)[i] = workunit - } - - return nil -}