Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utils command to migrate iamidentitymappings to EKS access entries #7710

Merged
merged 22 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a81e482
Added migrate-to-access-entry cmd structure
Mar 1, 2024
e7c03f1
Fix Target Authentication mode validation
Mar 1, 2024
bcbcf5f
Added logic to get accessEntries and cmEntries from cluster
venaws Mar 1, 2024
9f79636
Added logic to make unique list of configmap accessEntries, and stack…
venaws Mar 1, 2024
3b82eca
Added UpdateAuthentication mode and aeEntries filter logic
Mar 3, 2024
22ab908
Add approve flag check
Mar 5, 2024
8ec312d
Added functionality to remove awsauth after switch to API only
veekaly Mar 22, 2024
6872eb5
Adds logic to fetch FullARN of path stripped IAMIdentityMappings
Apr 12, 2024
5e7203a
Updates some info log text
Apr 12, 2024
a6850fe
Adds test case and refactors code
Apr 13, 2024
e50ac72
Removes comments
Apr 15, 2024
3bd5927
Adds taskTree and address PR comments
Apr 18, 2024
e9ca916
Refactors code and Adds exception handling for NoSuchEntityException
punkwalker Apr 18, 2024
7b58656
Resolves go.mod and go.sum conflicts
punkwalker Apr 18, 2024
3dce348
Doc update for migrate-to-access-entry feature
veekaly Apr 19, 2024
7b7b60e
Fixed minimum iam policies doc to add permission for iam:GetUser
veekaly Apr 22, 2024
2e14dfa
Updated access-entries doc at migrate-to-access-entry section
veekaly Apr 22, 2024
df601bc
Fixes failing Migrate To Access Entry Test & go.mod, go.sum
punkwalker Apr 22, 2024
0f55a7a
Amends migrate to access entry documentation
punkwalker Apr 22, 2024
a40a312
improve logs and simplify code logic
TiberiuGC Apr 23, 2024
a21a8c1
add unit tests
TiberiuGC Apr 24, 2024
f34dd6f
ensure target-auth-mode has a valid value
TiberiuGC Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Adds taskTree and address PR comments
  • Loading branch information
Pankaj Walke authored and TiberiuGC committed Apr 25, 2024
commit 3bd5927808b03d4da25bf25e6664f6cb279fde6d
138 changes: 56 additions & 82 deletions pkg/actions/accessentry/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import (
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
awsiam "github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/kris-nova/logger"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/authconfigmap"
"github.com/weaveworks/eksctl/pkg/awsapi"
"github.com/weaveworks/eksctl/pkg/eks/waiter"
"github.com/weaveworks/eksctl/pkg/iam"
"github.com/weaveworks/eksctl/pkg/kubernetes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/weaveworks/eksctl/pkg/utils/tasks"
)

type MigrationOptions struct {
Expand Down Expand Up @@ -59,68 +60,71 @@ func NewMigrator(
}

func (m *Migrator) MigrateToAccessEntry(ctx context.Context, options MigrationOptions) error {
taskTree := tasks.TaskTree{
Parallel: false,
PlanMode: !options.Approve,
}

if m.curAuthMode != m.tgAuthMode {
if m.curAuthMode != ekstypes.AuthenticationModeApiAndConfigMap {
logger.Info("target authentication mode %v is different than the current authentication mode %v, Updating the cluster authentication mode", m.tgAuthMode, m.curAuthMode)
err := m.doUpdateAuthenticationMode(ctx, ekstypes.AuthenticationModeApiAndConfigMap, options.Timeout)
if err != nil {
return err
}
}
m.curAuthMode = ekstypes.AuthenticationModeApiAndConfigMap
taskTree.Append(&tasks.GenericTask{
Description: fmt.Sprintf("update authentication mode to %v", ekstypes.AuthenticationModeApiAndConfigMap),
Doer: func() error {
if m.curAuthMode != ekstypes.AuthenticationModeApiAndConfigMap {
logger.Info("target authentication mode %v is different than the current authentication mode %v, Updating the cluster authentication mode", m.tgAuthMode, m.curAuthMode)
return m.doUpdateAuthenticationMode(ctx, ekstypes.AuthenticationModeApiAndConfigMap, options.Timeout)
}
m.curAuthMode = ekstypes.AuthenticationModeApiAndConfigMap
return nil
},
})
} else {
logger.Info("target authentication mode %v is same as current authentication mode %v, not updating the cluster authentication mode", m.tgAuthMode, m.curAuthMode)
}

cmEntries, err := m.doGetIAMIdentityMappings()
cmEntries, err := m.doGetIAMIdentityMappings(ctx)
if err != nil {
return err
}

curAccessEntries, err := m.doGetAccessEntries(ctx)
if err != nil {
if err != nil && m.curAuthMode != ekstypes.AuthenticationModeConfigMap {
return err
}

punkwalker marked this conversation as resolved.
Show resolved Hide resolved
newAccessEntries, skipAPImode, err := doFilterAccessEntries(cmEntries, curAccessEntries)
punkwalker marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return err
}

newaelen := len(newAccessEntries)

logger.Info("%d new access entries will be created", newaelen)

if len(newAccessEntries) != 0 {
err = m.aeCreator.Create(ctx, newAccessEntries)
if err != nil {
return err
}
if newaelen := len(newAccessEntries); newaelen != 0 {
logger.Info("%d new access entries will be created", newaelen)
aeTasks := m.aeCreator.CreateTasks(ctx, newAccessEntries)
aeTasks.IsSubTask = true
taskTree.Append(aeTasks)
}

if !skipAPImode {
punkwalker marked this conversation as resolved.
Show resolved Hide resolved
if m.curAuthMode != m.tgAuthMode {
logger.Info("target authentication mode %v is different than the current authentication mode %v, updating the cluster authentication mode", m.tgAuthMode, m.curAuthMode)
err = m.doUpdateAuthenticationMode(ctx, m.tgAuthMode, options.Timeout)
if err != nil {
return err
}

err = m.doDeleteIAMIdentityMapping()
if err != nil {
return err
}
if m.tgAuthMode == ekstypes.AuthenticationModeApi {
taskTree.Append(&tasks.GenericTask{
Description: fmt.Sprintf("update authentication mode to %v", ekstypes.AuthenticationModeApi),
Doer: func() error {
logger.Info("target authentication mode %v is different than the current authentication mode %v, updating the cluster authentication mode", m.tgAuthMode, m.curAuthMode)
return m.doUpdateAuthenticationMode(ctx, m.tgAuthMode, options.Timeout)
},
})

err = doDeleteAWSAuthConfigMap(m.clientSet, "kube-system", "aws-auth")
if err != nil {
return err
}
taskTree.Append(&tasks.GenericTask{
Description: fmt.Sprintf("delete aws-auth configMap when authentication mode is %v", ekstypes.AuthenticationModeApi),
Doer: func() error {
return doDeleteAWSAuthConfigMap(ctx, m.clientSet, authconfigmap.ObjectNamespace, authconfigmap.ObjectName)
},
})
}
} else if m.tgAuthMode == ekstypes.AuthenticationModeApi {
logger.Warning("one or more identitymapping could not be migrated to access entry, will not update authentication mode to %v", ekstypes.AuthenticationModeApi)
}

return nil

return runAllTasks(&taskTree)
}

func (m *Migrator) doUpdateAuthenticationMode(ctx context.Context, authMode ekstypes.AuthenticationMode, timeout time.Duration) error {
Expand Down Expand Up @@ -148,29 +152,21 @@ func (m *Migrator) doUpdateAuthenticationMode(ctx context.Context, authMode ekst
return fmt.Errorf("request to update cluster authentication mode was cancelled: %s", e.UpdateError)
}
return fmt.Errorf("failed to update cluster authentication mode: %s", e.UpdateError)

case nil:
logger.Info("authentication mode was successfully updated to %s on cluster %s", authMode, m.clusterName)
m.curAuthMode = authMode
return nil

default:
return err
}
}

func (m *Migrator) doGetAccessEntries(ctx context.Context) ([]Summary, error) {

aegetter := NewGetter(m.clusterName, m.eksAPI)
accessEntries, err := aegetter.Get(ctx, api.ARN{})
if err != nil {
return nil, err
}

return accessEntries, nil
aeGetter := NewGetter(m.clusterName, m.eksAPI)
return aeGetter.Get(ctx, api.ARN{})
}

func (m *Migrator) doGetIAMIdentityMappings() ([]iam.Identity, error) {
func (m *Migrator) doGetIAMIdentityMappings(ctx context.Context) ([]iam.Identity, error) {

nameRegex := regexp.MustCompile(`[^/]+$`)
punkwalker marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -196,7 +192,7 @@ func (m *Migrator) doGetIAMIdentityMappings() ([]iam.Identity, error) {
}

if match := nameRegex.FindStringSubmatch(roleCme.RoleARN); match != nil {
getRoleOutput, err := m.iamAPI.GetRole(context.Background(), &awsiam.GetRoleInput{RoleName: &match[0]})
getRoleOutput, err := m.iamAPI.GetRole(ctx, &awsiam.GetRoleInput{RoleName: &match[0]})
if err != nil {
return nil, err
}
Expand All @@ -216,16 +212,14 @@ func (m *Migrator) doGetIAMIdentityMappings() ([]iam.Identity, error) {
}

if match := nameRegex.FindStringSubmatch(userCme.UserARN); match != nil {
getUserOutput, err := m.iamAPI.GetUser(context.Background(), &awsiam.GetUserInput{UserName: &match[0]})
getUserOutput, err := m.iamAPI.GetUser(ctx, &awsiam.GetUserInput{UserName: &match[0]})
if err != nil {
return nil, err
}

userCme.UserARN = *getUserOutput.User.Arn
}

cmEntries[idx] = iam.Identity(userCme)

}
}

Expand All @@ -235,10 +229,9 @@ func (m *Migrator) doGetIAMIdentityMappings() ([]iam.Identity, error) {
func doFilterAccessEntries(cmEntries []iam.Identity, accessEntries []Summary) ([]api.AccessEntry, bool, error) {

skipAPImode := false
toDoEntries := []api.AccessEntry{}
var toDoEntries []api.AccessEntry
uniqueCmEntries := map[string]bool{}

aeArns := make(map[string]bool)
aeArns := map[string]bool{}

// Create ARN Map for current access entries
for _, ae := range accessEntries {
Expand All @@ -254,10 +247,14 @@ func doFilterAccessEntries(cmEntries []iam.Identity, accessEntries []Summary) ([
toDoEntries = append(toDoEntries, *aeEntry)
} else if aeEntry := doBuildAccessEntry(cme); aeEntry != nil {
toDoEntries = append(toDoEntries, *aeEntry)
} else {
skipAPImode = true
}
case iam.ResourceTypeUser:
if aeEntry := doBuildAccessEntry(cme); aeEntry != nil {
toDoEntries = append(toDoEntries, *aeEntry)
} else {
skipAPImode = true
}
case iam.ResourceTypeAccount:
logger.Warning("found account mapping %s, can not create access entry for account mapping, skipping", cme.Account())
Expand Down Expand Up @@ -327,31 +324,8 @@ func doBuildAccessEntry(cme iam.Identity) *api.AccessEntry {

}

func (m Migrator) doDeleteIAMIdentityMapping() error {
acm, err := authconfigmap.NewFromClientSet(m.clientSet)
if err != nil {
return err
}

cmEntries, err := acm.GetIdentities()
if err != nil {
return err
}

for _, cmEntry := range cmEntries {
arn := cmEntry.ARN()
if err := acm.RemoveIdentity(arn, true); err != nil {
return err
}
}
return acm.Save()
}
func doDeleteAWSAuthConfigMap(ctx context.Context, clientset kubernetes.Interface, namespace, name string) error {
logger.Info("deleting %q ConfigMap as it is no longer needed in API mode", name)
return clientset.CoreV1().ConfigMaps(namespace).Delete(ctx, name, metav1.DeleteOptions{})

func doDeleteAWSAuthConfigMap(clientset kubernetes.Interface, namespace string, name string) error {
logger.Info("Deleting %q ConfigMap as it is no longer needed in API mode", name)
err := clientset.CoreV1().ConfigMaps(namespace).Delete(context.Background(), name, metav1.DeleteOptions{})
if err != nil {
return err
}
return nil
}
20 changes: 20 additions & 0 deletions pkg/actions/accessentry/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/awsapi"
"github.com/weaveworks/eksctl/pkg/cfn/builder"
"github.com/weaveworks/eksctl/pkg/utils/tasks"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
Expand Down Expand Up @@ -127,3 +128,22 @@ func (t *deleteOwnedAccessEntryTask) Do(errorCh chan error) error {

return nil
}

func runAllTasks(taskTree *tasks.TaskTree) error {
logger.Info(taskTree.Describe())
if errs := taskTree.DoAllSync(); len(errs) > 0 {
var allErrs []string
for _, err := range errs {
allErrs = append(allErrs, err.Error())
}
return fmt.Errorf(strings.Join(allErrs, "\n"))
}
completedAction := func() string {
if taskTree.PlanMode {
return "skipped"
}
return "completed successfully"
}
logger.Info("all tasks were %s", completedAction())
return nil
}
14 changes: 5 additions & 9 deletions pkg/ctl/utils/migrate_to_access_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ func migrateAccessEntryCmd(cmd *cmdutils.Cmd) {
}

func doMigrateToAccessEntry(cmd *cmdutils.Cmd, options accessentryactions.MigrationOptions) error {
defer cmdutils.LogPlanModeWarning(options.Approve)
options.Approve = !cmd.Plan
cfg := cmd.ClusterConfig
cmd.ClusterConfig.AccessConfig.AuthenticationMode = ekstypes.AuthenticationMode(options.TargetAuthMode)
tgAuthMode := cmd.ClusterConfig.AccessConfig.AuthenticationMode
tgAuthMode := ekstypes.AuthenticationMode(options.TargetAuthMode)

if cfg.Metadata.Name == "" {
return cmdutils.ErrMustBeSet(cmdutils.ClusterNameFlag(cmd))
}

if cmd.Plan {
cmdutils.LogPlanModeWarning(true)
return nil
if tgAuthMode != ekstypes.AuthenticationModeApi && tgAuthMode != ekstypes.AuthenticationModeApiAndConfigMap {
return fmt.Errorf("target authentication mode is invalid, must be either %s or %s", ekstypes.AuthenticationModeApi, ekstypes.AuthenticationModeApiAndConfigMap)
}

ctx := context.Background()
Expand All @@ -60,10 +60,6 @@ func doMigrateToAccessEntry(cmd *cmdutils.Cmd, options accessentryactions.Migrat
return err
}

if tgAuthMode != ekstypes.AuthenticationModeApi && tgAuthMode != ekstypes.AuthenticationModeApiAndConfigMap {
return fmt.Errorf("target authentication mode is invalid")
}

curAuthMode := ctl.GetClusterState().AccessConfig.AuthenticationMode

clientSet, err := ctl.NewStdClientSet(cfg)
Expand Down