diff --git a/backend.go b/backend.go index 3c06d7d9..775cba57 100644 --- a/backend.go +++ b/backend.go @@ -3,6 +3,7 @@ package kubeauth import ( "context" "encoding/json" + "errors" "fmt" "strings" "sync" @@ -106,6 +107,17 @@ func (b *kubeAuthBackend) config(ctx context.Context, s logical.Storage) (*kubeC return conf, nil } +func (b *kubeAuthBackend) loadConfig(ctx context.Context, s logical.Storage) (*kubeConfig, error) { + config, err := b.config(ctx, s) + if err != nil { + return nil, err + } + if config == nil { + return nil, errors.New("could not load backend configuration") + } + return config, nil +} + // role takes a storage backend and the name and returns the role's storage // entry func (b *kubeAuthBackend) role(ctx context.Context, s logical.Storage, name string) (*roleStorageEntry, error) { diff --git a/path_login.go b/path_login.go index a76aa95f..51f1fcf6 100644 --- a/path_login.go +++ b/path_login.go @@ -87,13 +87,10 @@ func (b *kubeAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d } } - config, err := b.config(ctx, req.Storage) + config, err := b.loadConfig(ctx, req.Storage) if err != nil { return nil, err } - if config == nil { - return nil, errors.New("could not load backend configuration") - } serviceAccount, err := b.parseAndValidateJWT(jwtStr, role, config) if err != nil { @@ -171,6 +168,7 @@ func (b *kubeAuthBackend) getAliasName(role *roleStorageEntry, serviceAccount *s // aliasLookahead returns the alias object with the SA UID from the JWT // Claims. +// Only JWTs matching the specified role's configuration will be accepted as valid. func (b *kubeAuthBackend) aliasLookahead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName, resp := b.getFieldValueStr(data, "role") if resp != nil { @@ -190,15 +188,14 @@ func (b *kubeAuthBackend) aliasLookahead(ctx context.Context, req *logical.Reque return logical.ErrorResponse(fmt.Sprintf("invalid role name %q", roleName)), nil } - // Parse into JWT - parsedJWT, err := jws.ParseJWT([]byte(jwtStr)) + config, err := b.loadConfig(ctx, req.Storage) if err != nil { return nil, err } - // Decode claims into a service account object - sa := &serviceAccount{} - err = mapstructure.Decode(parsedJWT.Claims(), sa) + // validation of the JWT against the provided role ensures alias look ahead requests + // are authentic. + sa, err := b.parseAndValidateJWT(jwtStr, role, config) if err != nil { return nil, err } diff --git a/path_login_test.go b/path_login_test.go index d5fe753c..5d7ac0b8 100644 --- a/path_login_test.go +++ b/path_login_test.go @@ -610,6 +610,12 @@ func TestAliasLookAhead(t *testing.T) { config: defaultTestBackendConfig(), wantErr: errors.New("missing jwt"), }, + "invalid_jwt": { + role: "plugin-test", + config: defaultTestBackendConfig(), + jwt: jwtBadServiceAccount, + wantErr: errors.New("service account name not authorized"), + }, "serviceaccount_uid": { role: "plugin-test", jwt: jwtData,