Skip to content

Commit

Permalink
Check ARN for user ID strict check
Browse files Browse the repository at this point in the history
  • Loading branch information
nckturner committed Dec 7, 2023
1 parent b0456d8 commit 8388d98
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 41 deletions.
19 changes: 19 additions & 0 deletions pkg/config/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -100,6 +101,15 @@ func (m *RoleMapping) Key() string {
return m.SSOArnLike()
}

// IdentityMapping converts the RoleMapping into a generic IdentityMapping object
func (m *RoleMapping) IdentityMapping(identity *token.Identity) *IdentityMapping {
return &IdentityMapping{
IdentityARN: strings.ToLower(identity.CanonicalARN),
Username: m.Username,
Groups: m.Groups,
}
}

// Validate returns an error if the UserMapping is not valid after being unmarshaled
func (m *UserMapping) Validate() error {
if m == nil {
Expand All @@ -123,3 +133,12 @@ func (m *UserMapping) Matches(subject string) bool {
func (m *UserMapping) Key() string {
return m.UserARN
}

// IdentityMapping converts the UserMapping into a generic IdentityMapping object
func (m *UserMapping) IdentityMapping(identity *token.Identity) *IdentityMapping {
return &IdentityMapping{
IdentityARN: strings.ToLower(identity.CanonicalARN),
Username: m.Username,
Groups: m.Groups,
}
}
9 changes: 9 additions & 0 deletions pkg/errutil/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package errutil

import (
"errors"
)

var ErrNotMapped = errors.New("Identity is not mapped")

var ErrIDAndARNMismatch = errors.New("ARN does not match User ID")
6 changes: 4 additions & 2 deletions pkg/mapper/configmap/mapper.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package configmap

import (
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
"strings"

"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper"
)
Expand Down Expand Up @@ -53,7 +55,7 @@ func (m *ConfigMapMapper) Map(identity *token.Identity) (*config.IdentityMapping
}, nil
}

return nil, mapper.ErrNotMapped
return nil, errutil.ErrNotMapped
}

func (m *ConfigMapMapper) IsAccountAllowed(accountID string) bool {
Expand Down
6 changes: 4 additions & 2 deletions pkg/mapper/crd/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package crd

import (
"fmt"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
"strings"
"time"

"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -114,7 +116,7 @@ func (m *CRDMapper) Map(identity *token.Identity) (*config.IdentityMapping, erro
}
}

return nil, mapper.ErrNotMapped
return nil, errutil.ErrNotMapped
}

func (m *CRDMapper) IsAccountAllowed(accountID string) bool {
Expand Down
20 changes: 7 additions & 13 deletions pkg/mapper/dynamicfile/dynamicfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package dynamicfile

import (
"encoding/json"
"errors"
"fmt"
"strings"
"sync"

"github.com/sirupsen/logrus"
"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
)

type DynamicFileMapStore struct {
Expand Down Expand Up @@ -81,27 +81,21 @@ func (ms *DynamicFileMapStore) saveMap(
}
}

// UserNotFound is the error returned when the user is not found in the config map.
var UserNotFound = errors.New("User not found in dynamic file")

// RoleNotFound is the error returned when the role is not found in the config map.
var RoleNotFound = errors.New("Role not found in dynamic file")

func (ms *DynamicFileMapStore) UserMapping(arn string) (config.UserMapping, error) {
func (ms *DynamicFileMapStore) UserMapping(key string) (config.UserMapping, error) {
ms.mutex.RLock()
defer ms.mutex.RUnlock()
if user, ok := ms.users[arn]; !ok {
return config.UserMapping{}, UserNotFound
if user, ok := ms.users[key]; !ok {
return config.UserMapping{}, errutil.ErrNotMapped
} else {
return user, nil
}
}

func (ms *DynamicFileMapStore) RoleMapping(arn string) (config.RoleMapping, error) {
func (ms *DynamicFileMapStore) RoleMapping(key string) (config.RoleMapping, error) {
ms.mutex.RLock()
defer ms.mutex.RUnlock()
if role, ok := ms.roles[arn]; !ok {
return config.RoleMapping{}, RoleNotFound
if role, ok := ms.roles[key]; !ok {
return config.RoleMapping{}, errutil.ErrNotMapped
} else {
return role, nil
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/mapper/dynamicfile/dynamicfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/fileutil"
)

Expand Down Expand Up @@ -39,7 +40,7 @@ func TestUserMapping(t *testing.T) {
}

user, err = ms.UserMapping("nic")
if err != UserNotFound {
if err != errutil.ErrNotMapped {
t.Errorf("UserNotFound error was not returned for user 'nic'")
}
if !reflect.DeepEqual(user, config.UserMapping{}) {
Expand All @@ -58,7 +59,7 @@ func TestRoleMapping(t *testing.T) {
}

role, err = ms.RoleMapping("borg")
if err != RoleNotFound {
if err != errutil.ErrNotMapped {
t.Errorf("RoleNotFound error was not returend for role 'borg'")
}
if !reflect.DeepEqual(role, config.RoleMapping{}) {
Expand Down
41 changes: 25 additions & 16 deletions pkg/mapper/dynamicfile/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"

"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/fileutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
Expand Down Expand Up @@ -37,31 +38,39 @@ func (m *DynamicFileMapper) Start(stopCh <-chan struct{}) error {

func (m *DynamicFileMapper) Map(identity *token.Identity) (*config.IdentityMapping, error) {
canonicalARN := strings.ToLower(identity.CanonicalARN)

key := canonicalARN
if m.userIDStrict {
key = identity.UserID
}

rm, err := m.RoleMapping(key)
// TODO: Check for non Role/UserNotFound errors
if err == nil {
return &config.IdentityMapping{
IdentityARN: canonicalARN,
Username: rm.Username,
Groups: rm.Groups,
}, nil
if roleMapping, err := m.RoleMapping(key); err == nil {
if err := m.match(identity, roleMapping.RoleARN, roleMapping.UserId); err != nil {
return nil, err
}
return roleMapping.IdentityMapping(identity), nil
}

um, err := m.UserMapping(key)
if err == nil {
return &config.IdentityMapping{
IdentityARN: canonicalARN,
Username: um.Username,
Groups: um.Groups,
}, nil
if userMapping, err := m.UserMapping(key); err == nil {
if err := m.match(identity, userMapping.UserARN, userMapping.UserId); err != nil {
return nil, err
}
return userMapping.IdentityMapping(identity), nil
}

return nil, mapper.ErrNotMapped
return nil, errutil.ErrNotMapped
}

func (m *DynamicFileMapper) match(token *token.Identity, mappedUserID, mappedARN string) error {
if m.userIDStrict {
// If ARN is provided, ARN must be validated along with UserID. This avoids having to
// support IAM user name/ARN changes. Without preventing this the mapping would look
// invalid but still work and auditing would be difficult/impossible.
if mappedARN != "" && token.ARN != mappedARN {
return errutil.ErrIDAndARNMismatch
}
}
return nil
}

func (m *DynamicFileMapper) IsAccountAllowed(accountID string) bool {
Expand Down
3 changes: 2 additions & 1 deletion pkg/mapper/file/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
Expand Down Expand Up @@ -102,7 +103,7 @@ func (m *FileMapper) Map(identity *token.Identity) (*config.IdentityMapping, err
Groups: userMapping.Groups,
}, nil
}
return nil, mapper.ErrNotMapped
return nil, errutil.ErrNotMapped
}

func (m *FileMapper) IsAccountAllowed(accountID string) bool {
Expand Down
4 changes: 1 addition & 3 deletions pkg/mapper/mapper.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package mapper

import (
"errors"
"fmt"

"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -34,8 +34,6 @@ var (
BackendModeChoices = []string{ModeMountedFile, ModeEKSConfigMap, ModeCRD, ModeDynamicFile}
)

var ErrNotMapped = errors.New("ARN is not mapped")

type Mapper interface {
Name() string
// Start must be non-blocking
Expand Down
5 changes: 3 additions & 2 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/ec2provider"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/fileutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper/configmap"
Expand Down Expand Up @@ -431,7 +432,7 @@ func (h *handler) doMapping(identity *token.Identity) (string, []string, error)
}
return username, groups, nil
} else {
if err != mapper.ErrNotMapped {
if err != errutil.ErrNotMapped {
errs = append(errs, fmt.Errorf("mapper %s Map error: %v", m.Name(), err))
}

Expand All @@ -444,7 +445,7 @@ func (h *handler) doMapping(identity *token.Identity) (string, []string, error)
if len(errs) > 0 {
return "", nil, utilerrors.NewAggregate(errs)
}
return "", nil, mapper.ErrNotMapped
return "", nil, errutil.ErrNotMapped
}

func (h *handler) renderTemplates(mapping config.IdentityMapping, identity *token.Identity) (string, []string, error) {
Expand Down

0 comments on commit 8388d98

Please sign in to comment.