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 <mok@vmware.com>
This commit is contained in:
Monis Khan 2021-04-06 12:20:57 -04:00
parent 5dd4c89df3
commit b5a1a45d48
No known key found for this signature in database
GPG Key ID: 52C90ADA01B269B8
2 changed files with 27 additions and 29 deletions

View File

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

View File

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