ContainerImage.Pinniped/internal/controller/supervisorconfig/jwks_observer.go
2020-10-16 17:51:40 -07:00

95 lines
3.3 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"
"k8s.io/klog/v2"
"go.pinniped.dev/generated/1.19/client/informers/externalversions/config/v1alpha1"
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controllerlib"
)
type jwksObserverController struct {
issuerToJWKSSetter IssuerToJWKSMapSetter
oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer
secretInformer corev1informers.SecretInformer
}
type IssuerToJWKSMapSetter interface {
SetIssuerToJWKSMap(issuerToJWKSMap map[string]*jose.JSONWebKeySet)
}
// Returns a controller which watches all of the OIDCProviderConfigs 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,
oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
) controllerlib.Controller {
return controllerlib.New(
controllerlib.Config{
Name: "certs-observer-controller",
Syncer: &jwksObserverController{
issuerToJWKSSetter: issuerToJWKSSetter,
oidcProviderConfigInformer: oidcProviderConfigInformer,
secretInformer: secretInformer,
},
},
withInformer(
secretInformer,
pinnipedcontroller.MatchAnythingFilter(),
controllerlib.InformerOption{},
),
withInformer(
oidcProviderConfigInformer,
pinnipedcontroller.MatchAnythingFilter(),
controllerlib.InformerOption{},
),
)
}
func (c *jwksObserverController) Sync(ctx controllerlib.Context) error {
ns := ctx.Key.Namespace
allProviders, err := c.oidcProviderConfigInformer.Lister().OIDCProviderConfigs(ns).List(labels.Everything())
if err != nil {
return fmt.Errorf("failed to list OIDCProviderConfigs: %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{}
for _, provider := range allProviders {
secretRef := provider.Status.JWKSSecret
jwksSecret, err := c.secretInformer.Lister().Secrets(ns).Get(secretRef.Name)
if err != nil {
klog.InfoS("jwksObserverController Sync could not find JWKS secret", "namespace", ns, "secretName", secretRef.Name)
continue
}
jwkFromSecret := jose.JSONWebKeySet{}
err = json.Unmarshal(jwksSecret.Data[jwksKey], &jwkFromSecret)
if err != nil {
klog.InfoS("jwksObserverController Sync found a JWKS secret with Data in an unexpected format", "namespace", ns, "secretName", secretRef.Name)
continue
}
issuerToJWKSMap[provider.Spec.Issuer] = &jwkFromSecret
}
klog.InfoS("jwksObserverController Sync updated the JWKS cache", "issuerCount", len(issuerToJWKSMap))
c.issuerToJWKSSetter.SetIssuerToJWKSMap(issuerToJWKSMap)
return nil
}