From 747d49150b023082987fe7a27ddc9a30745e085e Mon Sep 17 00:00:00 2001 From: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> Date: Mon, 14 Dec 2020 10:07:07 -0800 Subject: [PATCH] Updates the OIDC/JWT auth plugin (#10546) --- changelog/10546.txt | 3 ++ go.mod | 2 +- go.sum | 4 +- .../vault-plugin-auth-jwt/path_config.go | 2 +- .../vault-plugin-auth-jwt/path_login.go | 43 ++++++++++--------- .../vault-plugin-auth-jwt/path_oidc.go | 10 ++--- .../vault-plugin-auth-jwt/provider_azure.go | 4 +- .../vault-plugin-auth-jwt/provider_config.go | 11 ++--- .../vault-plugin-auth-jwt/provider_gsuite.go | 33 ++++++-------- vendor/modules.txt | 2 +- 10 files changed, 58 insertions(+), 56 deletions(-) create mode 100644 changelog/10546.txt diff --git a/changelog/10546.txt b/changelog/10546.txt new file mode 100644 index 000000000..3cf83ff80 --- /dev/null +++ b/changelog/10546.txt @@ -0,0 +1,3 @@ +```release-note:bug +auth/jwt: Fixes `bound_claims` validation for provider-specific group and user info fetching. +``` diff --git a/go.mod b/go.mod index a6092a987..372ef2570 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( github.com/hashicorp/vault-plugin-auth-centrify v0.7.0 github.com/hashicorp/vault-plugin-auth-cf v0.7.0 github.com/hashicorp/vault-plugin-auth-gcp v0.8.0 - github.com/hashicorp/vault-plugin-auth-jwt v0.8.0 + github.com/hashicorp/vault-plugin-auth-jwt v0.7.2-0.20201203001230-e35700fcc0d5 github.com/hashicorp/vault-plugin-auth-kerberos v0.2.0 github.com/hashicorp/vault-plugin-auth-kubernetes v0.8.0 github.com/hashicorp/vault-plugin-auth-oci v0.6.0 diff --git a/go.sum b/go.sum index f865a6a90..4d9b074ac 100644 --- a/go.sum +++ b/go.sum @@ -634,8 +634,8 @@ github.com/hashicorp/vault-plugin-auth-cf v0.7.0/go.mod h1:exPUMj8yNohKM7yRiHa7O github.com/hashicorp/vault-plugin-auth-gcp v0.5.1/go.mod h1:eLj92eX8MPI4vY1jaazVLF2sVbSAJ3LRHLRhF/pUmlI= github.com/hashicorp/vault-plugin-auth-gcp v0.8.0 h1:E9EHvC9jCDNix/pB9NKYYLMUkpfv65TSDk2rVvtkdzU= github.com/hashicorp/vault-plugin-auth-gcp v0.8.0/go.mod h1:sHDguHmyGScoalGLEjuxvDCrMPVlw2c3f+ieeiHcv6w= -github.com/hashicorp/vault-plugin-auth-jwt v0.8.0 h1:DvwV2RPdZ4q6eTXJ1APIGsEdqzkkZXls1d46e45QSrk= -github.com/hashicorp/vault-plugin-auth-jwt v0.8.0/go.mod h1:pyR4z5f2Vuz9TXucuN0rivUJTtSdlOtDdZ16IqBjZVo= +github.com/hashicorp/vault-plugin-auth-jwt v0.7.2-0.20201203001230-e35700fcc0d5 h1:BEsc9LNqgCNMhRVVOzS2v1Czioqod5Lln+Zol7zFmak= +github.com/hashicorp/vault-plugin-auth-jwt v0.7.2-0.20201203001230-e35700fcc0d5/go.mod h1:pyR4z5f2Vuz9TXucuN0rivUJTtSdlOtDdZ16IqBjZVo= github.com/hashicorp/vault-plugin-auth-kerberos v0.2.0 h1:7ct50ngVFTeO7EJ3N9PvPHeHc+2cANTHi2+9RwIUIHM= github.com/hashicorp/vault-plugin-auth-kerberos v0.2.0/go.mod h1:IM/n7LY1rIM4MVzOfSH6cRmY/C2rGkrjGrEr0B/yO9c= github.com/hashicorp/vault-plugin-auth-kubernetes v0.8.0 h1:v1jOqR70chxRxONey7g/v0/57MneP05z2dfw6qmlE+8= diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go index 0b3c2682c..75bf76f58 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go @@ -303,7 +303,7 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque } // Validate provider_config - if _, err := NewProviderConfig(config, ProviderMap()); err != nil { + if _, err := NewProviderConfig(ctx, config, ProviderMap()); err != nil { return logical.ErrorResponse("invalid provider_config: %s", err), nil } diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go index 7541dfcd5..0bb84352a 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go @@ -213,15 +213,15 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d return nil, errors.New("unhandled case during login") } - if err := validateBoundClaims(b.Logger(), role.BoundClaimsType, role.BoundClaims, allClaims); err != nil { - return logical.ErrorResponse("error validating claims: %s", err.Error()), nil - } - - alias, groupAliases, err := b.createIdentity(allClaims, role) + alias, groupAliases, err := b.createIdentity(ctx, allClaims, role) if err != nil { return logical.ErrorResponse(err.Error()), nil } + if err := validateBoundClaims(b.Logger(), role.BoundClaimsType, role.BoundClaims, allClaims); err != nil { + return logical.ErrorResponse("error validating claims: %s", err.Error()), nil + } + tokenMetadata := map[string]string{"role": roleName} for k, v := range alias.Metadata { tokenMetadata[k] = v @@ -308,7 +308,7 @@ func (b *jwtAuthBackend) verifyOIDCToken(ctx context.Context, config *jwtConfig, // createIdentity creates an alias and set of groups aliases based on the role // definition and received claims. -func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role *jwtRole) (*logical.Alias, []*logical.Alias, error) { +func (b *jwtAuthBackend) createIdentity(ctx context.Context, allClaims map[string]interface{}, role *jwtRole) (*logical.Alias, []*logical.Alias, error) { userClaimRaw, ok := allClaims[role.UserClaim] if !ok { return nil, nil, fmt.Errorf("claim %q not found in token", role.UserClaim) @@ -318,8 +318,12 @@ func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role * return nil, nil, fmt.Errorf("claim %q could not be converted to string", role.UserClaim) } - err := b.fetchUserInfo(allClaims, role) + pConfig, err := NewProviderConfig(ctx, b.cachedConfig, ProviderMap()) if err != nil { + return nil, nil, fmt.Errorf("failed to load custom provider config: %s", err) + } + + if err := b.fetchUserInfo(ctx, pConfig, allClaims, role); err != nil { return nil, nil, err } @@ -339,7 +343,7 @@ func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role * return alias, groupAliases, nil } - groupsClaimRaw, err := b.fetchGroups(allClaims, role) + groupsClaimRaw, err := b.fetchGroups(ctx, pConfig, allClaims, role) if err != nil { return nil, nil, fmt.Errorf("failed to fetch groups: %s", err) } @@ -366,15 +370,11 @@ func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role * } // Checks if there's a custom provider_config and calls FetchUserInfo() if implemented. -func (b *jwtAuthBackend) fetchUserInfo(allClaims map[string]interface{}, role *jwtRole) error { - pConfig, err := NewProviderConfig(b.cachedConfig, ProviderMap()) - if err != nil { - return fmt.Errorf("failed to load custom provider config: %s", err) - } +func (b *jwtAuthBackend) fetchUserInfo(ctx context.Context, pConfig CustomProvider, allClaims map[string]interface{}, role *jwtRole) error { // Fetch user info from custom provider if it's implemented if pConfig != nil { if uif, ok := pConfig.(UserInfoFetcher); ok { - return uif.FetchUserInfo(b, allClaims, role) + return uif.FetchUserInfo(ctx, b, allClaims, role) } } @@ -382,16 +382,19 @@ func (b *jwtAuthBackend) fetchUserInfo(allClaims map[string]interface{}, role *j } // Checks if there's a custom provider_config and calls FetchGroups() if implemented -func (b *jwtAuthBackend) fetchGroups(allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { - pConfig, err := NewProviderConfig(b.cachedConfig, ProviderMap()) - if err != nil { - return nil, fmt.Errorf("failed to load custom provider config: %s", err) - } +func (b *jwtAuthBackend) fetchGroups(ctx context.Context, pConfig CustomProvider, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { // If the custom provider implements interface GroupsFetcher, call it, // otherwise fall through to the default method if pConfig != nil { if gf, ok := pConfig.(GroupsFetcher); ok { - return gf.FetchGroups(b, allClaims, role) + groupsRaw, err := gf.FetchGroups(ctx, b, allClaims, role) + if err != nil { + return nil, err + } + + // Add groups obtained by provider-specific fetching to the claims + // so that they can be used for bound_claims validation on the role. + allClaims["groups"] = groupsRaw } } groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim) diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go index 88cd95c42..6f993c844 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go @@ -280,15 +280,15 @@ func (b *jwtAuthBackend) pathCallback(ctx context.Context, req *logical.Request, } } - if err := validateBoundClaims(b.Logger(), role.BoundClaimsType, role.BoundClaims, allClaims); err != nil { - return logical.ErrorResponse("error validating claims: %s", err.Error()), nil - } - - alias, groupAliases, err := b.createIdentity(allClaims, role) + alias, groupAliases, err := b.createIdentity(ctx, allClaims, role) if err != nil { return logical.ErrorResponse(err.Error()), nil } + if err := validateBoundClaims(b.Logger(), role.BoundClaimsType, role.BoundClaims, allClaims); err != nil { + return logical.ErrorResponse("error validating claims: %s", err.Error()), nil + } + tokenMetadata := map[string]string{"role": roleName} for k, v := range alias.Metadata { tokenMetadata[k] = v diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go index f4fe00dfa..e497d8a79 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go @@ -35,7 +35,7 @@ type AzureProvider struct { } // Initialize anything in the AzureProvider struct - satisfying the CustomProvider interface -func (a *AzureProvider) Initialize(jc *jwtConfig) error { +func (a *AzureProvider) Initialize(_ context.Context, _ *jwtConfig) error { return nil } @@ -45,7 +45,7 @@ func (a *AzureProvider) SensitiveKeys() []string { } // FetchGroups - custom groups fetching for azure - satisfying GroupsFetcher interface -func (a *AzureProvider) FetchGroups(b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { +func (a *AzureProvider) FetchGroups(_ context.Context, b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim) if groupsClaimRaw == nil { diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go index c3f9542c1..4f91e90b6 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go @@ -1,6 +1,7 @@ package jwtauth import ( + "context" "fmt" ) @@ -21,7 +22,7 @@ type CustomProvider interface { // Initialize should validate jwtConfig.ProviderConfig, set internal values // and run any initialization necessary for subsequent calls to interface // functions the provider implements - Initialize(*jwtConfig) error + Initialize(context.Context, *jwtConfig) error // SensitiveKeys returns any fields in a provider's jwtConfig.ProviderConfig // that should be masked or omitted when output @@ -31,7 +32,7 @@ type CustomProvider interface { // NewProviderConfig - returns appropriate provider struct if provider_config is // specified in jwtConfig. The provider map is provider name -to- instance of a // CustomProvider. -func NewProviderConfig(jc *jwtConfig, providerMap map[string]CustomProvider) (CustomProvider, error) { +func NewProviderConfig(ctx context.Context, jc *jwtConfig, providerMap map[string]CustomProvider) (CustomProvider, error) { if len(jc.ProviderConfig) == 0 { return nil, nil } @@ -43,7 +44,7 @@ func NewProviderConfig(jc *jwtConfig, providerMap map[string]CustomProvider) (Cu if !ok { return nil, fmt.Errorf("provider %q not found in custom providers", provider) } - if err := newCustomProvider.Initialize(jc); err != nil { + if err := newCustomProvider.Initialize(ctx, jc); err != nil { return nil, fmt.Errorf("error initializing %q provider_config: %s", provider, err) } return newCustomProvider, nil @@ -51,11 +52,11 @@ func NewProviderConfig(jc *jwtConfig, providerMap map[string]CustomProvider) (Cu // UserInfoFetcher - Optional support for custom user info handling type UserInfoFetcher interface { - FetchUserInfo(*jwtAuthBackend, map[string]interface{}, *jwtRole) error + FetchUserInfo(context.Context, *jwtAuthBackend, map[string]interface{}, *jwtRole) error } // GroupsFetcher - Optional support for custom groups handling type GroupsFetcher interface { // FetchGroups queries for groups claims during login - FetchGroups(*jwtAuthBackend, map[string]interface{}, *jwtRole) (interface{}, error) + FetchGroups(context.Context, *jwtAuthBackend, map[string]interface{}, *jwtRole) (interface{}, error) } diff --git a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_gsuite.go b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_gsuite.go index 558076274..009e288e1 100644 --- a/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_gsuite.go +++ b/vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_gsuite.go @@ -46,7 +46,7 @@ type GSuiteProviderConfig struct { } // Initialize initializes the GSuiteProvider by validating and creating configuration. -func (g *GSuiteProvider) Initialize(jc *jwtConfig) error { +func (g *GSuiteProvider) Initialize(ctx context.Context, jc *jwtConfig) error { // Decode the provider config var config GSuiteProviderConfig if err := mapstructure.Decode(jc.ProviderConfig, &config); err != nil { @@ -60,10 +60,10 @@ func (g *GSuiteProvider) Initialize(jc *jwtConfig) error { } config.serviceAccountKeyJSON = keyJSON - return g.initialize(config) + return g.initialize(ctx, config) } -func (g *GSuiteProvider) initialize(config GSuiteProviderConfig) error { +func (g *GSuiteProvider) initialize(ctx context.Context, config GSuiteProviderConfig) error { var err error // Validate configuration @@ -88,6 +88,13 @@ func (g *GSuiteProvider) initialize(config GSuiteProviderConfig) error { // Set the subject to impersonate and config g.jwtConfig.Subject = config.AdminImpersonateEmail g.config = config + + // Create a new admin service for requests to Google admin APIs + g.adminSvc, err = admin.NewService(ctx, option.WithHTTPClient(g.jwtConfig.Client(ctx))) + if err != nil { + return err + } + return nil } @@ -97,7 +104,7 @@ func (g *GSuiteProvider) SensitiveKeys() []string { } // FetchGroups fetches and returns groups from G Suite. -func (g *GSuiteProvider) FetchGroups(b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { +func (g *GSuiteProvider) FetchGroups(ctx context.Context, b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) { if !g.config.FetchGroups { return nil, nil } @@ -107,15 +114,9 @@ func (g *GSuiteProvider) FetchGroups(b *jwtAuthBackend, allClaims map[string]int return nil, err } - // Set context and create a new admin service for requests to Google admin APIs - g.adminSvc, err = admin.NewService(b.providerCtx, option.WithHTTPClient(g.jwtConfig.Client(b.providerCtx))) - if err != nil { - return nil, err - } - // Get the G Suite groups userGroupsMap := make(map[string]bool) - if err := g.search(b.providerCtx, userGroupsMap, userName, g.config.GroupsRecurseMaxDepth); err != nil { + if err := g.search(ctx, userGroupsMap, userName, g.config.GroupsRecurseMaxDepth); err != nil { return nil, err } @@ -157,7 +158,7 @@ func (g *GSuiteProvider) search(ctx context.Context, visited map[string]bool, us } // FetchUserInfo fetches additional user information from G Suite using custom schemas. -func (g *GSuiteProvider) FetchUserInfo(b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) error { +func (g *GSuiteProvider) FetchUserInfo(ctx context.Context, b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) error { if !g.config.FetchUserInfo || g.config.UserCustomSchemas == "" { if g.config.UserCustomSchemas != "" { b.Logger().Warn(fmt.Sprintf("must set 'fetch_user_info=true' to fetch 'user_custom_schemas': %s", g.config.UserCustomSchemas)) @@ -171,13 +172,7 @@ func (g *GSuiteProvider) FetchUserInfo(b *jwtAuthBackend, allClaims map[string]i return err } - // Set context and create a new admin service for requests to Google admin APIs - g.adminSvc, err = admin.NewService(b.providerCtx, option.WithHTTPClient(g.jwtConfig.Client(b.providerCtx))) - if err != nil { - return err - } - - return g.fillCustomSchemas(b.providerCtx, userName, allClaims) + return g.fillCustomSchemas(ctx, userName, allClaims) } // fillCustomSchemas fetches G Suite user information associated with the custom schemas diff --git a/vendor/modules.txt b/vendor/modules.txt index 150ba21d0..72b992096 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -515,7 +515,7 @@ github.com/hashicorp/vault-plugin-auth-cf/util # github.com/hashicorp/vault-plugin-auth-gcp v0.8.0 github.com/hashicorp/vault-plugin-auth-gcp/plugin github.com/hashicorp/vault-plugin-auth-gcp/plugin/cache -# github.com/hashicorp/vault-plugin-auth-jwt v0.8.0 +# github.com/hashicorp/vault-plugin-auth-jwt v0.7.2-0.20201203001230-e35700fcc0d5 github.com/hashicorp/vault-plugin-auth-jwt # github.com/hashicorp/vault-plugin-auth-kerberos v0.2.0 github.com/hashicorp/vault-plugin-auth-kerberos