Merge pull request #87612 from enj/enj/i/oidc_audience_token_review

Make oidc authenticator audience agnostic
This commit is contained in:
Kubernetes Prow Robot 2020-02-07 00:07:56 -08:00 committed by GitHub
commit c5bf3fbdf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 5 additions and 152 deletions

View File

@ -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)

View File

@ -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",

View File

@ -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
} }

View File

@ -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 {