mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
Merge pull request #87612 from enj/enj/i/oidc_audience_token_review
Make oidc authenticator audience agnostic
This commit is contained in:
commit
c5bf3fbdf0
@ -165,7 +165,6 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er
|
|||||||
oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{
|
oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{
|
||||||
IssuerURL: config.OIDCIssuerURL,
|
IssuerURL: config.OIDCIssuerURL,
|
||||||
ClientID: config.OIDCClientID,
|
ClientID: config.OIDCClientID,
|
||||||
APIAudiences: config.APIAudiences,
|
|
||||||
CAFile: config.OIDCCAFile,
|
CAFile: config.OIDCCAFile,
|
||||||
UsernameClaim: config.OIDCUsernameClaim,
|
UsernameClaim: config.OIDCUsernameClaim,
|
||||||
UsernamePrefix: config.OIDCUsernamePrefix,
|
UsernamePrefix: config.OIDCUsernamePrefix,
|
||||||
@ -177,7 +176,7 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
tokenAuthenticators = append(tokenAuthenticators, oidcAuth)
|
tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, oidcAuth))
|
||||||
}
|
}
|
||||||
if len(config.WebhookTokenAuthnConfigFile) > 0 {
|
if len(config.WebhookTokenAuthnConfigFile) > 0 {
|
||||||
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnVersion, config.WebhookTokenAuthnCacheTTL, config.APIAudiences)
|
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnVersion, config.WebhookTokenAuthnCacheTTL, config.APIAudiences)
|
||||||
|
@ -13,7 +13,6 @@ go_test(
|
|||||||
data = glob(["testdata/**"]),
|
data = glob(["testdata/**"]),
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
"//vendor/github.com/coreos/go-oidc:go_default_library",
|
"//vendor/github.com/coreos/go-oidc:go_default_library",
|
||||||
"//vendor/gopkg.in/square/go-jose.v2:go_default_library",
|
"//vendor/gopkg.in/square/go-jose.v2:go_default_library",
|
||||||
|
@ -78,12 +78,6 @@ type Options struct {
|
|||||||
// See: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
// See: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||||
ClientID string
|
ClientID string
|
||||||
|
|
||||||
// APIAudiences are the audiences that the API server identitifes as. The
|
|
||||||
// (API audiences unioned with the ClientIDs) should have a non-empty
|
|
||||||
// intersection with the request's target audience. This preserves the
|
|
||||||
// behavior of the OIDC authenticator pre-introduction of API audiences.
|
|
||||||
APIAudiences authenticator.Audiences
|
|
||||||
|
|
||||||
// Path to a PEM encoded root certificate of the provider.
|
// Path to a PEM encoded root certificate of the provider.
|
||||||
CAFile string
|
CAFile string
|
||||||
|
|
||||||
@ -194,8 +188,6 @@ type Authenticator struct {
|
|||||||
groupsClaim string
|
groupsClaim string
|
||||||
groupsPrefix string
|
groupsPrefix string
|
||||||
requiredClaims map[string]string
|
requiredClaims map[string]string
|
||||||
clientIDs authenticator.Audiences
|
|
||||||
apiAudiences authenticator.Audiences
|
|
||||||
|
|
||||||
// Contains an *oidc.IDTokenVerifier. Do not access directly use the
|
// Contains an *oidc.IDTokenVerifier. Do not access directly use the
|
||||||
// idTokenVerifier method.
|
// idTokenVerifier method.
|
||||||
@ -325,8 +317,6 @@ func newAuthenticator(opts Options, initVerifier func(ctx context.Context, a *Au
|
|||||||
groupsClaim: opts.GroupsClaim,
|
groupsClaim: opts.GroupsClaim,
|
||||||
groupsPrefix: opts.GroupsPrefix,
|
groupsPrefix: opts.GroupsPrefix,
|
||||||
requiredClaims: opts.RequiredClaims,
|
requiredClaims: opts.RequiredClaims,
|
||||||
clientIDs: authenticator.Audiences{opts.ClientID},
|
|
||||||
apiAudiences: opts.APIAudiences,
|
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
resolver: resolver,
|
resolver: resolver,
|
||||||
}
|
}
|
||||||
@ -542,11 +532,6 @@ func (r *claimResolver) resolve(endpoint endpoint, allClaims claims) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
func (a *Authenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||||
if reqAuds, ok := authenticator.AudiencesFrom(ctx); ok {
|
|
||||||
if len(reqAuds.Intersect(a.clientIDs)) == 0 && len(reqAuds.Intersect(a.apiAudiences)) == 0 {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !hasCorrectIssuer(a.issuerURL, token) {
|
if !hasCorrectIssuer(a.issuerURL, token) {
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,6 @@ import (
|
|||||||
|
|
||||||
oidc "github.com/coreos/go-oidc"
|
oidc "github.com/coreos/go-oidc"
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
@ -142,7 +140,6 @@ type claimsTest struct {
|
|||||||
wantInitErr bool
|
wantInitErr bool
|
||||||
claimToResponseMap map[string]string
|
claimToResponseMap map[string]string
|
||||||
openIDConfig string
|
openIDConfig string
|
||||||
reqAudiences authenticator.Audiences
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace formats the contents of v into the provided template.
|
// Replace formats the contents of v into the provided template.
|
||||||
@ -299,12 +296,7 @@ func (c *claimsTest) run(t *testing.T) {
|
|||||||
t.Fatalf("serialize token: %v", err)
|
t.Fatalf("serialize token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
got, ok, err := a.AuthenticateToken(context.Background(), token)
|
||||||
if c.reqAudiences != nil {
|
|
||||||
ctx = authenticator.WithAudiences(ctx, c.reqAudiences)
|
|
||||||
}
|
|
||||||
|
|
||||||
got, ok, err := a.AuthenticateToken(ctx, token)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !c.wantErr {
|
if !c.wantErr {
|
||||||
@ -1397,11 +1389,10 @@ func TestToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "good token with api req audience",
|
name: "good token with bad client id",
|
||||||
options: Options{
|
options: Options{
|
||||||
IssuerURL: "https://auth.example.com",
|
IssuerURL: "https://auth.example.com",
|
||||||
ClientID: "my-client",
|
ClientID: "my-client",
|
||||||
APIAudiences: authenticator.Audiences{"api"},
|
|
||||||
UsernameClaim: "username",
|
UsernameClaim: "username",
|
||||||
now: func() time.Time { return now },
|
now: func() time.Time { return now },
|
||||||
},
|
},
|
||||||
@ -1411,132 +1402,11 @@ func TestToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
claims: fmt.Sprintf(`{
|
claims: fmt.Sprintf(`{
|
||||||
"iss": "https://auth.example.com",
|
"iss": "https://auth.example.com",
|
||||||
"aud": "my-client",
|
"aud": "my-wrong-client",
|
||||||
"username": "jane",
|
"username": "jane",
|
||||||
"exp": %d
|
"exp": %d
|
||||||
}`, valid.Unix()),
|
}`, valid.Unix()),
|
||||||
reqAudiences: authenticator.Audiences{"api"},
|
wantErr: true,
|
||||||
want: &user.DefaultInfo{
|
|
||||||
Name: "jane",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good token with multiple api req audience",
|
|
||||||
options: Options{
|
|
||||||
IssuerURL: "https://auth.example.com",
|
|
||||||
ClientID: "my-client",
|
|
||||||
APIAudiences: authenticator.Audiences{"api", "other"},
|
|
||||||
UsernameClaim: "username",
|
|
||||||
now: func() time.Time { return now },
|
|
||||||
},
|
|
||||||
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
pubKeys: []*jose.JSONWebKey{
|
|
||||||
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
},
|
|
||||||
claims: fmt.Sprintf(`{
|
|
||||||
"iss": "https://auth.example.com",
|
|
||||||
"aud": "my-client",
|
|
||||||
"username": "jane",
|
|
||||||
"exp": %d
|
|
||||||
}`, valid.Unix()),
|
|
||||||
reqAudiences: authenticator.Audiences{"api"},
|
|
||||||
want: &user.DefaultInfo{
|
|
||||||
Name: "jane",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good token with client_id req audience",
|
|
||||||
options: Options{
|
|
||||||
IssuerURL: "https://auth.example.com",
|
|
||||||
ClientID: "my-client",
|
|
||||||
APIAudiences: authenticator.Audiences{"api"},
|
|
||||||
UsernameClaim: "username",
|
|
||||||
now: func() time.Time { return now },
|
|
||||||
},
|
|
||||||
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
pubKeys: []*jose.JSONWebKey{
|
|
||||||
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
},
|
|
||||||
claims: fmt.Sprintf(`{
|
|
||||||
"iss": "https://auth.example.com",
|
|
||||||
"aud": "my-client",
|
|
||||||
"username": "jane",
|
|
||||||
"exp": %d
|
|
||||||
}`, valid.Unix()),
|
|
||||||
reqAudiences: authenticator.Audiences{"my-client"},
|
|
||||||
want: &user.DefaultInfo{
|
|
||||||
Name: "jane",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good token with client_id and api req audience",
|
|
||||||
options: Options{
|
|
||||||
IssuerURL: "https://auth.example.com",
|
|
||||||
ClientID: "my-client",
|
|
||||||
APIAudiences: authenticator.Audiences{"api"},
|
|
||||||
UsernameClaim: "username",
|
|
||||||
now: func() time.Time { return now },
|
|
||||||
},
|
|
||||||
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
pubKeys: []*jose.JSONWebKey{
|
|
||||||
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
},
|
|
||||||
claims: fmt.Sprintf(`{
|
|
||||||
"iss": "https://auth.example.com",
|
|
||||||
"aud": "my-client",
|
|
||||||
"username": "jane",
|
|
||||||
"exp": %d
|
|
||||||
}`, valid.Unix()),
|
|
||||||
reqAudiences: authenticator.Audiences{"my-client", "api"},
|
|
||||||
want: &user.DefaultInfo{
|
|
||||||
Name: "jane",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good token with client_id and api req audience",
|
|
||||||
options: Options{
|
|
||||||
IssuerURL: "https://auth.example.com",
|
|
||||||
ClientID: "my-client",
|
|
||||||
APIAudiences: authenticator.Audiences{"api"},
|
|
||||||
UsernameClaim: "username",
|
|
||||||
now: func() time.Time { return now },
|
|
||||||
},
|
|
||||||
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
pubKeys: []*jose.JSONWebKey{
|
|
||||||
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
},
|
|
||||||
claims: fmt.Sprintf(`{
|
|
||||||
"iss": "https://auth.example.com",
|
|
||||||
"aud": "my-client",
|
|
||||||
"username": "jane",
|
|
||||||
"exp": %d
|
|
||||||
}`, valid.Unix()),
|
|
||||||
reqAudiences: authenticator.Audiences{"my-client", "api"},
|
|
||||||
want: &user.DefaultInfo{
|
|
||||||
Name: "jane",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good token with client_id and bad req audience",
|
|
||||||
options: Options{
|
|
||||||
IssuerURL: "https://auth.example.com",
|
|
||||||
ClientID: "my-client",
|
|
||||||
APIAudiences: authenticator.Audiences{"api"},
|
|
||||||
UsernameClaim: "username",
|
|
||||||
now: func() time.Time { return now },
|
|
||||||
},
|
|
||||||
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
pubKeys: []*jose.JSONWebKey{
|
|
||||||
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
|
||||||
},
|
|
||||||
claims: fmt.Sprintf(`{
|
|
||||||
"iss": "https://auth.example.com",
|
|
||||||
"aud": "my-client",
|
|
||||||
"username": "jane",
|
|
||||||
"exp": %d
|
|
||||||
}`, valid.Unix()),
|
|
||||||
reqAudiences: authenticator.Audiences{"other"},
|
|
||||||
wantSkip: true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
Loading…
Reference in New Issue
Block a user