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

fix: validate JWT token on alias look ahead #114

Merged
merged 2 commits into from
Sep 29, 2021
Merged
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
12 changes: 12 additions & 0 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kubeauth
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"sync"
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 6 additions & 9 deletions path_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
Expand Down
6 changes: 6 additions & 0 deletions path_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down