From b5a1a45d48b4e90e54f512fc829b2ab9866b282e Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Tue, 6 Apr 2021 12:20:57 -0400 Subject: [PATCH] oidc authenticator: allow specifying a KeySet directly This change updates the oidc authenticator to allow specifying an oidc.KeySet as an input option. This makes it possible to synchronously initialize the KeySet instead of relying on the asynchronous initialization that is normally done to support self-hosted providers. This makes it easier to use this code as a library. Signed-off-by: Monis Khan --- .../pkg/authenticator/token/oidc/oidc.go | 43 ++++++++++--------- .../pkg/authenticator/token/oidc/oidc_test.go | 13 ++---- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go index 1fb5e70c679..2027c0ae4dc 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go @@ -70,6 +70,9 @@ type Options struct { // See: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig IssuerURL string + // Optional KeySet to allow for synchronous initlization instead of fetching from the remote issuer. + KeySet oidc.KeySet + // ClientID the JWT must be issued for, the "sub" field. This plugin only trusts a single // client to ensure the plugin can be used with public providers. // @@ -219,24 +222,6 @@ func (a *Authenticator) Close() { a.cancel() } -func New(opts Options) (*Authenticator, error) { - return newAuthenticator(opts, func(ctx context.Context, a *Authenticator, config *oidc.Config) { - // Asynchronously attempt to initialize the authenticator. This enables - // self-hosted providers, providers that run on top of Kubernetes itself. - go wait.PollImmediateUntil(time.Second*10, func() (done bool, err error) { - provider, err := oidc.NewProvider(ctx, a.issuerURL) - if err != nil { - klog.Errorf("oidc authenticator: initializing plugin: %v", err) - return false, nil - } - - verifier := provider.Verifier(config) - a.setVerifier(verifier) - return true, nil - }, ctx.Done()) - }) -} - // whitelist of signing algorithms to ensure users don't mistakenly pass something // goofy. var allowedSigningAlgs = map[string]bool{ @@ -251,7 +236,7 @@ var allowedSigningAlgs = map[string]bool{ oidc.PS512: true, } -func newAuthenticator(opts Options, initVerifier func(ctx context.Context, a *Authenticator, config *oidc.Config)) (*Authenticator, error) { +func New(opts Options) (*Authenticator, error) { url, err := url.Parse(opts.IssuerURL) if err != nil { return nil, err @@ -327,7 +312,25 @@ func newAuthenticator(opts Options, initVerifier func(ctx context.Context, a *Au resolver: resolver, } - initVerifier(ctx, authenticator, verifierConfig) + if opts.KeySet != nil { + // We already have a key set, synchronously initialize the verifier. + authenticator.setVerifier(oidc.NewVerifier(opts.IssuerURL, opts.KeySet, verifierConfig)) + } else { + // Asynchronously attempt to initialize the authenticator. This enables + // self-hosted providers, providers that run on top of Kubernetes itself. + go wait.PollImmediateUntil(10*time.Second, func() (done bool, err error) { + provider, err := oidc.NewProvider(ctx, opts.IssuerURL) + if err != nil { + klog.Errorf("oidc authenticator: initializing plugin: %v", err) + return false, nil + } + + verifier := provider.Verifier(verifierConfig) + authenticator.setVerifier(verifier) + return true, nil + }, ctx.Done()) + } + return authenticator, nil } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go index 93189d0ee63..23d851a959e 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go @@ -35,7 +35,6 @@ import ( "text/template" "time" - oidc "github.com/coreos/go-oidc" jose "gopkg.in/square/go-jose.v2" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/server/dynamiccertificates" @@ -271,15 +270,11 @@ func (c *claimsTest) run(t *testing.T) { c.claimToResponseMap[claim] = replace(response, &v) } + // Set the verifier to use the public key set instead of reading from a remote. + c.options.KeySet = &staticKeySet{keys: c.pubKeys} + // Initialize the authenticator. - a, err := newAuthenticator(c.options, func(ctx context.Context, a *Authenticator, config *oidc.Config) { - // Set the verifier to use the public key set instead of reading from a remote. - a.setVerifier(oidc.NewVerifier( - c.options.IssuerURL, - &staticKeySet{keys: c.pubKeys}, - config, - )) - }) + a, err := New(c.options) if err != nil { if !c.wantInitErr { t.Fatalf("initialize authenticator: %v", err)