ContainerImage.Pinniped/internal/controller/supervisorconfig/jwks_observer.go
Andrew Keesler 58237d0e7d
WIP: start to wire signing key into token handler
This commit includes a failing test (amongst other compiler failures) for the
dynamic signing key fetcher that we will inject into fosite. We are checking it
in so that we can pass the WIP off.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2020-12-03 15:37:25 -05:00

115 lines
3.8 KiB
Go

// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package supervisorconfig
import (
"encoding/json"
"fmt"
"gopkg.in/square/go-jose.v2"
"k8s.io/apimachinery/pkg/labels"
corev1informers "k8s.io/client-go/informers/core/v1"
"go.pinniped.dev/generated/1.19/client/supervisor/informers/externalversions/config/v1alpha1"
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/plog"
)
type jwksObserverController struct {
issuerToJWKSSetter IssuerToJWKSMapSetter
oidcProviderInformer v1alpha1.OIDCProviderInformer
secretInformer corev1informers.SecretInformer
}
type IssuerToJWKSMapSetter interface {
SetIssuerToJWKSMap(
issuerToJWKSMap map[string]*jose.JSONWebKeySet,
issuerToActiveJWKMap map[string]*jose.JSONWebKey,
)
}
// Returns a controller which watches all of the OIDCProviders and their corresponding Secrets
// and fills an in-memory cache of the JWKS info for each currently configured issuer.
// This controller assumes that the informers passed to it are already scoped down to the
// appropriate namespace. It also assumes that the IssuerToJWKSMapSetter passed to it has an
// underlying implementation which is thread-safe.
func NewJWKSObserverController(
issuerToJWKSSetter IssuerToJWKSMapSetter,
secretInformer corev1informers.SecretInformer,
oidcProviderInformer v1alpha1.OIDCProviderInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
) controllerlib.Controller {
return controllerlib.New(
controllerlib.Config{
Name: "jwks-observer-controller",
Syncer: &jwksObserverController{
issuerToJWKSSetter: issuerToJWKSSetter,
oidcProviderInformer: oidcProviderInformer,
secretInformer: secretInformer,
},
},
withInformer(
secretInformer,
pinnipedcontroller.MatchAnythingFilter(nil),
controllerlib.InformerOption{},
),
withInformer(
oidcProviderInformer,
pinnipedcontroller.MatchAnythingFilter(nil),
controllerlib.InformerOption{},
),
)
}
func (c *jwksObserverController) Sync(ctx controllerlib.Context) error {
ns := ctx.Key.Namespace
allProviders, err := c.oidcProviderInformer.Lister().OIDCProviders(ns).List(labels.Everything())
if err != nil {
return fmt.Errorf("failed to list OIDCProviders: %w", err)
}
// Rebuild the whole map on any change to any Secret or OIDCProvider, because either can have changes that
// can cause the map to need to be updated.
issuerToJWKSMap := map[string]*jose.JSONWebKeySet{}
issuerToActiveJWKMap := map[string]*jose.JSONWebKey{}
for _, provider := range allProviders {
secretRef := provider.Status.JWKSSecret
jwksSecret, err := c.secretInformer.Lister().Secrets(ns).Get(secretRef.Name)
if err != nil {
plog.Debug("jwksObserverController Sync could not find JWKS secret", "namespace", ns, "secretName", secretRef.Name)
continue
}
jwksFromSecret := jose.JSONWebKeySet{}
err = json.Unmarshal(jwksSecret.Data[jwksKey], &jwksFromSecret)
if err != nil {
plog.Debug("jwksObserverController Sync found a JWKS secret with Data in an unexpected format", "namespace", ns, "secretName", secretRef.Name)
continue
}
activeJWKFromSecret := jose.JSONWebKey{}
err = json.Unmarshal(jwksSecret.Data[activeJWKKey], &activeJWKFromSecret)
if err != nil {
plog.Debug("jwksObserverController Sync found an active JWK secret with Data in an unexpected format", "namespace", ns, "secretName", secretRef.Name)
continue
}
issuerToJWKSMap[provider.Spec.Issuer] = &jwksFromSecret
issuerToActiveJWKMap[provider.Spec.Issuer] = &activeJWKFromSecret
}
plog.Debug(
"jwksObserverController Sync updated the JWKS cache",
"issuerJWKSCount",
len(issuerToJWKSMap),
"issuerActiveJWKCount",
len(issuerToActiveJWKMap),
)
c.issuerToJWKSSetter.SetIssuerToJWKSMap(issuerToJWKSMap, issuerToActiveJWKMap)
return nil
}