diff --git a/.gitignore b/.gitignore index d0834b9a8..2f007e240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -aws-iam-authenticator - /dist /_output diff --git a/cmd/aws-iam-authenticator/add.go b/cmd/aws-iam-authenticator/add.go index b328884d8..595a0dae5 100644 --- a/cmd/aws-iam-authenticator/add.go +++ b/cmd/aws-iam-authenticator/add.go @@ -42,7 +42,7 @@ var addUserCmd = &cobra.Command{ Long: "NOTE: this does not currently support the CRD and file backends", Run: func(cmd *cobra.Command, args []string) { if userARN == "" || userName == "" || len(groups) == 0 { - fmt.Printf("invalid empty value in userARN %q, username %q, groups %q\n", userARN, userName, groups) + fmt.Printf("invalid empty value in userARN %q, username %q, groups %q", userARN, userName, groups) os.Exit(1) } @@ -73,52 +73,16 @@ var addRoleCmd = &cobra.Command{ Short: "add a role entity to an existing aws-auth configmap, not for CRD/file backends", Long: "NOTE: this does not currently support the CRD and file backends", Run: func(cmd *cobra.Command, args []string) { - if (roleARN == "" && ssoRole == nil) || userName == "" || len(groups) == 0 { - fmt.Printf("invalid empty value in rolearn %q, username %q, groups %q\n", roleARN, userName, groups) + if roleARN == "" || userName == "" || len(groups) == 0 { + fmt.Printf("invalid empty value in rolearn %q, username %q, groups %q", roleARN, userName, groups) os.Exit(1) } - var arnOrSSORole string - switch { - case roleARN != "" && ssoRole != nil: - fmt.Printf("only one of --rolearn or --sso can be supplied\n") - os.Exit(1) - case roleARN != "": - arnOrSSORole = "rolearn" - case ssoRole != nil: - arnOrSSORole = "sso" - - for _, key := range []string{"permissionSetName", "accountID"} { - if _, ok := ssoRole[key]; !ok { - fmt.Printf("required key '%s' missing from --sso flag\n", key) - os.Exit(1) - } - } - - var ssoPartition string - if partition, ok := ssoRole["partition"]; !ok { - ssoPartition = "aws" - } else { - ssoPartition = partition - } - ssoRoleConfig.PermissionSetName = ssoRole["permissionSetName"] - ssoRoleConfig.AccountID = ssoRole["accountID"] - ssoRoleConfig.Partition = ssoPartition - - rm := config.RoleMapping{SSO: ssoRoleConfig} - err := rm.Validate() - if err != nil { - fmt.Printf("error validating --sso: %s\n", err) - os.Exit(1) - } - } - - checkPrompt(fmt.Sprintf("add %s %s, username %s, groups %s", arnOrSSORole, roleARN, userName, groups)) + checkPrompt(fmt.Sprintf("add rolearn %s, username %s, groups %s", roleARN, userName, groups)) cli := createClient() cm, err := cli.AddRole(&config.RoleMapping{ RoleARN: roleARN, - SSO: ssoRoleConfig, Username: userName, Groups: groups, }) @@ -210,10 +174,6 @@ var ( userName string groups []string roleARN string - // ssoRole contains the settings for a config.SSOARNMatcher - // it expects the keys "permissionSetName", "accountID", and "partition" (optional) - ssoRole map[string]string - ssoRoleConfig *config.SSOARNMatcher ) func init() { @@ -231,7 +191,6 @@ func init() { addUserCmd.PersistentFlags().StringSliceVar(&groups, "groups", nil, "A new user groups") addRoleCmd.PersistentFlags().StringVar(&roleARN, "rolearn", "", "A new role ARN") - addRoleCmd.PersistentFlags().StringToStringVar(&ssoRole, "sso", nil, `Settings for a new SSO role. Expects "permissionSetName", "accountID", and "partition" (optional)`) addRoleCmd.PersistentFlags().StringVar(&userName, "username", "", "A new user name") addRoleCmd.PersistentFlags().StringSliceVar(&groups, "groups", nil, "A new role groups") } diff --git a/cmd/aws-iam-authenticator/root.go b/cmd/aws-iam-authenticator/root.go index 946344c02..e4196b271 100644 --- a/cmd/aws-iam-authenticator/root.go +++ b/cmd/aws-iam-authenticator/root.go @@ -115,10 +115,6 @@ func getConfig() (config.Config, error) { logrus.WithError(err).Fatal("invalid server account mappings") } - if featureGates.Enabled(config.SSORoleMatch) { - logrus.Info("SSORoleMatch feature enabled") - config.SSORoleMatchEnabled = true - } if featureGates.Enabled(config.ConfiguredInitDirectories) { logrus.Info("ConfiguredInitDirectories feature enabled") } diff --git a/docs/sso_role_matcher.md b/docs/sso_role_matcher.md deleted file mode 100644 index 39c854960..000000000 --- a/docs/sso_role_matcher.md +++ /dev/null @@ -1,62 +0,0 @@ -# SSO Role Matcher - -Maps configuration for an AWS SSO managed IAM Role to a Kubernetes username and groups. - -## Feature state - -Alpha - -## Use case - -Easy and robust configuration for AWS SSO managed roles, which currently have two main issues: - -Firstly - confusing configuration. To use an SSO role, a user needs to map the Role ARN of the SSO ROle, minus the path. - -For example: given a permission set `MyPermissionSet`, region `us-east-1` and account number `000000000000`; AWS SSO -creates a role: `arn:aws:iam::000000000000:role/aws-reserved/sso.amazonaws.com/us-east-1/AWSReservedSSO_MyPermissionSet_1234567890abcde`. - -To match this role, a user would need to create a mapRoles entry like: -``` - mapRoles: | - - rolearn: arn:aws:iam::000000000000:role/AWSReservedSSO_MyPermissionSet_1234567890abcde - username: ... - groups: ... -``` - -Secondly - brittle configuration. If AWS SSO recreates IAM Roles, they receive a different random suffix and all the users of that -role can no longer authenticate to Kubernetes. - -## New UX - -Users can create a mapRoles entry that will automatically match roles created by AWS SSO without needing to be updated -every time the roles are changed. - -Users will now create mapRoles entries like: -``` - mapRoles: | - - sso: - permissionSetName: MyPermissionSet - accountID: "000000000000" - username: ... - groups: ... -``` - -If the user is using the aws-us-govt or aws-cn partitions, they must specify the partition attribute in the `sso` structure. -``` - mapRoles: | - - sso: - permissionSetName: MyPermissionSet - accountID: "000000000000" - partition: "aws-us-govt" - username: ... - groups: ... -``` - -## Implementation - -config.RoleMapping will be extended with a nested structure containing the necessary information to construct a canonicalized -Role Arn. The random suffix will not need to be specified and will instead be matched for the user by constructing the -expected ARN and applying a wildcard to the end. - -Users are protected from non-AWS SSO created roles as the AWS API prevents roles being manually created with AWSReservedSSO -at the beginning of their names. diff --git a/pkg/arn/arnlike.go b/pkg/arn/arnlike.go deleted file mode 100644 index c9e46cb52..000000000 --- a/pkg/arn/arnlike.go +++ /dev/null @@ -1,104 +0,0 @@ -package arn - -import ( - "fmt" - "regexp" - "strings" -) - -const ( - arnDelimiter = ":" - arnSectionsExpected = 6 - arnPrefix = "arn:" - - // zero-indexed - sectionPartition = 1 - sectionService = 2 - sectionRegion = 3 - sectionAccountID = 4 - sectionResource = 5 - - // errors - invalidPrefix = "invalid prefix" - invalidSections = "not enough sections" -) - -// ArnLike takes an ARN and returns true if it is matched by the pattern. -// Each component of the ARN is matched individually as per -// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_ARN -func ArnLike(arn, pattern string) (bool, error) { - // "parse" the input arn into sections - arnSections, err := parse(arn) - if err != nil { - return false, fmt.Errorf("Could not parse input arn: %v", err) - } - patternSections, err := parse(pattern) - if err != nil { - return false, fmt.Errorf("Could not parse ArnLike string: %v", err) - } - - // Tidy regexp special characters. Escape the ones not used in ArnLike. - // Replace multiple * with .* - we're assuming `\` is not allowed in ARNs - preparePatternSections(patternSections) - - for index := range arnSections { - patternGlob, err := regexp.Compile(patternSections[index]) - if err != nil { - return false, fmt.Errorf("Could not parse %s: %v", patternSections[index], err) - } - - if !patternGlob.MatchString(arnSections[index]) { - return false, nil - } - } - - return true, nil -} - -// parse is a copy of arn.Parse from the AWS SDK but represents the ARN as []string -func parse(input string) ([]string, error) { - if !strings.HasPrefix(input, arnPrefix) { - return nil, fmt.Errorf(invalidPrefix) - } - arnSections := strings.SplitN(input, arnDelimiter, arnSectionsExpected) - if len(arnSections) != arnSectionsExpected { - return nil, fmt.Errorf(invalidSections) - } - - return arnSections, nil -} - -// preparePatternSections goes through each section of the arnLike slice and escapes any meta characters, except for -// `*` and `?` which are replaced by `.*` and `.?` respectively. ^ and $ are added as we require an exact match -func preparePatternSections(arnLikeSlice []string) { - for index, section := range arnLikeSlice { - quotedString := quoteMeta(section) - arnLikeSlice[index] = `^` + quotedString + `$` - } -} - -// the below is based on regexp.QuoteMeta to escape metacharacters except for `?` and `*`, changing them to `*` and `.*` - -// quoteMeta returns a string that escapes all regular expression metacharacters -// inside the argument text; the returned string is a regular expression matching -// the literal text. -func quoteMeta(s string) string { - const specialChars = `\.+()|[]{}^$` - - var i int - b := make([]byte, 2*len(s)-i) - copy(b, s[:i]) - j := i - for ; i < len(s); i++ { - if strings.Contains(specialChars, s[i:i+1]) { - b[j] = '\\' - j++ - } else if s[i] == '*' || s[i] == '?' { - b[j] = '.' - j++ - } - b[j] = s[i] - j++ - } - return string(b[:j]) -} diff --git a/pkg/arn/arnlike_test.go b/pkg/arn/arnlike_test.go deleted file mode 100644 index 0b4cfc8b1..000000000 --- a/pkg/arn/arnlike_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package arn - -import ( - "strings" - "testing" -) - -type arnLikeInput struct { - arn, pattern string -} - -type quoteMetaInput struct { - input, expected string -} - -func TestArnLikePostiveMatches(t *testing.T) { - inputs := []arnLikeInput{ - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:role/some-role`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:*`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:*:*:*:*:*`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:**`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:*role*`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:ro*`, - }, - { - arn: `arn:aws:iam::000000000000:role/some-role`, - pattern: `arn:aws:iam::000000000000:??????????????`, - }, - { - arn: `arn:aws:testservice::000000000000:some/wacky-new-[resource]{with}\metacharacters`, - pattern: `arn:aws:testservice::000000000000:some/wacky-new-[resource]{with}\metacharacters`, - }, - { - arn: `arn:aws:testservice::000000000000:some/wacky-new-[resource]{with}\metacharacters`, - pattern: `arn:aws:testservice::000000000000:some/wacky-new-[reso*`, - }, - } - - for _, v := range inputs { - ok, err := ArnLike(v.arn, v.pattern) - if err != nil { - t.Errorf("Expected no error for input arn: %s pattern: %s", v.arn, v.pattern) - } - - if !ok { - t.Errorf("Expected true for input arn: %s pattern: %s", v.arn, v.pattern) - } - } -} - -func TestArnLikeNetagiveMatches(t *testing.T) { - inputs := []arnLikeInput{ - { - arn: `arn:aws:iam::111111111111:role/some-role`, - pattern: `arn:aws:iam::000000000000:role/some-role`, - }, - { - arn: `arn:aws:testservice::000000000000:some/wacky:resource:with:colon:delims`, - pattern: `arn:aws:testservice::**:delims`, - }, - } - - for _, v := range inputs { - ok, err := ArnLike(v.arn, v.pattern) - if err != nil { - t.Errorf("Expected no error for input arn: %s pattern: %s", v.arn, v.pattern) - } - - if ok { - t.Errorf("Expected false for input arn: %s pattern: %s", v.arn, v.pattern) - } - } -} - -func TestIncompleteArnLikePattern(t *testing.T) { - incompleteArnLikePattern := "arn:*" - validArn := `arn:aws:iam::000000000000:role/some-role` - - ok, err := ArnLike(validArn, incompleteArnLikePattern) - if ok { - t.Errorf("Expected false result on error for input arn: %s, pattern: %s", incompleteArnLikePattern, validArn) - } - expectedErrorText := "Could not parse ArnLike string: not enough sections" - if !strings.EqualFold(expectedErrorText, err.Error()) { - t.Errorf("Did not receive expected error text. Expected: '%s', got: '%s'", expectedErrorText, err.Error()) - } -} - -func TestArnLikeInvalidArns(t *testing.T) { - invalidPrefixArn := `nar:aws:iam::000000000000:role/some-role` - invalidSectionsArn := `arn:aws:iam:000000000000:role/some-role` - validArn := `arn:aws:iam::000000000000:role/some-role` - - // invalid prefix - ok, err := ArnLike(invalidPrefixArn, validArn) - if ok { - t.Errorf("Expected false result on error for input arn: %s, pattern: %s", invalidPrefixArn, validArn) - } - - expectedErrorText := "Could not parse input arn: invalid prefix" - if !strings.EqualFold(expectedErrorText, err.Error()) { - t.Errorf("Did not receive expected error text. Expected: '%s', got: '%s'", expectedErrorText, err.Error()) - } - - // invalid sections - ok, err = ArnLike(invalidSectionsArn, validArn) - if ok { - t.Errorf("Expected false result on error for input arn: %s, pattern: %s", invalidSectionsArn, validArn) - } - - expectedErrorText = "Could not parse input arn: not enough sections" - if !strings.EqualFold(expectedErrorText, err.Error()) { - t.Errorf("Did not receive expected error text. Expected: '%s', got: '%s'", expectedErrorText, err.Error()) - } -} - -func TestQuoteMeta(t *testing.T) { - inputs := []quoteMetaInput{ - { - input: `**`, - expected: `.*.*`, - }, - { - input: `??`, - expected: `.?.?`, - }, - { - input: `abdcEFG`, - expected: `abdcEFG`, - }, - { - input: `abd.EFG`, - expected: `abd\.EFG`, - }, - { - input: `\.+()|[]{}^$`, - expected: `\\\.\+\(\)\|\[\]\{\}\^\$`, - }, - { - input: `\.+()|[]{}^$*?`, - expected: `\\\.\+\(\)\|\[\]\{\}\^\$.*.?`, - }, - } - - for _, v := range inputs { - output := quoteMeta(v.input) - if !strings.EqualFold(v.expected, output) { - t.Errorf("Did not get expected output from quoteMeta. Expected: '%s', got: '%s'", v.expected, output) - } - } -} diff --git a/pkg/config/features.go b/pkg/config/features.go index 52e0ab2b4..be559e070 100644 --- a/pkg/config/features.go +++ b/pkg/config/features.go @@ -26,17 +26,9 @@ const ( ConfiguredInitDirectories featuregate.Feature = "ConfiguredInitDirectories" // IAMIdentityMappingCRD enables using CRDs to manage allowed users IAMIdentityMappingCRD featuregate.Feature = "IAMIdentityMappingCRD" - // SSORoleMatch enables matching roles managed by AWS SSO, with handling - // for their randomly generated suffixes - SSORoleMatch featuregate.Feature = "SSORoleMatch" -) - -var ( - SSORoleMatchEnabled bool ) var DefaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ ConfiguredInitDirectories: {Default: false, PreRelease: featuregate.Alpha}, IAMIdentityMappingCRD: {Default: false, PreRelease: featuregate.Alpha}, - SSORoleMatch: {Default: false, PreRelease: featuregate.Alpha}, } diff --git a/pkg/config/mapper.go b/pkg/config/mapper.go deleted file mode 100644 index 5629bfe95..000000000 --- a/pkg/config/mapper.go +++ /dev/null @@ -1,125 +0,0 @@ -package config - -import ( - "fmt" - "regexp" - "strings" - - "sigs.k8s.io/aws-iam-authenticator/pkg/arn" - - "github.com/sirupsen/logrus" -) - -// SSOArnLike returns a string that can be passed to arnlike.ArnLike to -// match canonicalized IAM Role ARNs against. Assumes Validate() has been called. -func (m *RoleMapping) SSOArnLike() string { - if m.SSO == nil { - return "" - } - - var partition string - if m.SSO.Partition == "" { - partition = "aws" - } - - return strings.ToLower(fmt.Sprintf("arn:%s:iam::%s:role/AWSReservedSSO_%s_*", partition, m.SSO.AccountID, m.SSO.PermissionSetName)) -} - -// Validate returns an error if the RoleMapping is not valid after being unmarshaled -func (m *RoleMapping) Validate() error { - if m == nil { - return fmt.Errorf("RoleMapping is nil") - } - - if m.RoleARN == "" && m.SSO == nil { - return fmt.Errorf("One of rolearn or SSO must be supplied") - } else if m.RoleARN != "" && m.SSO != nil { - return fmt.Errorf("Only one of rolearn or SSO can be supplied") - } - - if m.SSO != nil { - accountIDRegexp := regexp.MustCompile("^[0-9]{12}$") - if !accountIDRegexp.MatchString(m.SSO.AccountID) { - return fmt.Errorf("AccountID '%s' is not a valid AWS Account ID", m.SSO.AccountID) - } - - // https://docs.aws.amazon.com/singlesignon/latest/APIReference/API_PermissionSet.html - permissionSetNameRegexp := regexp.MustCompile(`^[\w+=,.@-]{1,32}$`) - if !permissionSetNameRegexp.MatchString(m.SSO.PermissionSetName) { - return fmt.Errorf("PermissionSetName '%s' is not a valid AWS SSO PermissionSet Name", m.SSO.PermissionSetName) - } - - switch m.SSO.Partition { - case "aws", "aws-cn", "aws-us-gov", "aws-iso", "aws-iso-b": - // valid - case "": - // treated as "aws" - default: - return fmt.Errorf("Partition '%s' is not a valid AWS partition", m.SSO.Partition) - } - - ssoArnLikeString := m.SSOArnLike() - ok, err := arn.ArnLike(ssoArnLikeString, "arn:*:iam:*:*:role/*") - if err != nil { - return fmt.Errorf("SSOArnLike '%s' is not valid: %v", ssoArnLikeString, err) - } else if !ok { - return fmt.Errorf("SSOArnLike '%s' did not match an ARN for a canonicalized IAM Role", ssoArnLikeString) - } - } - - return nil -} - -// Matches returns true if the supplied ARN or SSO settings matches -// this RoleMapping -func (m *RoleMapping) Matches(subject string) bool { - if m.RoleARN != "" { - return strings.ToLower(m.RoleARN) == strings.ToLower(subject) - } - - // Assume the caller has called Validate(), which parses m.RoleARNLike - // If subject is not parsable, then it cannot be a valid ARN anyway so - // we can ignore the error here - var ok bool - if SSORoleMatchEnabled { - var err error - ok, err = arn.ArnLike(subject, m.SSOArnLike()) - if err != nil { - logrus.Error("Could not parse subject ARN: ", err) - } - } - return ok -} - -// Key returns RoleARN or SSOArnLike(), whichever is not empty. -// Used to get a Key name for map[string]RoleMapping -func (m *RoleMapping) Key() string { - if m.RoleARN != "" { - return strings.ToLower(m.RoleARN) - } - return m.SSOArnLike() -} - -// Validate returns an error if the UserMapping is not valid after being unmarshaled -func (m *UserMapping) Validate() error { - if m == nil { - return fmt.Errorf("UserMapping is nil") - } - - if m.UserARN == "" { - return fmt.Errorf("Value for userarn must be supplied") - } - - return nil -} - -// Matches returns true if the supplied ARN string matche this UserMapping -func (m *UserMapping) Matches(subject string) bool { - return strings.ToLower(m.UserARN) == strings.ToLower(subject) -} - -// Key returns UserARN. -// Used to get a Key name for map[string]UserMapping -func (m *UserMapping) Key() string { - return m.UserARN -} diff --git a/pkg/config/mapper_test.go b/pkg/config/mapper_test.go deleted file mode 100644 index 2994e2fa3..000000000 --- a/pkg/config/mapper_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package config - -import ( - "reflect" - "testing" -) - -func init() { - SSORoleMatchEnabled = true -} - -func TestSSORoleMapping(t *testing.T) { - rm := RoleMapping{ - SSO: &SSOARNMatcher{ - PermissionSetName: "ViewOnlyAccess", - AccountID: "012345678912", - }, - Username: "admin", - Groups: []string{"system:masters"}, - } - - expectedKey := "arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_*" - actualKey := rm.Key() - - if !reflect.DeepEqual(actualKey, expectedKey) { - t.Errorf("RoleMapping.Key() does not match expected value.\nActual: %v\nExpected: %v", actualKey, expectedKey) - } - - expectedMatch := "arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_abcdefg" - matches := rm.Matches(expectedMatch) - if !matches { - t.Errorf("RoleMapping %v did not match %s", rm, expectedMatch) - } - - unexpectedMatch := "arn:aws:iam::012345678912:role/awsreservedsso_billing_hijklmn" - matches = rm.Matches(unexpectedMatch) - if matches { - t.Errorf("RoleMapping %v unexpectedly matched %s", rm, unexpectedMatch) - } - - err := rm.Validate() - if err != nil { - t.Errorf("Received error %v validating RoleMapping %v", err, rm) - } - - invalidRoleMappings := []RoleMapping{ - { - RoleARN: "", - SSO: &SSOARNMatcher{ - Partition: "aws-nk", // invalid - AccountID: "012345678912", - PermissionSetName: "ViewOnlyAccess", - }, - }, - { - RoleARN: "", - SSO: &SSOARNMatcher{ - Partition: "aws", - AccountID: "0123456789", // too short - PermissionSetName: "ViewOnlyAccess", - }, - }, - { - RoleARN: "", - SSO: &SSOARNMatcher{ - Partition: "aws", - AccountID: "012345678912", - PermissionSetName: "ViewOnlyAccess*", // contains disallowed chars - }, - }, - } - for _, invalidRoleMapping := range invalidRoleMappings { - err = invalidRoleMapping.Validate() - if err == nil { - t.Errorf("Invalid RoleMapping %+v with SSO %+v did not raise error when validated", invalidRoleMapping, invalidRoleMapping.SSO) - } - } -} - -func TestRoleARNMapping(t *testing.T) { - rm := RoleMapping{ - RoleARN: "arn:aws:iam::012345678912:role/KubeAdmin", - Username: "admin", - Groups: []string{"system:masters"}, - } - - expectedKey := "arn:aws:iam::012345678912:role/kubeadmin" - actualKey := rm.Key() - - if !reflect.DeepEqual(actualKey, expectedKey) { - t.Errorf("RoleMapping.Key() does not match expected value.\nActual: %v\nExpected: %v", actualKey, expectedKey) - } - - expectedMatch := "arn:aws:iam::012345678912:role/KubeAdmin" - matches := rm.Matches(expectedMatch) - if !matches { - t.Errorf("RoleMapping %v did not match %s", rm, expectedMatch) - } - - unexpectedMatch := "arn:aws:iam::012345678912:role/notKubeAdmin" - matches = rm.Matches(unexpectedMatch) - if matches { - t.Errorf("RoleMapping %v unexpectedly matched %s", rm, unexpectedMatch) - } - - err := rm.Validate() - if err != nil { - t.Errorf("Received error %v validating RoleMapping %v", err, rm) - } - - invalidRoleMapping := RoleMapping{ - RoleARN: "", - SSO: nil, - } - err = invalidRoleMapping.Validate() - if err == nil { - t.Errorf("Invalid RoleMapping %v did not raise error when validated", invalidRoleMapping) - } -} - -func TestUserARNMapping(t *testing.T) { - um := UserMapping{ - UserARN: "arn:aws:iam::012345678912:user/Shanice", - Username: "Shanice", - Groups: []string{"system:masters"}, - } - - expectedKey := "arn:aws:iam::012345678912:user/Shanice" - actualKey := um.Key() - - if !reflect.DeepEqual(actualKey, expectedKey) { - t.Errorf("UserMapping.Key() does not match expected value.\nActual: %v\nExpected: %v", actualKey, expectedKey) - } - - expectedMatch := "arn:aws:iam::012345678912:user/shanice" - matches := um.Matches(expectedMatch) - if !matches { - t.Errorf("UserMapping %v did not match %s", um, expectedMatch) - } - - unexpectedMatch := "arn:aws:iam::012345678912:user/notShanice" - matches = um.Matches(unexpectedMatch) - if matches { - t.Errorf("UserMapping %v unexpectedly matched %s", um, unexpectedMatch) - } - - err := um.Validate() - if err != nil { - t.Errorf("Received error %v validating UserMapping %v", err, um) - } - - invalidUserMapping := UserMapping{ - UserARN: "", - } - err = invalidUserMapping.Validate() - if err == nil { - t.Errorf("Invalid UserMapping %v did not raise error when validated", invalidUserMapping) - } -} diff --git a/pkg/config/types.go b/pkg/config/types.go index dbba927c9..7c7fac4fd 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -44,55 +44,28 @@ type IdentityMapping struct { // You can use plain values without parameters to have a more static mapping. type RoleMapping struct { // RoleARN is the AWS Resource Name of the role. (e.g., "arn:aws:iam::000000000000:role/Foo"). - RoleARN string `json:"rolearn,omitempty" yaml:"rolearn,omitempty"` - - // SSO contains fields used to match Role ARNs that - // are generated for AWS SSO sessions. - SSO *SSOARNMatcher `json:"sso,omitempty" yaml:"sso,omitempty"` + RoleARN string `json:"rolearn"` // Username is the username pattern that this instances assuming this // role will have in Kubernetes. - Username string `json:"username" yaml:"username"` + Username string `json:"username"` // Groups is a list of Kubernetes groups this role will authenticate // as (e.g., `system:masters`). Each group name can include placeholders. - Groups []string `json:"groups" yaml:"groups"` + Groups []string `json:"groups"` } // UserMapping is a static mapping of a single AWS User ARN to a // Kubernetes username and a list of Kubernetes groups type UserMapping struct { // UserARN is the AWS Resource Name of the user. (e.g., "arn:aws:iam::000000000000:user/Test"). - UserARN string `json:"userarn" yaml:"userarn"` + UserARN string `json:"userarn"` // Username is the Kubernetes username this role will authenticate as (e.g., `mycorp:foo`) - Username string `json:"username" yaml:"username"` + Username string `json:"username"` // Groups is a list of Kubernetes groups this role will authenticate as (e.g., `system:masters`) - Groups []string `json:"groups" yaml:"groups"` -} - -// SSOARNMatcher contains fields used to match Role ARNs that -// are generated for AWS SSO sessions. These SSO Role ARNs -// follow this pattern: -// -// arn:aws:iam:::role/aws-reserved/sso.amazonaws.com//AWSReservedSSO__ -// -// These ARNs are canonicalized to look like: -// -// arn:aws:iam:::role/AWSReservedSSO__ -// -// This struct enables aws-iam-authenticator to match SSO generated Role ARNs with -// handling for their random string suffixes. -type SSOARNMatcher struct { - // PermissionSetName is the name of the SSO Permission Set that will be found - // after the "AWSReservedSSO_" string in the Role ARN. - // See: https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsets.html - PermissionSetName string `json:"permissionSetName" yaml:"permissionSetName"` - // AccountID is the AWS Account ID to match in the Role ARN - AccountID string `json:"accountID" yaml:"accountID"` - // Partition is the AWS partition to match in the Role ARN. Defaults to "aws" - Partition string `json:"partition,omitempty" yaml:"partition,omitempty"` + Groups []string `json:"groups"` } // Config specifies the configuration for a aws-iam-authenticator server diff --git a/pkg/mapper/configmap/client/client.go b/pkg/mapper/configmap/client/client.go index 8ecb851e4..7cf1a90da 100644 --- a/pkg/mapper/configmap/client/client.go +++ b/pkg/mapper/configmap/client/client.go @@ -78,27 +78,18 @@ func (cli *client) add(role *config.RoleMapping, user *config.UserMapping) (cm * } if role != nil { - err = role.Validate() - if err != nil { - return fmt.Errorf("role is invalid: %v", err) - } - for _, r := range roleMappings { - if r.Key() == role.Key() { - return fmt.Errorf("cannot add duplicate role ARN %q", role.Key()) + if r.RoleARN == role.RoleARN { + return fmt.Errorf("cannot add duplicate role ARN %q", role.RoleARN) } } roleMappings = append(roleMappings, *role) } if user != nil { - err = user.Validate() - if err != nil { - return fmt.Errorf("user is invalid: %v", err) - } for _, r := range userMappings { - if r.Key() == user.Key() { - return fmt.Errorf("cannot add duplicate user ARN %q", user.Key()) + if r.UserARN == user.UserARN { + return fmt.Errorf("cannot add duplicate user ARN %q", user.UserARN) } } userMappings = append(userMappings, *user) diff --git a/pkg/mapper/configmap/client/client_test.go b/pkg/mapper/configmap/client/client_test.go index b633e4000..09e35cba3 100644 --- a/pkg/mapper/configmap/client/client_test.go +++ b/pkg/mapper/configmap/client/client_test.go @@ -62,41 +62,6 @@ func TestAddRole(t *testing.T) { if _, err := cli.AddUser(&config.UserMapping{UserARN: "a"}); err == nil || !strings.Contains(err.Error(), `cannot add duplicate user ARN`) { t.Fatal(err) } - - cli = makeTestClient(t, - nil, - nil, - nil, - ) - newSSORole := config.RoleMapping{ - RoleARN: "", - SSO: &config.SSOARNMatcher{ - PermissionSetName: "ViewOnlyAccess", - AccountID: "012345678912", - }, - Username: "b", - Groups: []string{"b"}} - cm, err = cli.AddRole(&newSSORole) - if err != nil { - t.Fatal(err) - } - _, srm, _, err := configmap.ParseMap(cm.Data) - if err != nil { - t.Fatal(err) - } - updatedRole = srm[0] - if !reflect.DeepEqual(newSSORole, updatedRole) { - t.Fatalf("unexpected updated role %+v", updatedRole) - } - - cli = makeTestClient(t, - nil, - []config.RoleMapping{newSSORole}, - nil, - ) - if _, err := cli.AddRole(&newSSORole); err == nil || !strings.Contains(err.Error(), `cannot add duplicate role ARN`) { - t.Fatal(err) - } } func makeTestClient( diff --git a/pkg/mapper/configmap/configmap.go b/pkg/mapper/configmap/configmap.go index 350cab0c6..d77790057 100644 --- a/pkg/mapper/configmap/configmap.go +++ b/pkg/mapper/configmap/configmap.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" "sync" "time" @@ -112,49 +113,29 @@ func (err ErrParsingMap) Error() string { func ParseMap(m map[string]string) (userMappings []config.UserMapping, roleMappings []config.RoleMapping, awsAccounts []string, err error) { errs := make([]error, 0) - rawUserMappings := make([]config.UserMapping, 0) userMappings = make([]config.UserMapping, 0) if userData, ok := m["mapUsers"]; ok { userJson, err := utilyaml.ToJSON([]byte(userData)) if err != nil { errs = append(errs, err) } else { - err = json.Unmarshal(userJson, &rawUserMappings) + err = json.Unmarshal(userJson, &userMappings) if err != nil { errs = append(errs, err) } - - for _, userMapping := range rawUserMappings { - err = userMapping.Validate() - if err != nil { - errs = append(errs, err) - } else { - userMappings = append(userMappings, userMapping) - } - } } } - rawRoleMappings := make([]config.RoleMapping, 0) roleMappings = make([]config.RoleMapping, 0) if roleData, ok := m["mapRoles"]; ok { roleJson, err := utilyaml.ToJSON([]byte(roleData)) if err != nil { errs = append(errs, err) } else { - err = json.Unmarshal(roleJson, &rawRoleMappings) + err = json.Unmarshal(roleJson, &roleMappings) if err != nil { errs = append(errs, err) } - - for _, roleMapping := range rawRoleMappings { - err = roleMapping.Validate() - if err != nil { - errs = append(errs, err) - } else { - roleMappings = append(roleMappings, roleMapping) - } - } } } @@ -203,11 +184,7 @@ func EncodeMap(userMappings []config.UserMapping, roleMappings []config.RoleMapp return m, nil } -func (ms *MapStore) saveMap( - userMappings []config.UserMapping, - roleMappings []config.RoleMapping, - awsAccounts []string) { - +func (ms *MapStore) saveMap(userMappings []config.UserMapping, roleMappings []config.RoleMapping, awsAccounts []string) { ms.mutex.Lock() defer ms.mutex.Unlock() ms.users = make(map[string]config.UserMapping) @@ -215,10 +192,10 @@ func (ms *MapStore) saveMap( ms.awsAccounts = make(map[string]interface{}) for _, user := range userMappings { - ms.users[user.Key()] = user + ms.users[strings.ToLower(user.UserARN)] = user } for _, role := range roleMappings { - ms.roles[role.Key()] = role + ms.roles[strings.ToLower(role.RoleARN)] = role } for _, awsAccount := range awsAccounts { ms.awsAccounts[awsAccount] = nil @@ -234,23 +211,21 @@ var RoleNotFound = errors.New("Role not found in configmap") func (ms *MapStore) UserMapping(arn string) (config.UserMapping, error) { ms.mutex.RLock() defer ms.mutex.RUnlock() - for _, user := range ms.users { - if user.Matches(arn) { - return user, nil - } + if user, ok := ms.users[arn]; !ok { + return config.UserMapping{}, UserNotFound + } else { + return user, nil } - return config.UserMapping{}, UserNotFound } func (ms *MapStore) RoleMapping(arn string) (config.RoleMapping, error) { ms.mutex.RLock() defer ms.mutex.RUnlock() - for _, role := range ms.roles { - if role.Matches(arn) { - return role, nil - } + if role, ok := ms.roles[arn]; !ok { + return config.RoleMapping{}, RoleNotFound + } else { + return role, nil } - return config.RoleMapping{}, RoleNotFound } func (ms *MapStore) AWSAccount(id string) bool { diff --git a/pkg/mapper/configmap/configmap_test.go b/pkg/mapper/configmap/configmap_test.go index ff6bb64fd..6ae3dac10 100644 --- a/pkg/mapper/configmap/configmap_test.go +++ b/pkg/mapper/configmap/configmap_test.go @@ -15,22 +15,8 @@ import ( "sigs.k8s.io/aws-iam-authenticator/pkg/config" ) -func init() { - config.SSORoleMatchEnabled = true -} - -var ( - testUser = config.UserMapping{UserARN: "arn:aws:iam::012345678912:user/matt", Username: "matlan", Groups: []string{"system:master", "dev"}} - testRole = config.RoleMapping{RoleARN: "arn:aws:iam::012345678912:role/computer", Username: "computer", Groups: []string{"system:nodes"}} - testSSORole = config.RoleMapping{ - SSO: &config.SSOARNMatcher{ - PermissionSetName: "ViewOnlyAccess", - AccountID: "012345678912", - }, - Username: "television", - Groups: []string{"system:nodes"}, - } -) +var testUser = config.UserMapping{Username: "matlan", Groups: []string{"system:master", "dev"}} +var testRole = config.RoleMapping{Username: "computer", Groups: []string{"system:nodes"}} func makeStore() MapStore { ms := MapStore{ @@ -38,9 +24,8 @@ func makeStore() MapStore { roles: make(map[string]config.RoleMapping), awsAccounts: make(map[string]interface{}), } - ms.users["arn:aws:iam::012345678912:user/matt"] = testUser - ms.roles["arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_*"] = testSSORole - ms.roles["arn:aws:iam::012345678912:role/comp*"] = testRole + ms.users["matt"] = testUser + ms.roles["instance"] = testRole ms.awsAccounts["123"] = nil return ms } @@ -59,7 +44,7 @@ func makeStoreWClient() (MapStore, *fake.FakeConfigMaps) { func TestUserMapping(t *testing.T) { ms := makeStore() - user, err := ms.UserMapping("arn:aws:iam::012345678912:user/matt") + user, err := ms.UserMapping("matt") if err != nil { t.Errorf("Could not find user 'matt' in map") } @@ -78,7 +63,7 @@ func TestUserMapping(t *testing.T) { func TestRoleMapping(t *testing.T) { ms := makeStore() - role, err := ms.RoleMapping("arn:aws:iam::012345678912:role/computer") + role, err := ms.RoleMapping("instance") if err != nil { t.Errorf("Could not find user 'instance in map") } @@ -95,17 +80,6 @@ func TestRoleMapping(t *testing.T) { } } -func TestSSORoleMapping(t *testing.T) { - ms := makeStore() - role, err := ms.RoleMapping("arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_123123123") - if err != nil { - t.Errorf("Could not find a match for role arn 'arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_123123123' in map") - } - if !reflect.DeepEqual(role, testSSORole) { - t.Errorf("Role arn 'arn:aws:iam::012345678912:role/awsreservedsso_viewonlyaccess_123123123' does not match expected value. (Acutal: %+v, Expected: %+v", role, testSSORole) - } -} - func TestAWSAccount(t *testing.T) { ms := makeStore() if !ms.AWSAccount("123") { @@ -126,7 +100,7 @@ var userMapping = ` - groups: - "system:master" - userarn: "arn:aws:iam::012345678912:user/NIC" + userarn: "arn:iam:NIC" username: nic ` @@ -142,7 +116,7 @@ var updatedUserMapping = ` groups: - "system:master" - "test" - userarn: "arn:aws:iam::012345678912:user/NIC" + userarn: "arn:iam:NIC" username: nic - userarn: "arn:iam:beswar" username: beswar @@ -186,7 +160,7 @@ func TestLoadConfigMap(t *testing.T) { ms.startLoadConfigMap(stopCh) defer close(stopCh) - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Millisecond) meta := metav1.ObjectMeta{Name: "aws-auth"} data := make(map[string]string) @@ -196,7 +170,7 @@ func TestLoadConfigMap(t *testing.T) { watcher.Add(&core_v1.ConfigMap{ObjectMeta: meta, Data: data}) - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Millisecond) if !ms.AWSAccount("123") { t.Errorf("AWS Account '123' not in allowed accounts") @@ -207,12 +181,12 @@ func TestLoadConfigMap(t *testing.T) { } expectedUser := config.UserMapping{ - UserARN: "arn:aws:iam::012345678912:user/NIC", + UserARN: "arn:iam:NIC", Username: "nic", Groups: []string{"system:master"}, } - user, err := ms.UserMapping("arn:aws:iam::012345678912:user/NIC") + user, err := ms.UserMapping("arn:iam:nic") if err != nil { t.Errorf("Expected to find user 'nic' but got error: %v", err) } @@ -238,7 +212,7 @@ func TestLoadConfigMap(t *testing.T) { } expectedUser.Groups = append(expectedUser.Groups, "test") - user, err = ms.UserMapping("arn:aws:iam::012345678912:user/NIC") + user, err = ms.UserMapping("arn:iam:nic") if !reflect.DeepEqual(user, expectedUser) { t.Errorf("Updated returned from mapping does not match expected user. (Actual: %+v, Expected: %+v", user, expectedUser) } @@ -268,13 +242,6 @@ func TestParseMap(t *testing.T) { groups: - system:bootstrappers - system:nodes -- sso: - permissionSetName: ViewOnlyAccess - accountID: "012345678912" - partition: aws-cn - username: user1 - groups: - - system:basic-users `, "mapUsers": `- userarn: arn:aws:iam::123456789101:user/Hello username: Hello @@ -291,20 +258,7 @@ func TestParseMap(t *testing.T) { {UserARN: "arn:aws:iam::123456789101:user/World", Username: "World", Groups: []string{"system:masters"}}, } roleMappings := []config.RoleMapping{ - { - RoleARN: "arn:aws:iam::123456789101:role/test-NodeInstanceRole-1VWRHZ3GKZ1T4", - Username: "system:node:{{EC2PrivateDNSName}}", - Groups: []string{"system:bootstrappers", "system:nodes"}, - }, - { - SSO: &config.SSOARNMatcher{ - PermissionSetName: "ViewOnlyAccess", - AccountID: "012345678912", - Partition: "aws-cn", - }, - Username: "user1", - Groups: []string{"system:basic-users"}, - }, + {RoleARN: "arn:aws:iam::123456789101:role/test-NodeInstanceRole-1VWRHZ3GKZ1T4", Username: "system:node:{{EC2PrivateDNSName}}", Groups: []string{"system:bootstrappers", "system:nodes"}}, } accounts := []string{} diff --git a/pkg/mapper/file/mapper.go b/pkg/mapper/file/mapper.go index f62269023..e38c2eb30 100644 --- a/pkg/mapper/file/mapper.go +++ b/pkg/mapper/file/mapper.go @@ -2,6 +2,7 @@ package file import ( "fmt" + "strings" "sigs.k8s.io/aws-iam-authenticator/pkg/arn" "sigs.k8s.io/aws-iam-authenticator/pkg/config" @@ -9,43 +10,33 @@ import ( ) type FileMapper struct { - roleMap map[string]config.RoleMapping - userMap map[string]config.UserMapping - accountMap map[string]bool + lowercaseRoleMap map[string]config.RoleMapping + lowercaseUserMap map[string]config.UserMapping + accountMap map[string]bool } var _ mapper.Mapper = &FileMapper{} func NewFileMapper(cfg config.Config) (*FileMapper, error) { fileMapper := &FileMapper{ - roleMap: make(map[string]config.RoleMapping), - userMap: make(map[string]config.UserMapping), - accountMap: make(map[string]bool), + lowercaseRoleMap: make(map[string]config.RoleMapping), + lowercaseUserMap: make(map[string]config.UserMapping), + accountMap: make(map[string]bool), } for _, m := range cfg.RoleMappings { - err := m.Validate() + canonicalizedARN, err := arn.Canonicalize(strings.ToLower(m.RoleARN)) if err != nil { - return nil, err + return nil, fmt.Errorf("error canonicalizing ARN: %v", err) } - fileMapper.roleMap[m.Key()] = m + fileMapper.lowercaseRoleMap[canonicalizedARN] = m } for _, m := range cfg.UserMappings { - err := m.Validate() + canonicalizedARN, err := arn.Canonicalize(strings.ToLower(m.UserARN)) if err != nil { - return nil, err + return nil, fmt.Errorf("error canonicalizing ARN: %v", err) } - var key string - if m.UserARN != "" { - canonicalizedARN, err := arn.Canonicalize(m.UserARN) - if err != nil { - return nil, fmt.Errorf("error canonicalizing ARN: %v", err) - } - key = canonicalizedARN - } else { - key = m.Key() - } - fileMapper.userMap[key] = m + fileMapper.lowercaseUserMap[canonicalizedARN] = m } for _, m := range cfg.AutoMappedAWSAccounts { fileMapper.accountMap[m] = true @@ -59,9 +50,9 @@ func NewFileMapperWithMaps( lowercaseUserMap map[string]config.UserMapping, accountMap map[string]bool) *FileMapper { return &FileMapper{ - roleMap: lowercaseRoleMap, - userMap: lowercaseUserMap, - accountMap: accountMap, + lowercaseRoleMap: lowercaseRoleMap, + lowercaseUserMap: lowercaseUserMap, + accountMap: accountMap, } } @@ -74,24 +65,22 @@ func (m *FileMapper) Start(_ <-chan struct{}) error { } func (m *FileMapper) Map(canonicalARN string) (*config.IdentityMapping, error) { - for _, roleMapping := range m.roleMap { - if roleMapping.Matches(canonicalARN) { - return &config.IdentityMapping{ - IdentityARN: canonicalARN, - Username: roleMapping.Username, - Groups: roleMapping.Groups, - }, nil - } + canonicalARN = strings.ToLower(canonicalARN) + + if roleMapping, exists := m.lowercaseRoleMap[canonicalARN]; exists { + return &config.IdentityMapping{ + IdentityARN: canonicalARN, + Username: roleMapping.Username, + Groups: roleMapping.Groups, + }, nil } - for _, userMapping := range m.userMap { - if userMapping.Matches(canonicalARN) { - return &config.IdentityMapping{ - IdentityARN: canonicalARN, - Username: userMapping.Username, - Groups: userMapping.Groups, - }, nil - } + if userMapping, exists := m.lowercaseUserMap[canonicalARN]; exists { + return &config.IdentityMapping{ + IdentityARN: canonicalARN, + Username: userMapping.Username, + Groups: userMapping.Groups, + }, nil } return nil, mapper.ErrNotMapped diff --git a/pkg/mapper/file/mapper_test.go b/pkg/mapper/file/mapper_test.go deleted file mode 100644 index 5120d1fa1..000000000 --- a/pkg/mapper/file/mapper_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package file - -import ( - "reflect" - "testing" - - "sigs.k8s.io/aws-iam-authenticator/pkg/config" -) - -func init() { - config.SSORoleMatchEnabled = true -} - -func newConfig() config.Config { - return config.Config{ - RoleMappings: []config.RoleMapping{ - { - RoleARN: "arn:aws:iam::012345678910:role/test-role", - Username: "shreyas", - Groups: []string{"system:masters"}, - }, - { - SSO: &config.SSOARNMatcher{ - PermissionSetName: "CookieCutterPermissions", - AccountID: "012345678910", - }, - Username: "cookie-cutter", - Groups: []string{"system:masters"}, - }, - }, - UserMappings: []config.UserMapping{ - { - UserARN: "arn:aws:iam::012345678910:user/donald", - Username: "donald", - Groups: []string{"system:masters"}, - }, - }, - AutoMappedAWSAccounts: []string{"000000000000"}, - } -} - -func TestNewFileMapper(t *testing.T) { - cfg := newConfig() - - expected := &FileMapper{ - roleMap: map[string]config.RoleMapping{ - "arn:aws:iam::012345678910:role/test-role": { - RoleARN: "arn:aws:iam::012345678910:role/test-role", - Username: "shreyas", - Groups: []string{"system:masters"}, - }, - "arn:aws:iam::012345678910:role/awsreservedsso_cookiecutterpermissions_*": { - SSO: &config.SSOARNMatcher{ - PermissionSetName: "CookieCutterPermissions", - AccountID: "012345678910", - }, - Username: "cookie-cutter", - Groups: []string{"system:masters"}, - }, - }, - userMap: map[string]config.UserMapping{ - "arn:aws:iam::012345678910:user/donald": { - UserARN: "arn:aws:iam::012345678910:user/donald", - Username: "donald", - Groups: []string{"system:masters"}, - }, - }, - accountMap: map[string]bool{ - "000000000000": true, - }, - } - - actual, err := NewFileMapper(cfg) - if err != nil { - t.Errorf("Could not build FileMapper from test config: %v", err) - } - - if !reflect.DeepEqual(actual, expected) { - t.Errorf("FileMapper does not match expected value.\nActual: %v\nExpected: %v", actual, expected) - } -} - -func TestMap(t *testing.T) { - fm, err := NewFileMapper(newConfig()) - if err != nil { - t.Errorf("Could not build FileMapper from test config: %v", err) - } - - identityArn := "arn:aws:iam::012345678910:role/test-role" - expected := &config.IdentityMapping{ - IdentityARN: identityArn, - Username: "shreyas", - Groups: []string{"system:masters"}, - } - actual, err := fm.Map(identityArn) - if err != nil { - t.Errorf("Could not map %s: %s", identityArn, err) - } - if !reflect.DeepEqual(actual, expected) { - t.Errorf("FileMapper.Map() does not match expected value for roleMapping:\nActual: %v\nExpected: %v", actual, expected) - } - - identityArn = "arn:aws:iam::012345678910:role/awsreservedsso_cookiecutterpermissions_123123123" - expected = &config.IdentityMapping{ - IdentityARN: identityArn, - Username: "cookie-cutter", - Groups: []string{"system:masters"}, - } - actual, err = fm.Map(identityArn) - if err != nil { - t.Errorf("Could not map %s: %s", identityArn, err) - } - if !reflect.DeepEqual(actual, expected) { - t.Errorf("FileMapper.Map() does not match expected value for roleArnLikeMapping:\nActual: %v\nExpected: %v", actual, expected) - } - - identityArn = "arn:aws:iam::012345678910:user/donald" - expected = &config.IdentityMapping{ - IdentityARN: identityArn, - Username: "donald", - Groups: []string{"system:masters"}, - } - actual, err = fm.Map(identityArn) - if err != nil { - t.Errorf("Could not map %s: %s", identityArn, err) - } - if !reflect.DeepEqual(actual, expected) { - t.Errorf("FileMapper.Map() does not match expected value for userMapping:\nActual: %v\nExpected: %v", actual, expected) - } -} diff --git a/pkg/server/server.go b/pkg/server/server.go index 29b3c664b..05daf62c0 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -90,19 +90,11 @@ func New(cfg config.Config, stopCh <-chan struct{}) *Server { } for _, mapping := range c.RoleMappings { - if mapping.RoleARN != "" { - logrus.WithFields(logrus.Fields{ - "role": mapping.RoleARN, - "username": mapping.Username, - "groups": mapping.Groups, - }).Infof("mapping IAM role") - } else if mapping.SSO != nil { - logrus.WithFields(logrus.Fields{ - "sso": *mapping.SSO, - "username": mapping.Username, - "groups": mapping.Groups, - }).Infof("mapping IAM role") - } + logrus.WithFields(logrus.Fields{ + "role": mapping.RoleARN, + "username": mapping.Username, + "groups": mapping.Groups, + }).Infof("mapping IAM role") } for _, mapping := range c.UserMappings { logrus.WithFields(logrus.Fields{