Restrict which packages are aware of the keys used in Pinniped-generated certificate secrets

This commit is contained in:
Joshua Casey
2024-12-03 12:25:54 -06:00
committed by Joshua Casey
parent 2e996aaecd
commit e6a0f94f8f
15 changed files with 196 additions and 39 deletions
@@ -164,6 +164,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
apicerts.NewAPIServiceUpdaterController(
c.ServerInstallationInfo.Namespace,
c.NamesConfig.ServingCertificateSecret,
apicerts.RetrieveCAFromSecret,
loginConciergeGroupData.APIServiceName(),
client.Aggregation,
informers.installationNamespaceK8s.Core().V1().Secrets(),
@@ -175,6 +176,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
apicerts.NewAPIServiceUpdaterController(
c.ServerInstallationInfo.Namespace,
c.NamesConfig.ServingCertificateSecret,
apicerts.RetrieveCAFromSecret,
identityConciergeGroupData.APIServiceName(),
client.Aggregation,
informers.installationNamespaceK8s.Core().V1().Secrets(),
@@ -186,6 +188,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
apicerts.NewCertsObserverController(
c.ServerInstallationInfo.Namespace,
c.NamesConfig.ServingCertificateSecret,
apicerts.RetrieveCertificateFromSecret,
c.DynamicServingCertProvider,
informers.installationNamespaceK8s.Core().V1().Secrets(),
controllerlib.WithInformer,
@@ -200,7 +203,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
informers.installationNamespaceK8s.Core().V1().Secrets(),
controllerlib.WithInformer,
c.ServingCertRenewBefore,
apicerts.TLSCertificateChainSecretKey,
apicerts.RetrieveCertificateFromSecret,
plog.New(),
),
singletonWorker,
@@ -293,6 +296,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
clock.RealClock{},
impersonator.New,
c.NamesConfig.ImpersonationSignerSecret,
apicerts.RetrieveCAFromSecret,
c.ImpersonationSigningCertProvider,
plog.New(),
c.ImpersonationProxyTokenCache,
@@ -322,7 +326,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
informers.installationNamespaceK8s.Core().V1().Secrets(),
controllerlib.WithInformer,
365*24*time.Hour-time.Hour, // 1 year minus 1 hour hard coded value (i.e. wait until the last moment to break the signer)
apicerts.CACertificateSecretKey,
apicerts.RetrieveCAFromSecret,
plog.New(),
),
singletonWorker,
@@ -18,6 +18,7 @@ import (
type apiServiceUpdaterController struct {
namespace string
certsSecretResourceName string
certificateRetriever RetrieveFromSecretFunc
aggregatorClient aggregatorclient.Interface
secretInformer corev1informers.SecretInformer
apiServiceName string
@@ -26,6 +27,7 @@ type apiServiceUpdaterController struct {
func NewAPIServiceUpdaterController(
namespace string,
certsSecretResourceName string,
certificateRetriever RetrieveFromSecretFunc,
apiServiceName string,
aggregatorClient aggregatorclient.Interface,
secretInformer corev1informers.SecretInformer,
@@ -37,6 +39,7 @@ func NewAPIServiceUpdaterController(
Syncer: &apiServiceUpdaterController{
namespace: namespace,
certsSecretResourceName: certsSecretResourceName,
certificateRetriever: certificateRetriever,
aggregatorClient: aggregatorClient,
secretInformer: secretInformer,
apiServiceName: apiServiceName,
@@ -63,13 +66,15 @@ func (c *apiServiceUpdaterController) Sync(ctx controllerlib.Context) error {
return nil
}
caCertPEM, _ := c.certificateRetriever(certSecret)
// Update the APIService to give it the new CA bundle.
if err := UpdateAPIService(
ctx.Context,
c.aggregatorClient,
c.apiServiceName,
c.namespace,
certSecret.Data[CACertificateSecretKey],
caCertPEM,
); err != nil {
return fmt.Errorf("could not update the API service: %w", err)
}
@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package apicerts
@@ -42,6 +42,9 @@ func TestAPIServiceUpdaterControllerOptions(t *testing.T) {
_ = NewAPIServiceUpdaterController(
installedInNamespace,
certsSecretResourceName,
func(secret *corev1.Secret) ([]byte, []byte) {
return secret.Data["some-key-for-ca-certificate"], []byte("this value does not matter")
},
loginv1alpha1.SchemeGroupVersion.Version+"."+loginv1alpha1.GroupName,
nil,
secretsInformer,
@@ -122,6 +125,9 @@ func TestAPIServiceUpdaterControllerSync(t *testing.T) {
subject = NewAPIServiceUpdaterController(
installedInNamespace,
certsSecretResourceName,
func(secret *corev1.Secret) ([]byte, []byte) {
return secret.Data["some-key-for-ca-certificate"], []byte("this value does not matter")
},
loginv1alpha1.SchemeGroupVersion.Version+"."+loginv1alpha1.GroupName,
aggregatorAPIClient,
kubeInformers.Core().V1().Secrets(),
@@ -185,9 +191,9 @@ func TestAPIServiceUpdaterControllerSync(t *testing.T) {
Namespace: installedInNamespace,
},
Data: map[string][]byte{
"caCertificate": []byte("fake CA cert"),
"tlsPrivateKey": []byte("fake private key"),
"tlsCertificateChain": []byte("fake cert chain"),
"some-key-for-ca-certificate": []byte("fake CA cert"),
"serving-cert-key-EXTRA": []byte("fake cert chain"),
"private-key-EXTRA": []byte("fake private key"),
},
}
err := kubeInformerClient.Tracker().Add(apiServingCertSecret)
+12 -6
View File
@@ -19,11 +19,17 @@ import (
"go.pinniped.dev/internal/plog"
)
// The following key names are unexported, to prevent a leaky abstraction.
// Even the string literals should only be used in a very limited set of places:
// - The unit tests for this file
// - The unit tests for retrieve_from_secret.go
// - Integration tests
// Comment must end in a period, so here's a period: .
const (
CACertificateSecretKey = "caCertificate"
CACertificatePrivateKeySecretKey = "caCertificatePrivateKey"
caCertificateSecretKey = "caCertificate"
caCertificatePrivateKeySecretKey = "caCertificatePrivateKey"
tlsCertificateChainSecretKey = "tlsCertificateChain"
tlsPrivateKeySecretKey = "tlsPrivateKey"
TLSCertificateChainSecretKey = "tlsCertificateChain"
)
type certsCreatorController struct {
@@ -111,8 +117,8 @@ func (c *certsCreatorController) Sync(ctx controllerlib.Context) error {
Labels: c.certsSecretLabels,
},
Data: map[string][]byte{
CACertificateSecretKey: ca.Bundle(),
CACertificatePrivateKeySecretKey: caPrivateKeyPEM,
caCertificateSecretKey: ca.Bundle(),
caCertificatePrivateKeySecretKey: caPrivateKeyPEM,
},
}
@@ -132,7 +138,7 @@ func (c *certsCreatorController) Sync(ctx controllerlib.Context) error {
}
secret.Data[tlsPrivateKeySecretKey] = tlsPrivateKeyPEM
secret.Data[TLSCertificateChainSecretKey] = tlsCertChainPEM
secret.Data[tlsCertificateChainSecretKey] = tlsCertChainPEM
}
_, err = c.k8sClient.CoreV1().Secrets(c.namespace).Create(ctx.Context, &secret, metav1.CreateOptions{})
@@ -31,7 +31,7 @@ type certsExpirerController struct {
// this controller will start to try to rotate it.
renewBefore time.Duration
secretKey string
certificateRetriever RetrieveFromSecretFunc
logger plog.Logger
}
@@ -46,7 +46,7 @@ func NewCertsExpirerController(
secretInformer corev1informers.SecretInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
renewBefore time.Duration,
secretKey string,
certificateRetriever RetrieveFromSecretFunc,
logger plog.Logger,
) controllerlib.Controller {
const name = "certs-expirer-controller"
@@ -59,7 +59,7 @@ func NewCertsExpirerController(
k8sClient: k8sClient,
secretInformer: secretInformer,
renewBefore: renewBefore,
secretKey: secretKey,
certificateRetriever: certificateRetriever,
logger: logger.WithName(name),
},
},
@@ -83,7 +83,6 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error {
"controller", ctx.Name,
"namespace", c.namespace,
"name", c.certsSecretResourceName,
"key", c.secretKey,
"renewBefore", c.renewBefore.String(),
)
return nil
@@ -91,7 +90,7 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error {
notBefore, notAfter, err := c.getCertBounds(secret)
if err != nil {
return fmt.Errorf("failed to get cert bounds for secret %q with key %q: %w", secret.Name, c.secretKey, err)
return fmt.Errorf("failed to get cert bounds for secret %q: %w", secret.Name, err)
}
certAge := time.Since(notBefore)
@@ -100,7 +99,6 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error {
"controller", ctx.Name,
"namespace", c.namespace,
"name", c.certsSecretResourceName,
"key", c.secretKey,
"renewBefore", c.renewBefore.String(),
"notBefore", notBefore.String(),
"notAfter", notAfter.String(),
@@ -130,7 +128,7 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error {
// getCertBounds returns the NotBefore and NotAfter fields of the TLS
// certificate in the provided secret, or an error.
func (c *certsExpirerController) getCertBounds(secret *corev1.Secret) (time.Time, time.Time, error) {
certPEM := secret.Data[c.secretKey]
certPEM, _ := c.certificateRetriever(secret)
if certPEM == nil {
return time.Time{}, time.Time{}, constable.Error("failed to find certificate")
}
@@ -102,8 +102,8 @@ func TestExpirerControllerFilters(t *testing.T) {
nil, // k8sClient, not needed
secretsInformer,
withInformer.WithInformer,
0, // renewBefore, not needed
"", // not needed
0, // renewBefore, not needed
nil, // not needed
logger,
)
@@ -134,14 +134,14 @@ func TestExpirerControllerSync(t *testing.T) {
}{
{
name: "secret does not exist",
wantLog: `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"certs-expirer-controller","caller":"apicerts/certs_expirer.go:<line>$apicerts.(*certsExpirerController).Sync","message":"secret does not exist yet or was deleted","controller":"","namespace":"some-namespace","name":"some-resource-name","key":"some-awesome-key","renewBefore":"0s"}`,
wantLog: `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"certs-expirer-controller","caller":"apicerts/certs_expirer.go:<line>$apicerts.(*certsExpirerController).Sync","message":"secret does not exist yet or was deleted","controller":"","namespace":"some-namespace","name":"some-resource-name","renewBefore":"0s"}`,
wantDelete: false,
},
{
name: "secret missing key",
fillSecretData: func(t *testing.T, m map[string][]byte) {},
wantDelete: false,
wantError: `failed to get cert bounds for secret "some-resource-name" with key "some-awesome-key": failed to find certificate`,
wantError: `failed to get cert bounds for secret "some-resource-name": failed to find certificate`,
},
{
name: "lifetime below threshold",
@@ -214,7 +214,7 @@ func TestExpirerControllerSync(t *testing.T) {
require.NoError(t, err)
},
wantDelete: false,
wantError: `failed to get cert bounds for secret "some-resource-name" with key "some-awesome-key": failed to decode certificate PEM`,
wantError: `failed to get cert bounds for secret "some-resource-name": failed to decode certificate PEM`,
},
}
for _, test := range tests {
@@ -265,7 +265,9 @@ func TestExpirerControllerSync(t *testing.T) {
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
test.renewBefore,
fakeTestKey,
func(secret *corev1.Secret) ([]byte, []byte) {
return secret.Data[fakeTestKey], nil
},
logger,
)
@@ -18,6 +18,7 @@ import (
type certsObserverController struct {
namespace string
certsSecretResourceName string
certificateRetriever RetrieveFromSecretFunc
dynamicCertProvider dynamiccert.Private
secretInformer corev1informers.SecretInformer
}
@@ -25,6 +26,7 @@ type certsObserverController struct {
func NewCertsObserverController(
namespace string,
certsSecretResourceName string,
certificateRetriever RetrieveFromSecretFunc,
dynamicCertProvider dynamiccert.Private,
secretInformer corev1informers.SecretInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
@@ -35,6 +37,7 @@ func NewCertsObserverController(
Syncer: &certsObserverController{
namespace: namespace,
certsSecretResourceName: certsSecretResourceName,
certificateRetriever: certificateRetriever,
dynamicCertProvider: dynamicCertProvider,
secretInformer: secretInformer,
},
@@ -62,7 +65,7 @@ func (c *certsObserverController) Sync(_ controllerlib.Context) error {
}
// Mutate the in-memory cert provider to update with the latest cert values.
if err := c.dynamicCertProvider.SetCertKeyContent(certSecret.Data[TLSCertificateChainSecretKey], certSecret.Data[tlsPrivateKeySecretKey]); err != nil {
if err := c.dynamicCertProvider.SetCertKeyContent(c.certificateRetriever(certSecret)); err != nil {
return fmt.Errorf("failed to set serving cert/key content from secret %s/%s: %w", c.namespace, c.certsSecretResourceName, err)
}
@@ -5,6 +5,7 @@ package apicerts
import (
"context"
_ "embed"
"strings"
"testing"
"time"
@@ -23,6 +24,9 @@ import (
"go.pinniped.dev/internal/testutil"
)
//go:embed testdata/private_key_prefix.txt
var privateKeyPrefix string
func TestObserverControllerInformerFilters(t *testing.T) {
spec.Run(t, "informer filters", func(t *testing.T, when spec.G, it spec.S) {
const installedInNamespace = "some-namespace"
@@ -40,6 +44,7 @@ func TestObserverControllerInformerFilters(t *testing.T) {
installedInNamespace,
certsSecretResourceName,
nil,
nil,
secretsInformer,
observableWithInformerOption.WithInformer, // make it possible to observe the behavior of the Filters
)
@@ -119,6 +124,9 @@ func TestObserverControllerSync(t *testing.T) {
subject = NewCertsObserverController(
installedInNamespace,
certsSecretResourceName,
func(secret *corev1.Secret) ([]byte, []byte) {
return secret.Data["some-key-for-certificate"], secret.Data["some-key-for-private-key"]
},
dynamicCertProvider,
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
@@ -202,7 +210,7 @@ func TestObserverControllerSync(t *testing.T) {
ca, err := certauthority.Load(string(caCrt), string(caKey))
require.NoError(t, err)
pem, err := ca.IssueServerCertPEM(nil, nil, time.Hour)
servingCert, err := ca.IssueServerCertPEM(nil, nil, time.Hour)
require.NoError(t, err)
apiServingCertSecret := &corev1.Secret{
@@ -211,9 +219,9 @@ func TestObserverControllerSync(t *testing.T) {
Namespace: installedInNamespace,
},
Data: map[string][]byte{
"caCertificate": []byte("fake cert"),
"tlsPrivateKey": pem.KeyPEM,
"tlsCertificateChain": pem.CertPEM,
"some-pretend-ca-EXTRA": []byte("fake cert"),
"some-key-for-certificate": servingCert.CertPEM,
"some-key-for-private-key": servingCert.KeyPEM,
},
}
err = kubeInformerClient.Tracker().Add(apiServingCertSecret)
@@ -234,7 +242,7 @@ func TestObserverControllerSync(t *testing.T) {
actualCertChain, actualKey = dynamicCertProvider.CurrentCertKeyContent()
r.True(strings.HasPrefix(string(actualCertChain), `-----BEGIN CERTIFICATE-----`), "not a cert:\n%s", string(actualCertChain))
r.True(strings.HasPrefix(string(actualKey), `-----BEGIN PRIVATE KEY-----`), "not a key:\n%s", string(actualKey))
r.True(strings.HasPrefix(string(actualKey), privateKeyPrefix), "not a key:\n%s", string(actualKey))
})
})
@@ -0,0 +1,30 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package apicerts
import (
corev1 "k8s.io/api/core/v1"
)
type RetrieveFromSecretFunc func(secret *corev1.Secret) ([]byte, []byte)
func RetrieveCAFromSecret(secret *corev1.Secret) ([]byte, []byte) {
if secret == nil {
return nil, nil
}
return secret.Data[caCertificateSecretKey], secret.Data[caCertificatePrivateKeySecretKey]
}
func RetrieveCertificateFromSecret(secret *corev1.Secret) ([]byte, []byte) {
if secret == nil {
return nil, nil
}
return secret.Data[tlsCertificateChainSecretKey], secret.Data[tlsPrivateKeySecretKey]
}
// Ensure matching function signature at compile time.
var _ RetrieveFromSecretFunc = RetrieveCAFromSecret
var _ RetrieveFromSecretFunc = RetrieveCertificateFromSecret
@@ -0,0 +1,87 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package apicerts
import (
"testing"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
)
func TestRetrieveCAFromSecret(t *testing.T) {
tests := []struct {
name string
secret *corev1.Secret
wantCertificate []byte
wantPrivateKey []byte
}{
{
name: "nil input returns empty",
secret: nil,
},
{
name: "empty secret returns empty",
secret: &corev1.Secret{},
},
{
name: "populated secret returns values",
secret: &corev1.Secret{
Data: map[string][]byte{
"caCertificate": []byte("foo"),
"caCertificatePrivateKey": []byte("bar"),
"baz": []byte("quz"),
},
},
wantCertificate: []byte("foo"),
wantPrivateKey: []byte("bar"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actualCert, actualKey := RetrieveCAFromSecret(test.secret)
require.Equal(t, test.wantCertificate, actualCert)
require.Equal(t, test.wantPrivateKey, actualKey)
})
}
}
func TestRetrieveCertificateFromSecret(t *testing.T) {
tests := []struct {
name string
secret *corev1.Secret
wantCertificate []byte
wantPrivateKey []byte
}{
{
name: "nil input returns empty",
secret: nil,
},
{
name: "empty secret returns empty",
secret: &corev1.Secret{},
},
{
name: "populated secret returns values",
secret: &corev1.Secret{
Data: map[string][]byte{
"tlsCertificateChain": []byte("foo"),
"tlsPrivateKey": []byte("bar"),
"baz": []byte("quz"),
},
},
wantCertificate: []byte("foo"),
wantPrivateKey: []byte("bar"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actualCert, actualKey := RetrieveCertificateFromSecret(test.secret)
require.Equal(t, test.wantCertificate, actualCert)
require.Equal(t, test.wantPrivateKey, actualKey)
})
}
}
@@ -67,6 +67,7 @@ type impersonatorConfigController struct {
tlsSecretName string
caSecretName string
impersonationSignerSecretName string
impersonationSignerCertRetriever apicerts.RetrieveFromSecretFunc
k8sClient kubernetes.Interface
pinnipedAPIClient conciergeclientset.Interface
@@ -107,6 +108,7 @@ func NewImpersonatorConfigController(
clock clock.Clock,
impersonatorFunc impersonator.FactoryFunc,
impersonationSignerSecretName string,
impersonationSignerCertRetriever apicerts.RetrieveFromSecretFunc,
impersonationSigningCertProvider dynamiccert.Provider,
log plog.Logger,
impersonationProxyTokenCache tokenclient.ExpiringSingletonTokenCacheGet,
@@ -125,6 +127,7 @@ func NewImpersonatorConfigController(
tlsSecretName: tlsSecretName,
caSecretName: caSecretName,
impersonationSignerSecretName: impersonationSignerSecretName,
impersonationSignerCertRetriever: impersonationSignerCertRetriever,
k8sClient: k8sClient,
pinnipedAPIClient: pinnipedAPIClient,
credIssuerInformer: credentialIssuerInformer,
@@ -1116,8 +1119,7 @@ func (c *impersonatorConfigController) loadSignerCA() error {
return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err)
}
certPEM := signingCertSecret.Data[apicerts.CACertificateSecretKey]
keyPEM := signingCertSecret.Data[apicerts.CACertificatePrivateKeySecretKey]
certPEM, keyPEM := c.impersonationSignerCertRetriever(signingCertSecret)
if err := c.impersonationSigningCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil {
return fmt.Errorf("could not set the impersonator's credential signing secret: %w", err)
@@ -39,7 +39,6 @@ import (
conciergefake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
conciergeinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/controller/apicerts"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/dynamiccert"
"go.pinniped.dev/internal/kubeclient"
@@ -95,6 +94,7 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
nil,
caSignerName,
nil,
nil,
logger,
nil,
)
@@ -588,6 +588,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
clocktesting.NewFakeClock(frozenNow),
impersonatorFunc,
mTLSClientCertCASecretName,
func(secret *corev1.Secret) ([]byte, []byte) {
return secret.Data["some-key-for-ca-certificate"], secret.Data["some-key-for-ca-private-key"]
},
mTLSClientCertProvider,
logger,
fakeExpiringSingletonTokenCacheGet,
@@ -675,8 +678,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var newSigningKeySecret = func(resourceName string, certPEM, keyPEM []byte) *corev1.Secret {
return newSecretWithData(resourceName, map[string][]byte{
apicerts.CACertificateSecretKey: certPEM,
apicerts.CACertificatePrivateKeySecretKey: keyPEM,
"some-key-for-ca-certificate": certPEM,
"some-key-for-ca-private-key": keyPEM,
})
}
@@ -4095,7 +4098,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
when("the cert is invalid", func() {
it.Before(func() {
mTLSClientCertCASecret.Data[apicerts.CACertificateSecretKey] = []byte("not a valid PEM formatted cert")
mTLSClientCertCASecret.Data["some-key-for-ca-certificate"] = []byte("not a valid PEM formatted cert")
addSecretToTrackers(mTLSClientCertCASecret, kubeInformerClient)
})
@@ -313,6 +313,7 @@ func startControllers(
apicerts.NewCertsObserverController(
namespace,
certsSecretResourceName,
apicerts.RetrieveCertificateFromSecret,
dynamicCertProvider,
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
+3 -1
View File
@@ -367,6 +367,7 @@ func prepareControllers(
apicerts.NewAPIServiceUpdaterController(
podInfo.Namespace,
certificateName,
apicerts.RetrieveCAFromSecret,
clientSecretSupervisorGroupData.APIServiceName(),
aggregatorClient,
secretInformer,
@@ -378,6 +379,7 @@ func prepareControllers(
apicerts.NewCertsObserverController(
podInfo.Namespace,
certificateName,
apicerts.RetrieveCertificateFromSecret,
dynamicServingCertProvider,
secretInformer,
controllerlib.WithInformer,
@@ -392,7 +394,7 @@ func prepareControllers(
secretInformer,
controllerlib.WithInformer,
9*30*24*time.Hour, // about 9 months
apicerts.TLSCertificateChainSecretKey,
apicerts.RetrieveCertificateFromSecret,
plog.New(),
),
singletonWorker,