Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions pkg/config/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,23 @@ func (m *RoleMapping) Validate() error {
return nil
}

// hasWildcards returns true if the string contains ArnLike wildcard characters
func hasWildcards(s string) bool {
return strings.ContainsAny(s, "*?")
}

// Matches returns true if the supplied ARN or SSO settings matches
// this RoleMapping
func (m *RoleMapping) Matches(subject string) bool {
if m.RoleARN != "" {
if hasWildcards(m.RoleARN) {
ok, err := arn.ArnLike(subject, strings.ToLower(m.RoleARN))
if err != nil {
logrus.Error("Could not parse subject ARN for wildcard match: ", err)
return false
}
return ok
}
return strings.EqualFold(m.RoleARN, subject)
}

Expand Down
50 changes: 50 additions & 0 deletions pkg/config/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,56 @@ func TestRoleARNMapping(t *testing.T) {
}
}

func TestWildcardRoleARNMapping(t *testing.T) {
rm := RoleMapping{
RoleARN: "arn:aws:iam::012345678912:role/*",
Username: "wildcard-admin",
Groups: []string{"system:masters"},
}

// Should match any role in the account
matches := rm.Matches("arn:aws:iam::012345678912:role/anyrole")
if !matches {
t.Errorf("Wildcard RoleMapping did not match role in same account")
}

// Should match roles with paths
matches = rm.Matches("arn:aws:iam::012345678912:role/path/to/role")
if !matches {
t.Errorf("Wildcard RoleMapping did not match role with path")
}

// Should NOT match a different account
matches = rm.Matches("arn:aws:iam::999999999999:role/anyrole")
if matches {
t.Errorf("Wildcard RoleMapping unexpectedly matched different account")
}

// Should NOT match a user ARN
matches = rm.Matches("arn:aws:iam::012345678912:user/someuser")
if matches {
t.Errorf("Wildcard RoleMapping unexpectedly matched user ARN")
}
}

func TestWildcardRoleARNPrefixMapping(t *testing.T) {
rm := RoleMapping{
RoleARN: "arn:aws:iam::012345678912:role/dev-*",
Username: "dev-{{SessionName}}",
Groups: []string{"developers"},
}

matches := rm.Matches("arn:aws:iam::012345678912:role/dev-team-lead")
if !matches {
t.Errorf("Prefix wildcard RoleMapping did not match dev-team-lead")
}

matches = rm.Matches("arn:aws:iam::012345678912:role/prod-admin")
if matches {
t.Errorf("Prefix wildcard RoleMapping unexpectedly matched prod-admin")
}
}

func TestUserARNMapping(t *testing.T) {
um := UserMapping{
UserARN: "arn:aws:iam::012345678912:user/Shanice",
Expand Down
13 changes: 9 additions & 4 deletions pkg/mapper/file/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ func NewFileMapper(cfg config.Config) (*FileMapper, error) {
return nil, err
}
if m.RoleARN != "" {
_, canonicalizedARN, err := arn.Canonicalize(m.RoleARN)
if err != nil {
return nil, err
if strings.ContainsAny(m.RoleARN, "*?") {
// Wildcard ARNs cannot be canonicalized; store as lowercase
m.RoleARN = strings.ToLower(m.RoleARN)
} else {
_, canonicalizedARN, err := arn.Canonicalize(m.RoleARN)
if err != nil {
return nil, err
}
m.RoleARN = canonicalizedARN
}
m.RoleARN = canonicalizedARN
}
fileMapper.roleMap[m.Key()] = m
}
Expand Down
38 changes: 38 additions & 0 deletions pkg/mapper/file/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,41 @@ func TestMap(t *testing.T) {
t.Errorf("FileMapper.Map() does not match expected value for userMapping:\nActual: %v\nExpected: %v", actual, expected)
}
}

func TestWildcardRoleMap(t *testing.T) {
cfg := config.Config{
RoleMappings: []config.RoleMapping{
{
RoleARN: "arn:aws:iam::012345678910:role/dev-*",
Username: "dev-{{SessionName}}",
Groups: []string{"developers"},
},
},
}

fm, err := NewFileMapper(cfg)
if err != nil {
t.Fatalf("Could not build FileMapper with wildcard config: %v", err)
}

// Should match
identity := token.Identity{
CanonicalARN: "arn:aws:iam::012345678910:role/dev-team-lead",
}
mapping, err := fm.Map(&identity)
if err != nil {
t.Fatalf("Wildcard role did not match dev-team-lead: %v", err)
}
if mapping.Username != "dev-{{SessionName}}" {
t.Errorf("Expected username dev-{{SessionName}}, got %s", mapping.Username)
}

// Should not match
identity = token.Identity{
CanonicalARN: "arn:aws:iam::012345678910:role/prod-admin",
}
_, err = fm.Map(&identity)
if err == nil {
t.Errorf("Wildcard role unexpectedly matched prod-admin")
}
}