95 lines
3.3 KiB
Go
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
|
||
|
}
|