Update auth-jwt to v0.7.0 (#9320)
Adds support for distributed groups claims on Azure, necessary when a user is a member of more than 200 groups.
This commit is contained in:
parent
6bd5674345
commit
c943235288
2
go.mod
2
go.mod
|
@ -70,7 +70,7 @@ require (
|
|||
github.com/hashicorp/vault-plugin-auth-centrify v0.5.5
|
||||
github.com/hashicorp/vault-plugin-auth-cf v0.5.4
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.2-0.20200428223335-82bd3a3ad5b3
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.6.2
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.7.0
|
||||
github.com/hashicorp/vault-plugin-auth-kerberos v0.1.6
|
||||
github.com/hashicorp/vault-plugin-auth-kubernetes v0.6.2
|
||||
github.com/hashicorp/vault-plugin-auth-oci v0.5.5
|
||||
|
|
6
go.sum
6
go.sum
|
@ -531,8 +531,9 @@ github.com/hashicorp/vault-plugin-auth-gcp v0.5.1/go.mod h1:eLj92eX8MPI4vY1jaazV
|
|||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.1 h1:WXTuja3WC2BdZekYCnzuZGoVvZTAGH8kSDUHzOK2PQY=
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.1/go.mod h1:8eBRzg+JIhAaDBfDndDAQKIhDrQ3WW8OPklxAYftNFs=
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.2-0.20200428223335-82bd3a3ad5b3/go.mod h1:U0fkAlxWTEyQ74lx8wlGdD493lP1DD/qpMjXgOEbwj0=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.6.2 h1:fp6Rk89iPjDS8dyEK7lEauYE/UhkgkHbmwRZKuQA01U=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.6.2/go.mod h1:SFadxIfoLGzugEjwUUmUaCGbsYEz2/jJymZDDQjEqYg=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.7.0 h1:lHg02BB7IpUQbJStAPmGyS3KnZJC7PSEvc5LOZNPjHM=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.7.0/go.mod h1:ZJJy4b0H3N7CSoJ6iPlWhV9EjHLSoB5NhP06CNm6ImU=
|
||||
github.com/hashicorp/vault-plugin-auth-kerberos v0.1.5 h1:knWedzZ51g8Aj6Hyi1ATlQ/7jEx6nJeqFoCoHSrbQFI=
|
||||
github.com/hashicorp/vault-plugin-auth-kerberos v0.1.5/go.mod h1:r4UqWITHYKmBeAMKPWqLo4V8bl/wNqoSIaQcMpeK9ss=
|
||||
github.com/hashicorp/vault-plugin-auth-kerberos v0.1.6 h1:l5wu8J7aiQBLsTtkKhf1QQjGoeVjcfcput+uJ/pu2MM=
|
||||
|
@ -697,8 +698,9 @@ github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc
|
|||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8 h1:1CO5wil3HuiVLrUQ2ovSTO+6AfNOA5EMkHHVyHE9IwA=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
|
||||
github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
|
|
|
@ -104,7 +104,28 @@ $ vault auth enable -plugin-name='jwt' plugin
|
|||
Successfully enabled 'plugin' at 'jwt'!
|
||||
```
|
||||
|
||||
#### Tests
|
||||
### Provider-specific handling
|
||||
|
||||
Provider-specific handling can be added by writing an object that conforms to
|
||||
one or more interfaces in [provider_config.go](provider_config.go). Some
|
||||
interfaces will be required, like [CustomProvider](provider_config.go), and
|
||||
others will be invoked if present during the login process (e.g. GroupsFetcher).
|
||||
The interfaces themselves will be small (usually a single method) as it is
|
||||
expected that the parts of the login that need specialization will be different
|
||||
per provider. This pattern allows us to start with a minimal set and add
|
||||
interfaces as necessary.
|
||||
|
||||
If a custom provider is configured on the backend object and satisfies a given
|
||||
interface, the interface will be used during the relevant part of the login
|
||||
flow. e.g. after an ID token has been received, the custom provider's
|
||||
UserInfoFetcher interface will be used, if present, to fetch and merge
|
||||
additional identity data.
|
||||
|
||||
The custom handlers will be standalone objects defined in their own file (one
|
||||
per provider). They'll be part of the main jwtauth package to avoid potential
|
||||
circular import issues.
|
||||
|
||||
### Tests
|
||||
|
||||
If you are developing this plugin and want to verify it is still
|
||||
functioning (and you haven't broken anything else), we recommend
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module github.com/hashicorp/vault-plugin-auth-jwt
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
|
@ -10,14 +10,16 @@ require (
|
|||
github.com/hashicorp/go-hclog v0.12.0
|
||||
github.com/hashicorp/go-sockaddr v1.0.2
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02
|
||||
github.com/hashicorp/go-version v1.2.0 // indirect
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8
|
||||
github.com/mitchellh/pointerstructure v1.0.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
github.com/stretchr/testify v1.3.0
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/appengine v1.5.0 // indirect
|
||||
|
|
|
@ -62,17 +62,19 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I
|
|||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02 h1:OGEV0U0+lb8SP5aZA1m456Sr3MYxFel2awVr55QRri0=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820 h1:biZidYDDEWnuOI9mXnJre8lwHKhb5ym85aSXk3oz/dc=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500 h1:tiMX2ewq4ble+e2zENzBvaH2dMoFHe80NbnrF5Ir9Kk=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02 h1:vVrOAVfunVvkTkE9iF3Fe1+PGPLwGIp3nP4qgHGrHFs=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820 h1:TmDZ1sS6gU0hFeFlFuyJVUwRPEzifZIHCBeS2WF2uSc=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
|
@ -95,8 +97,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
|||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8 h1:1CO5wil3HuiVLrUQ2ovSTO+6AfNOA5EMkHHVyHE9IwA=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
|
||||
github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
|
@ -140,8 +142,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
|
@ -81,6 +81,18 @@ func pathConfig(b *jwtAuthBackend) *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "The value against which to match the 'iss' claim in a JWT. Optional.",
|
||||
},
|
||||
"provider_config": {
|
||||
Type: framework.TypeMap,
|
||||
Description: "Provider-specific configuration. Optional.",
|
||||
DisplayAttrs: &framework.DisplayAttributes{
|
||||
Name: "Provider Config",
|
||||
Value: map[string]interface{}{
|
||||
"provider": "gsuite",
|
||||
"fetch_groups": true,
|
||||
"gsuite_service_account": "ey4921...",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
|
@ -157,6 +169,7 @@ func (b *jwtAuthBackend) pathConfigRead(ctx context.Context, req *logical.Reques
|
|||
"jwks_url": config.JWKSURL,
|
||||
"jwks_ca_pem": config.JWKSCAPEM,
|
||||
"bound_issuer": config.BoundIssuer,
|
||||
"provider_config": config.ProviderConfig,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -177,6 +190,7 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
JWTValidationPubKeys: d.Get("jwt_validation_pubkeys").([]string),
|
||||
JWTSupportedAlgs: d.Get("jwt_supported_algs").([]string),
|
||||
BoundIssuer: d.Get("bound_issuer").(string),
|
||||
ProviderConfig: d.Get("provider_config").(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Run checks on values
|
||||
|
@ -239,6 +253,9 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
return nil, errors.New("unknown condition")
|
||||
}
|
||||
|
||||
// NOTE: the OIDC lib states that if nothing is passed into its config, it
|
||||
// defaults to "RS256". So in the case of a zero value here it won't
|
||||
// default to e.g. "none".
|
||||
for _, a := range config.JWTSupportedAlgs {
|
||||
switch a {
|
||||
case oidc.RS256, oidc.RS384, oidc.RS512, oidc.ES256, oidc.ES384, oidc.ES512, oidc.PS256, oidc.PS384, oidc.PS512:
|
||||
|
@ -263,6 +280,11 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
return logical.ErrorResponse("invalid response_mode: %q", config.OIDCResponseMode), nil
|
||||
}
|
||||
|
||||
// Validate provider_config
|
||||
if _, err := NewProviderConfig(config, ProviderMap()); err != nil {
|
||||
return logical.ErrorResponse("invalid provider_config: %s", err), nil
|
||||
}
|
||||
|
||||
entry, err := logical.StorageEntryJSON(configPath, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -318,18 +340,19 @@ func (b *jwtAuthBackend) createCAContext(ctx context.Context, caPEM string) (con
|
|||
}
|
||||
|
||||
type jwtConfig struct {
|
||||
OIDCDiscoveryURL string `json:"oidc_discovery_url"`
|
||||
OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"`
|
||||
OIDCClientID string `json:"oidc_client_id"`
|
||||
OIDCClientSecret string `json:"oidc_client_secret"`
|
||||
OIDCResponseMode string `json:"oidc_response_mode"`
|
||||
OIDCResponseTypes []string `json:"oidc_response_types"`
|
||||
JWKSURL string `json:"jwks_url"`
|
||||
JWKSCAPEM string `json:"jwks_ca_pem"`
|
||||
JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"`
|
||||
JWTSupportedAlgs []string `json:"jwt_supported_algs"`
|
||||
BoundIssuer string `json:"bound_issuer"`
|
||||
DefaultRole string `json:"default_role"`
|
||||
OIDCDiscoveryURL string `json:"oidc_discovery_url"`
|
||||
OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"`
|
||||
OIDCClientID string `json:"oidc_client_id"`
|
||||
OIDCClientSecret string `json:"oidc_client_secret"`
|
||||
OIDCResponseMode string `json:"oidc_response_mode"`
|
||||
OIDCResponseTypes []string `json:"oidc_response_types"`
|
||||
JWKSURL string `json:"jwks_url"`
|
||||
JWKSCAPEM string `json:"jwks_ca_pem"`
|
||||
JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"`
|
||||
JWTSupportedAlgs []string `json:"jwt_supported_algs"`
|
||||
BoundIssuer string `json:"bound_issuer"`
|
||||
DefaultRole string `json:"default_role"`
|
||||
ProviderConfig map[string]interface{} `json:"provider_config"`
|
||||
|
||||
ParsedJWTPubKeys []interface{} `json:"-"`
|
||||
}
|
||||
|
|
|
@ -334,10 +334,9 @@ func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role *
|
|||
return alias, groupAliases, nil
|
||||
}
|
||||
|
||||
groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim)
|
||||
|
||||
if groupsClaimRaw == nil {
|
||||
return nil, nil, fmt.Errorf("%q claim not found in token", role.GroupsClaim)
|
||||
groupsClaimRaw, err := b.fetchGroups(allClaims, role)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to fetch groups: %s", err)
|
||||
}
|
||||
|
||||
groups, ok := normalizeList(groupsClaimRaw)
|
||||
|
@ -361,6 +360,28 @@ func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role *
|
|||
return alias, groupAliases, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim)
|
||||
|
||||
if groupsClaimRaw == nil {
|
||||
return nil, fmt.Errorf("%q claim not found in token", role.GroupsClaim)
|
||||
}
|
||||
|
||||
return groupsClaimRaw, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pathLoginHelpSyn = `
|
||||
Authenticates to Vault using a JWT (or OIDC) token.
|
||||
|
|
|
@ -28,6 +28,7 @@ const (
|
|||
errLoginFailed = "Vault login failed."
|
||||
errNoResponse = "No response from provider."
|
||||
errTokenVerification = "Token verification failed."
|
||||
errNotOIDCFlow = "OIDC login is not configured for this mount"
|
||||
|
||||
noCode = "no_code"
|
||||
)
|
||||
|
@ -344,7 +345,7 @@ func (b *jwtAuthBackend) authURL(ctx context.Context, req *logical.Request, d *f
|
|||
}
|
||||
|
||||
if config.authType() != OIDCFlow {
|
||||
return logical.ErrorResponse("OIDC login is not configured for this mount"), nil
|
||||
return logical.ErrorResponse(errNotOIDCFlow), nil
|
||||
}
|
||||
|
||||
roleName := d.Get("role").(string)
|
||||
|
|
188
vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go
generated
vendored
Normal file
188
vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_azure.go
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
|||
package jwtauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/clientcredentials"
|
||||
)
|
||||
|
||||
const (
|
||||
// The old MS graph API requires setting an api-version query parameter
|
||||
windowsGraphHost = "graph.windows.net"
|
||||
windowsAPIVersion = "1.6"
|
||||
|
||||
// Distributed claim fields
|
||||
claimNamesField = "_claim_names"
|
||||
claimSourcesField = "_claim_sources"
|
||||
)
|
||||
|
||||
// AzureProvider is used for Azure-specific configuration
|
||||
type AzureProvider struct {
|
||||
// Context for azure calls
|
||||
ctx context.Context
|
||||
|
||||
// OIDC provider
|
||||
provider *oidc.Provider
|
||||
}
|
||||
|
||||
// Initialize anything in the AzureProvider struct - satisfying the CustomProvider interface
|
||||
func (a *AzureProvider) Initialize(jc *jwtConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SensitiveKeys - satisfying the CustomProvider interface
|
||||
func (a *AzureProvider) SensitiveKeys() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// FetchGroups - custom groups fetching for azure - satisfying GroupsFetcher interface
|
||||
func (a *AzureProvider) FetchGroups(b *jwtAuthBackend, allClaims map[string]interface{}, role *jwtRole) (interface{}, error) {
|
||||
groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim)
|
||||
|
||||
if groupsClaimRaw == nil {
|
||||
// If the "groups" claim is missing, it might be because the user is a
|
||||
// member of more than 200 groups, which means the token contains
|
||||
// distributed claim information. Attempt to look that up here.
|
||||
azureClaimSourcesURL, err := a.getClaimSource(b.Logger(), allClaims, role)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get claim sources: %s", err)
|
||||
}
|
||||
|
||||
// Get provider because we'll need to get a new token for microsoft's
|
||||
// graph API, specifically the old graph API
|
||||
provider, err := b.getProvider(b.cachedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get provider: %s", err)
|
||||
}
|
||||
a.provider = provider
|
||||
|
||||
a.ctx, err = b.createCAContext(b.providerCtx, b.cachedConfig.OIDCDiscoveryCAPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create CA Context: %s", err)
|
||||
}
|
||||
|
||||
azureGroups, err := a.getAzureGroups(azureClaimSourcesURL, b.cachedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%q claim not found in token: %v", role.GroupsClaim, err)
|
||||
}
|
||||
groupsClaimRaw = azureGroups
|
||||
}
|
||||
b.Logger().Debug(fmt.Sprintf("groups claim raw is %v", groupsClaimRaw))
|
||||
return groupsClaimRaw, nil
|
||||
}
|
||||
|
||||
// In Azure, if you are indirectly member of more than 200 groups, they will
|
||||
// send _claim_names and _claim_sources instead of the groups, per OIDC Core
|
||||
// 1.0, section 5.6.2:
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims
|
||||
// In the future this could be used with other providers as well. Example:
|
||||
//
|
||||
// {
|
||||
// "_claim_names": {
|
||||
// "groups": "src1"
|
||||
// },
|
||||
// "_claim_sources": {
|
||||
// "src1": {
|
||||
// "endpoint": "https://graph.windows.net...."
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// For this to work, "profile" should be set in "oidc_scopes" in the vault oidc role.
|
||||
//
|
||||
func (a *AzureProvider) getClaimSource(logger log.Logger, allClaims map[string]interface{}, role *jwtRole) (string, error) {
|
||||
// Get the source key for the groups claim
|
||||
name := fmt.Sprintf("/%s/%s", claimNamesField, role.GroupsClaim)
|
||||
groupsClaimSource := getClaim(logger, allClaims, name)
|
||||
if groupsClaimSource == nil {
|
||||
return "", fmt.Errorf("unable to locate groups claim %q in %s", role.GroupsClaim, claimNamesField)
|
||||
}
|
||||
// Get the endpoint source for the groups claim
|
||||
endpoint := fmt.Sprintf("/%s/%s/endpoint", claimSourcesField, groupsClaimSource.(string))
|
||||
val := getClaim(logger, allClaims, endpoint)
|
||||
if val == nil {
|
||||
return "", fmt.Errorf("unable to locate %s in claims", endpoint)
|
||||
}
|
||||
logger.Debug(fmt.Sprintf("found Azure Graph API endpoint for group membership: %v", val))
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
|
||||
// Fetch user groups from the Azure AD Graph API
|
||||
func (a *AzureProvider) getAzureGroups(groupsURL string, c *jwtConfig) (interface{}, error) {
|
||||
urlParsed, err := url.Parse(groupsURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse distributed groups source url %s: %s", groupsURL, err)
|
||||
}
|
||||
token, err := a.getAzureToken(c, urlParsed.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get token: %s", err)
|
||||
}
|
||||
payload := strings.NewReader("{\"securityEnabledOnly\": false}")
|
||||
req, err := http.NewRequest("POST", groupsURL, payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error constructing groups endpoint request: %s", err)
|
||||
}
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req.Header.Add("authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
// If endpoint is the old windows graph api, add api-version
|
||||
if urlParsed.Host == windowsGraphHost {
|
||||
query := req.URL.Query()
|
||||
query.Add("api-version", windowsAPIVersion)
|
||||
req.URL.RawQuery = query.Encode()
|
||||
}
|
||||
client := http.DefaultClient
|
||||
if c, ok := a.ctx.Value(oauth2.HTTPClient).(*http.Client); ok {
|
||||
client = c
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to call Azure AD Graph API: %s", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read Azure AD Graph API response: %s", err)
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get groups: %s", string(body))
|
||||
}
|
||||
|
||||
var target azureGroups
|
||||
if err := json.Unmarshal(body, &target); err != nil {
|
||||
return nil, fmt.Errorf("unabled to decode response: %s", err)
|
||||
}
|
||||
return target.Value, nil
|
||||
}
|
||||
|
||||
// Login to Azure, using client id and secret.
|
||||
func (a *AzureProvider) getAzureToken(c *jwtConfig, host string) (string, error) {
|
||||
config := &clientcredentials.Config{
|
||||
ClientID: c.OIDCClientID,
|
||||
ClientSecret: c.OIDCClientSecret,
|
||||
TokenURL: a.provider.Endpoint().TokenURL,
|
||||
Scopes: []string{
|
||||
"openid",
|
||||
"profile",
|
||||
"https://" + host + "/.default",
|
||||
},
|
||||
}
|
||||
token, err := config.Token(a.ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to fetch Azure token: %s", err)
|
||||
}
|
||||
return token.AccessToken, nil
|
||||
}
|
||||
|
||||
type azureGroups struct {
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
55
vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go
generated
vendored
Normal file
55
vendor/github.com/hashicorp/vault-plugin-auth-jwt/provider_config.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package jwtauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Provider-specific configuration interfaces
|
||||
// All providers must implement the CustomProvider interface, and may implement
|
||||
// others as needed.
|
||||
|
||||
// ProviderMap returns a map of provider names to custom types
|
||||
func ProviderMap() map[string]CustomProvider {
|
||||
return map[string]CustomProvider{
|
||||
"azure": &AzureProvider{},
|
||||
}
|
||||
}
|
||||
|
||||
// CustomProvider - Any custom provider must implement this interface
|
||||
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
|
||||
|
||||
// SensitiveKeys returns any fields in a provider's jwtConfig.ProviderConfig
|
||||
// that should be masked or omitted when output
|
||||
SensitiveKeys() []string
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if len(jc.ProviderConfig) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
provider, ok := jc.ProviderConfig["provider"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("'provider' field not found in provider_config")
|
||||
}
|
||||
newCustomProvider, ok := providerMap[provider]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("provider %q not found in custom providers", provider)
|
||||
}
|
||||
if err := newCustomProvider.Initialize(jc); err != nil {
|
||||
return nil, fmt.Errorf("error initializing %q provider_config: %s", provider, err)
|
||||
}
|
||||
return newCustomProvider, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
|
@ -461,7 +461,7 @@ github.com/hashicorp/vault-plugin-auth-cf/util
|
|||
# github.com/hashicorp/vault-plugin-auth-gcp v0.6.1
|
||||
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.6.2
|
||||
# github.com/hashicorp/vault-plugin-auth-jwt v0.7.0
|
||||
github.com/hashicorp/vault-plugin-auth-jwt
|
||||
# github.com/hashicorp/vault-plugin-auth-kerberos v0.1.6
|
||||
github.com/hashicorp/vault-plugin-auth-kerberos
|
||||
|
@ -678,7 +678,7 @@ github.com/mitchellh/hashstructure
|
|||
github.com/mitchellh/iochan
|
||||
# github.com/mitchellh/mapstructure v1.3.2
|
||||
github.com/mitchellh/mapstructure
|
||||
# github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8
|
||||
# github.com/mitchellh/pointerstructure v1.0.0
|
||||
github.com/mitchellh/pointerstructure
|
||||
# github.com/mitchellh/reflectwalk v1.0.1
|
||||
github.com/mitchellh/reflectwalk
|
||||
|
|
Loading…
Reference in New Issue