oidc authenticator: allow passing in CA via bytes

This change updates the OIDC authenticator code to use a subset of
the dynamiccertificates.CAContentProvider interface to provide the
root CA bytes.  This removes the hard dependency on a file based CA
and 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 11:04:05 -04:00
parent b0abe89ae2
commit 5dd4c89df3
No known key found for this signature in database
GPG Key ID: 52C90ADA01B269B8
3 changed files with 28 additions and 7 deletions

View File

@ -151,10 +151,20 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er
// simply returns an error, the OpenID Connect plugin may query the provider to // simply returns an error, the OpenID Connect plugin may query the provider to
// update the keys, causing performance hits. // update the keys, causing performance hits.
if len(config.OIDCIssuerURL) > 0 && len(config.OIDCClientID) > 0 { if len(config.OIDCIssuerURL) > 0 && len(config.OIDCClientID) > 0 {
// TODO(enj): wire up the Notifier and ControllerRunner bits when OIDC supports CA reload
var oidcCAContent oidc.CAContentProvider
if len(config.OIDCCAFile) != 0 {
var oidcCAErr error
oidcCAContent, oidcCAErr = dynamiccertificates.NewDynamicCAContentFromFile("oidc-authenticator", config.OIDCCAFile)
if oidcCAErr != nil {
return nil, nil, oidcCAErr
}
}
oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{ oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{
IssuerURL: config.OIDCIssuerURL, IssuerURL: config.OIDCIssuerURL,
ClientID: config.OIDCClientID, ClientID: config.OIDCClientID,
CAFile: config.OIDCCAFile, CAContentProvider: oidcCAContent,
UsernameClaim: config.OIDCUsernameClaim, UsernameClaim: config.OIDCUsernameClaim,
UsernamePrefix: config.OIDCUsernamePrefix, UsernamePrefix: config.OIDCUsernamePrefix,
GroupsClaim: config.OIDCGroupsClaim, GroupsClaim: config.OIDCGroupsClaim,

View File

@ -78,8 +78,8 @@ 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
// Path to a PEM encoded root certificate of the provider. // PEM encoded root certificate contents of the provider.
CAFile string CAContentProvider CAContentProvider
// UsernameClaim is the JWT field to use as the user's username. // UsernameClaim is the JWT field to use as the user's username.
UsernameClaim string UsernameClaim string
@ -116,6 +116,11 @@ type Options struct {
now func() time.Time now func() time.Time
} }
// Subset of dynamiccertificates.CAContentProvider that can be used to dynamically load root CAs.
type CAContentProvider interface {
CurrentCABundleContent() []byte
}
// initVerifier creates a new ID token verifier for the given configuration and issuer URL. On success, calls setVerifier with the // initVerifier creates a new ID token verifier for the given configuration and issuer URL. On success, calls setVerifier with the
// resulting verifier. // resulting verifier.
func initVerifier(ctx context.Context, config *oidc.Config, iss string) (*oidc.IDTokenVerifier, error) { func initVerifier(ctx context.Context, config *oidc.Config, iss string) (*oidc.IDTokenVerifier, error) {
@ -273,10 +278,11 @@ func newAuthenticator(opts Options, initVerifier func(ctx context.Context, a *Au
} }
var roots *x509.CertPool var roots *x509.CertPool
if opts.CAFile != "" { if opts.CAContentProvider != nil {
roots, err = certutil.NewPool(opts.CAFile) // TODO(enj): make this reload CA data dynamically
roots, err = certutil.NewPoolFromBytes(opts.CAContentProvider.CurrentCABundleContent())
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read the CA file: %v", err) return nil, fmt.Errorf("Failed to read the CA contents: %v", err)
} }
} else { } else {
klog.Info("OIDC: No x509 certificates provided, will use host's root CA set") klog.Info("OIDC: No x509 certificates provided, will use host's root CA set")

View File

@ -38,6 +38,7 @@ 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/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
"k8s.io/klog/v2" "k8s.io/klog/v2"
) )
@ -254,7 +255,11 @@ func (c *claimsTest) run(t *testing.T) {
// by writing its root CA certificate into a temporary file. // by writing its root CA certificate into a temporary file.
tempFileName := writeTempCert(t, ts.TLS.Certificates[0].Certificate[0]) tempFileName := writeTempCert(t, ts.TLS.Certificates[0].Certificate[0])
defer os.Remove(tempFileName) defer os.Remove(tempFileName)
c.options.CAFile = tempFileName caContent, err := dynamiccertificates.NewDynamicCAContentFromFile("oidc-authenticator", tempFileName)
if err != nil {
t.Fatalf("initialize ca: %v", err)
}
c.options.CAContentProvider = caContent
// Allow claims to refer to the serving URL of the test server. For this, // Allow claims to refer to the serving URL of the test server. For this,
// substitute all references to {{.URL}} in appropriate places. // substitute all references to {{.URL}} in appropriate places.