mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-20 06:54:20 +00:00
Merge remote-tracking branch 'origin' into active-directory-identity-provider
This commit is contained in:
@@ -90,7 +90,12 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error {
|
||||
err := c.k8sClient.
|
||||
CoreV1().
|
||||
Secrets(c.namespace).
|
||||
Delete(ctx.Context, c.certsSecretResourceName, metav1.DeleteOptions{})
|
||||
Delete(ctx.Context, c.certsSecretResourceName, metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &secret.UID,
|
||||
ResourceVersion: &secret.ResourceVersion,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
// Do return an error here so that the controller library will reschedule
|
||||
// us to try deleting this cert again.
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
kubernetesfake "k8s.io/client-go/kubernetes/fake"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
@@ -223,14 +224,19 @@ func TestExpirerControllerSync(t *testing.T) {
|
||||
test.configKubeAPIClient(kubeAPIClient)
|
||||
}
|
||||
|
||||
testRV := "rv_001"
|
||||
testUID := types.UID("uid_002")
|
||||
|
||||
kubeInformerClient := kubernetesfake.NewSimpleClientset()
|
||||
name := certsSecretResourceName
|
||||
namespace := "some-namespace"
|
||||
if test.fillSecretData != nil {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
ResourceVersion: testRV,
|
||||
UID: testUID,
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
}
|
||||
@@ -245,10 +251,13 @@ func TestExpirerControllerSync(t *testing.T) {
|
||||
0,
|
||||
)
|
||||
|
||||
opts := &[]metav1.DeleteOptions{}
|
||||
trackDeleteClient := testutil.NewDeleteOptionsRecorder(kubeAPIClient, opts)
|
||||
|
||||
c := NewCertsExpirerController(
|
||||
namespace,
|
||||
certsSecretResourceName,
|
||||
kubeAPIClient,
|
||||
trackDeleteClient,
|
||||
kubeInformers.Core().V1().Secrets(),
|
||||
controllerlib.WithInformer,
|
||||
test.renewBefore,
|
||||
@@ -285,6 +294,18 @@ func TestExpirerControllerSync(t *testing.T) {
|
||||
}
|
||||
acActions := kubeAPIClient.Actions()
|
||||
require.Equal(t, exActions, acActions)
|
||||
|
||||
if test.wantDelete {
|
||||
require.Len(t, *opts, 1)
|
||||
require.Equal(t, metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &testUID,
|
||||
ResourceVersion: &testRV,
|
||||
},
|
||||
}, (*opts)[0])
|
||||
} else {
|
||||
require.Len(t, *opts, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@ package jwtcachefiller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
@@ -151,22 +150,13 @@ func newJWTAuthenticator(spec *auth1alpha1.JWTAuthenticatorSpec) (*jwtAuthentica
|
||||
return nil, fmt.Errorf("invalid TLS configuration: %w", err)
|
||||
}
|
||||
|
||||
var caFile string
|
||||
if caBundle != nil {
|
||||
temp, err := ioutil.TempFile("", "pinniped-jwkauthenticator-cafile-*")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create temporary file: %w", err)
|
||||
var caContentProvider oidc.CAContentProvider
|
||||
if len(caBundle) != 0 {
|
||||
var caContentProviderErr error
|
||||
caContentProvider, caContentProviderErr = dynamiccertificates.NewStaticCAContent("ignored", caBundle)
|
||||
if caContentProviderErr != nil {
|
||||
return nil, caContentProviderErr // impossible since caBundle is validated already
|
||||
}
|
||||
|
||||
// We can safely remove the temp file at the end of this function since oidc.New() reads the
|
||||
// provided CA file and then forgets about it.
|
||||
defer func() { _ = os.Remove(temp.Name()) }()
|
||||
|
||||
if _, err := temp.Write(caBundle); err != nil {
|
||||
return nil, fmt.Errorf("cannot write CA file: %w", err)
|
||||
}
|
||||
|
||||
caFile = temp.Name()
|
||||
}
|
||||
usernameClaim := spec.Claims.Username
|
||||
if usernameClaim == "" {
|
||||
@@ -183,7 +173,7 @@ func newJWTAuthenticator(spec *auth1alpha1.JWTAuthenticatorSpec) (*jwtAuthentica
|
||||
UsernameClaim: usernameClaim,
|
||||
GroupsClaim: groupsClaim,
|
||||
SupportedSigningAlgs: defaultSupportedSigningAlgos(),
|
||||
CAFile: caFile,
|
||||
CAContentProvider: caContentProvider,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not initialize authenticator: %w", err)
|
||||
|
||||
@@ -143,7 +143,15 @@ func NewImpersonatorConfigController(
|
||||
withInformer(
|
||||
servicesInformer,
|
||||
pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool {
|
||||
return obj.GetNamespace() == namespace && obj.GetName() == generatedLoadBalancerServiceName
|
||||
if obj.GetNamespace() != namespace {
|
||||
return false
|
||||
}
|
||||
switch obj.GetName() {
|
||||
case generatedLoadBalancerServiceName, generatedClusterIPServiceName:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
@@ -175,8 +183,6 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error
|
||||
Message: err.Error(),
|
||||
LastUpdateTime: metav1.NewTime(c.clock.Now()),
|
||||
}
|
||||
// The impersonator is not ready, so clear the signer CA from the dynamic provider.
|
||||
c.clearSignerCA()
|
||||
}
|
||||
|
||||
err = utilerrors.NewAggregate([]error{err, issuerconfig.Update(
|
||||
@@ -273,27 +279,32 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context, cre
|
||||
|
||||
nameInfo, err := c.findDesiredTLSCertificateName(impersonationSpec)
|
||||
if err != nil {
|
||||
// Unexpected error while determining the name that should go into the certs, so clear any existing certs.
|
||||
c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var impersonationCA *certauthority.CA
|
||||
if c.shouldHaveTLSSecret(impersonationSpec) {
|
||||
if c.shouldHaveImpersonator(impersonationSpec) {
|
||||
if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = c.ensureTLSSecret(ctx, nameInfo, impersonationCA); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.clearTLSSecret()
|
||||
}
|
||||
|
||||
credentialIssuerStrategyResult := c.doSyncResult(nameInfo, impersonationSpec, impersonationCA)
|
||||
|
||||
if err = c.loadSignerCA(credentialIssuerStrategyResult.Status); err != nil {
|
||||
return nil, err
|
||||
if c.shouldHaveImpersonator(impersonationSpec) {
|
||||
if err = c.loadSignerCA(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
c.clearSignerCA()
|
||||
}
|
||||
|
||||
return credentialIssuerStrategyResult, nil
|
||||
@@ -342,20 +353,16 @@ func (c *impersonatorConfigController) shouldHaveClusterIPService(config *v1alph
|
||||
return c.shouldHaveImpersonator(config) && config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeClusterIP
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) shouldHaveTLSSecret(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return c.shouldHaveImpersonator(config)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) serviceExists(serviceName string) (bool, error) {
|
||||
_, err := c.servicesInformer.Lister().Services(c.namespace).Get(serviceName)
|
||||
func (c *impersonatorConfigController) serviceExists(serviceName string) (bool, *v1.Service, error) {
|
||||
service, err := c.servicesInformer.Lister().Services(c.namespace).Get(serviceName)
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if notFound {
|
||||
return false, nil
|
||||
return false, nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, nil, err
|
||||
}
|
||||
return true, nil
|
||||
return true, service, nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) tlsSecretExists() (bool, *v1.Secret, error) {
|
||||
@@ -469,7 +476,7 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStarted(ctx context.C
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.Context) error {
|
||||
running, err := c.serviceExists(c.generatedLoadBalancerServiceName)
|
||||
running, service, err := c.serviceExists(c.generatedLoadBalancerServiceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -480,7 +487,12 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.C
|
||||
c.infoLog.Info("deleting load balancer for impersonation proxy",
|
||||
"service", klog.KRef(c.namespace, c.generatedLoadBalancerServiceName),
|
||||
)
|
||||
err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{})
|
||||
err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &service.UID,
|
||||
ResourceVersion: &service.ResourceVersion,
|
||||
},
|
||||
})
|
||||
return utilerrors.FilterOut(err, k8serrors.IsNotFound)
|
||||
}
|
||||
|
||||
@@ -509,7 +521,7 @@ func (c *impersonatorConfigController) ensureClusterIPServiceIsStarted(ctx conte
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureClusterIPServiceIsStopped(ctx context.Context) error {
|
||||
running, err := c.serviceExists(c.generatedClusterIPServiceName)
|
||||
running, service, err := c.serviceExists(c.generatedClusterIPServiceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -520,7 +532,12 @@ func (c *impersonatorConfigController) ensureClusterIPServiceIsStopped(ctx conte
|
||||
c.infoLog.Info("deleting cluster ip for impersonation proxy",
|
||||
"service", klog.KRef(c.namespace, c.generatedClusterIPServiceName),
|
||||
)
|
||||
err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedClusterIPServiceName, metav1.DeleteOptions{})
|
||||
err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedClusterIPServiceName, metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &service.UID,
|
||||
ResourceVersion: &service.ResourceVersion,
|
||||
},
|
||||
})
|
||||
return utilerrors.FilterOut(err, k8serrors.IsNotFound)
|
||||
}
|
||||
|
||||
@@ -934,7 +951,6 @@ func (c *impersonatorConfigController) loadTLSCertFromSecret(tlsSecret *v1.Secre
|
||||
keyPEM := tlsSecret.Data[v1.TLSPrivateKeyKey]
|
||||
|
||||
if err := c.tlsServingCertDynamicCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil {
|
||||
c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent()
|
||||
return fmt.Errorf("could not parse TLS cert PEM data from Secret: %w", err)
|
||||
}
|
||||
|
||||
@@ -947,39 +963,33 @@ func (c *impersonatorConfigController) loadTLSCertFromSecret(tlsSecret *v1.Secre
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureTLSSecretIsRemoved(ctx context.Context) error {
|
||||
tlsSecretExists, _, err := c.tlsSecretExists()
|
||||
tlsSecretExists, secret, err := c.tlsSecretExists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !tlsSecretExists {
|
||||
return nil
|
||||
}
|
||||
c.infoLog.Info("deleting TLS certificates for impersonation proxy",
|
||||
c.infoLog.Info("deleting TLS serving certificate for impersonation proxy",
|
||||
"secret", klog.KRef(c.namespace, c.tlsSecretName),
|
||||
)
|
||||
err = c.k8sClient.CoreV1().Secrets(c.namespace).Delete(ctx, c.tlsSecretName, metav1.DeleteOptions{})
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if notFound {
|
||||
// its okay if we tried to delete and we got a not found error. This probably means
|
||||
// another instance of the concierge got here first so there's nothing to delete.
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent()
|
||||
|
||||
return nil
|
||||
err = c.k8sClient.CoreV1().Secrets(c.namespace).Delete(ctx, c.tlsSecretName, metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &secret.UID,
|
||||
ResourceVersion: &secret.ResourceVersion,
|
||||
},
|
||||
})
|
||||
// it is okay if we tried to delete and we got a not found error. This probably means
|
||||
// another instance of the concierge got here first so there's nothing to delete.
|
||||
return utilerrors.FilterOut(err, k8serrors.IsNotFound)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) loadSignerCA(status v1alpha1.StrategyStatus) error {
|
||||
// Clear it when the impersonator is not completely ready.
|
||||
if status != v1alpha1.SuccessStrategyStatus {
|
||||
c.clearSignerCA()
|
||||
return nil
|
||||
}
|
||||
func (c *impersonatorConfigController) clearTLSSecret() {
|
||||
c.debugLog.Info("clearing TLS serving certificate for impersonation proxy")
|
||||
c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent()
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) loadSignerCA() error {
|
||||
signingCertSecret, err := c.secretsInformer.Lister().Secrets(c.namespace).Get(c.impersonationSignerSecretName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err)
|
||||
@@ -989,7 +999,7 @@ func (c *impersonatorConfigController) loadSignerCA(status v1alpha1.StrategyStat
|
||||
keyPEM := signingCertSecret.Data[apicerts.CACertificatePrivateKeySecretKey]
|
||||
|
||||
if err := c.impersonationSigningCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil {
|
||||
return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err)
|
||||
return fmt.Errorf("could not set the impersonator's credential signing secret: %w", err)
|
||||
}
|
||||
|
||||
c.infoLog.Info("loading credential signing certificate for impersonation proxy",
|
||||
|
||||
@@ -29,11 +29,14 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubernetesfake "k8s.io/client-go/kubernetes/fake"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
@@ -131,11 +134,12 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
|
||||
|
||||
when("watching Service objects", func() {
|
||||
var subject controllerlib.Filter
|
||||
var target, wrongNamespace, wrongName, unrelated *corev1.Service
|
||||
var targetLBService, targetClusterIPService, wrongNamespace, wrongName, unrelated *corev1.Service
|
||||
|
||||
it.Before(func() {
|
||||
subject = servicesInformerFilter
|
||||
target = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: installedInNamespace}}
|
||||
targetLBService = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: installedInNamespace}}
|
||||
targetClusterIPService = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedClusterIPServiceName, Namespace: installedInNamespace}}
|
||||
wrongNamespace = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: "wrong-namespace"}}
|
||||
wrongName = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "wrong-name", Namespace: installedInNamespace}}
|
||||
unrelated = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "wrong-name", Namespace: "wrong-namespace"}}
|
||||
@@ -143,10 +147,14 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
|
||||
|
||||
when("the target Service changes", func() {
|
||||
it("returns true to trigger the sync method", func() {
|
||||
r.True(subject.Add(target))
|
||||
r.True(subject.Update(target, unrelated))
|
||||
r.True(subject.Update(unrelated, target))
|
||||
r.True(subject.Delete(target))
|
||||
r.True(subject.Add(targetLBService))
|
||||
r.True(subject.Update(targetLBService, unrelated))
|
||||
r.True(subject.Update(unrelated, targetLBService))
|
||||
r.True(subject.Delete(targetLBService))
|
||||
r.True(subject.Add(targetClusterIPService))
|
||||
r.True(subject.Update(targetClusterIPService, unrelated))
|
||||
r.True(subject.Update(unrelated, targetClusterIPService))
|
||||
r.True(subject.Delete(targetClusterIPService))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -261,6 +269,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
var subject controllerlib.Controller
|
||||
var kubeAPIClient *kubernetesfake.Clientset
|
||||
var deleteOptions *[]metav1.DeleteOptions
|
||||
var deleteOptionsRecorder kubernetes.Interface
|
||||
var pinnipedAPIClient *pinnipedfake.Clientset
|
||||
var pinnipedInformerClient *pinnipedfake.Clientset
|
||||
var pinnipedInformers pinnipedinformers.SharedInformerFactory
|
||||
@@ -270,6 +280,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
var cancelContextCancelFunc context.CancelFunc
|
||||
var syncContext *controllerlib.Context
|
||||
var frozenNow time.Time
|
||||
var tlsServingCertDynamicCertProvider dynamiccert.Private
|
||||
var signingCertProvider dynamiccert.Provider
|
||||
var signingCACertPEM, signingCAKeyPEM []byte
|
||||
var signingCASecret *corev1.Secret
|
||||
@@ -413,6 +424,20 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var requireTLSSecretProviderHasLoadedCerts = func() {
|
||||
actualCert, actualKey := tlsServingCertDynamicCertProvider.CurrentCertKeyContent()
|
||||
r.NotEmpty(actualCert)
|
||||
r.NotEmpty(actualKey)
|
||||
_, err := tls.X509KeyPair(actualCert, actualKey)
|
||||
r.NoError(err)
|
||||
}
|
||||
|
||||
var requireTLSSecretProviderIsEmpty = func() {
|
||||
actualCert, actualKey := tlsServingCertDynamicCertProvider.CurrentCertKeyContent()
|
||||
r.Nil(actualCert)
|
||||
r.Nil(actualKey)
|
||||
}
|
||||
|
||||
var requireTLSServerIsRunning = func(caCrt []byte, addr string, dnsOverrides map[string]string) {
|
||||
r.Greater(impersonatorFuncWasCalled, 0)
|
||||
|
||||
@@ -464,6 +489,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.NoError(resp.Body.Close())
|
||||
r.NoError(err)
|
||||
r.Equal(fakeServerResponseBody, string(body))
|
||||
|
||||
requireTLSSecretProviderHasLoadedCerts()
|
||||
}
|
||||
|
||||
var requireTLSServerIsRunningWithoutCerts = func() {
|
||||
@@ -485,6 +512,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
}, 20*time.Second, 50*time.Millisecond)
|
||||
r.Error(err)
|
||||
r.Regexp(expectedErrorRegex, err.Error())
|
||||
|
||||
requireTLSSecretProviderIsEmpty()
|
||||
}
|
||||
|
||||
var requireTLSServerIsNoLongerRunning = func() {
|
||||
@@ -503,10 +532,14 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
}, 20*time.Second, 50*time.Millisecond)
|
||||
r.Error(err)
|
||||
r.Regexp(expectedErrorRegex, err.Error())
|
||||
|
||||
requireTLSSecretProviderIsEmpty()
|
||||
}
|
||||
|
||||
var requireTLSServerWasNeverStarted = func() {
|
||||
r.Equal(0, impersonatorFuncWasCalled)
|
||||
|
||||
requireTLSSecretProviderIsEmpty()
|
||||
}
|
||||
|
||||
// Defer starting the informers until the last possible moment so that the
|
||||
@@ -516,7 +549,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
subject = NewImpersonatorConfigController(
|
||||
installedInNamespace,
|
||||
credentialIssuerResourceName,
|
||||
kubeAPIClient,
|
||||
deleteOptionsRecorder,
|
||||
pinnipedAPIClient,
|
||||
pinnipedInformers.Config().V1alpha1().CredentialIssuers(),
|
||||
kubeInformers.Core().V1().Services(),
|
||||
@@ -533,6 +566,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
signingCertProvider,
|
||||
testLog,
|
||||
)
|
||||
controllerlib.TestWrap(t, subject, func(syncer controllerlib.Syncer) controllerlib.Syncer {
|
||||
tlsServingCertDynamicCertProvider = syncer.(*impersonatorConfigController).tlsServingCertDynamicCertProvider
|
||||
return syncer
|
||||
})
|
||||
|
||||
// Set this at the last second to support calling subject.Name().
|
||||
syncContext = &controllerlib.Context{
|
||||
@@ -559,8 +596,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
var newSecretWithData = func(resourceName string, data map[string][]byte) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: installedInNamespace,
|
||||
Name: resourceName,
|
||||
Namespace: installedInNamespace,
|
||||
UID: "uid-1234", // simulate KAS filling out UID and RV
|
||||
ResourceVersion: "rv-5678",
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
@@ -700,6 +739,14 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
var addObjectFromCreateActionToInformerAndWait = func(action coretesting.Action, informer controllerlib.InformerGetter) {
|
||||
createdObject, ok := action.(coretesting.CreateAction).GetObject().(kubeclient.Object)
|
||||
r.True(ok, "should have been able to cast this action's object to kubeclient.Object: %v", action)
|
||||
|
||||
if secret, ok := createdObject.(*corev1.Secret); ok && len(secret.ResourceVersion) == 0 {
|
||||
secret = secret.DeepCopy()
|
||||
secret.UID = "uid-1234" // simulate KAS filling out UID and RV
|
||||
secret.ResourceVersion = "rv-5678"
|
||||
createdObject = secret
|
||||
}
|
||||
|
||||
addObjectToKubeInformerAndWait(createdObject, informer)
|
||||
}
|
||||
|
||||
@@ -981,6 +1028,18 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Equal("delete", deleteAction.GetVerb())
|
||||
r.Equal(tlsSecretName, deleteAction.GetName())
|
||||
r.Equal("secrets", deleteAction.GetResource().Resource)
|
||||
|
||||
// validate that we set delete preconditions correctly
|
||||
r.NotEmpty(*deleteOptions)
|
||||
for _, opt := range *deleteOptions {
|
||||
uid := types.UID("uid-1234")
|
||||
r.Equal(metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &uid,
|
||||
ResourceVersion: pointer.String("rv-5678"),
|
||||
},
|
||||
}, opt)
|
||||
}
|
||||
}
|
||||
|
||||
var requireCASecretWasCreated = func(action coretesting.Action) []byte {
|
||||
@@ -1059,6 +1118,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
kubeinformers.WithNamespace(installedInNamespace),
|
||||
)
|
||||
kubeAPIClient = kubernetesfake.NewSimpleClientset()
|
||||
deleteOptions = &[]metav1.DeleteOptions{}
|
||||
deleteOptionsRecorder = testutil.NewDeleteOptionsRecorder(kubeAPIClient, deleteOptions)
|
||||
pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
|
||||
frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local)
|
||||
signingCertProvider = dynamiccert.NewCA(name)
|
||||
@@ -1217,7 +1278,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1236,7 +1297,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireNodesListed(kubeAPIClient.Actions()[0])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1255,7 +1316,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireNodesListed(kubeAPIClient.Actions()[0])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1446,10 +1507,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
errString := "could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name"
|
||||
r.EqualError(runControllerSync(), errString)
|
||||
r.Len(kubeAPIClient.Actions(), 1) // no new actions
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
r.Len(kubeAPIClient.Actions(), 1) // no new actions
|
||||
requireTLSServerIsRunning(caCrt, testServerAddr(), nil) // serving certificate is not unloaded in this case
|
||||
requireCredentialIssuer(newErrorStrategy(errString))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1505,7 +1566,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
|
||||
it("returns an error when the impersonation TLS server fails to start", func() {
|
||||
@@ -1540,7 +1601,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
|
||||
it("returns an error when the impersonation TLS server fails to start", func() {
|
||||
@@ -1680,7 +1741,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1775,7 +1836,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
// Check that the server is running without certs.
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2124,7 +2185,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 4)
|
||||
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3]) // tried to delete cert but failed
|
||||
requireCredentialIssuer(newErrorStrategy("error on tls secret delete"))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2155,7 +2216,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load when enabled
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2173,7 +2234,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 4)
|
||||
requireServiceWasDeleted(kubeAPIClient.Actions()[3], loadBalancerServiceName)
|
||||
requireCredentialIssuer(newManuallyDisabledStrategy())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderIsEmpty() // only unload when disabled
|
||||
|
||||
deleteServiceFromTracker(loadBalancerServiceName, kubeInformerClient)
|
||||
waitForObjectToBeDeletedFromInformer(loadBalancerServiceName, kubeInformers.Core().V1().Services())
|
||||
@@ -2190,7 +2251,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 5)
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[4])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load again when enabled
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2221,7 +2282,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireClusterIPWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load when enabled
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2239,7 +2300,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 4)
|
||||
requireServiceWasDeleted(kubeAPIClient.Actions()[3], clusterIPServiceName)
|
||||
requireCredentialIssuer(newManuallyDisabledStrategy())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderIsEmpty() // only unload when disabled
|
||||
|
||||
deleteServiceFromTracker(clusterIPServiceName, kubeInformerClient)
|
||||
waitForObjectToBeDeletedFromInformer(clusterIPServiceName, kubeInformers.Core().V1().Services())
|
||||
@@ -2259,7 +2320,88 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 5)
|
||||
requireClusterIPWasCreated(kubeAPIClient.Actions()[4])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load again when enabled
|
||||
})
|
||||
})
|
||||
|
||||
when("service type none with a hostname", func() {
|
||||
const fakeHostname = "hello.com"
|
||||
it.Before(func() {
|
||||
addSecretToTrackers(signingCASecret, kubeInformerClient)
|
||||
addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: credentialIssuerResourceName},
|
||||
Spec: v1alpha1.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{
|
||||
Mode: v1alpha1.ImpersonationProxyModeEnabled,
|
||||
ExternalEndpoint: fakeHostname,
|
||||
Service: v1alpha1.ImpersonationProxyServiceSpec{
|
||||
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, pinnipedInformerClient, pinnipedAPIClient)
|
||||
addNodeWithRoleToTracker("worker", kubeAPIClient)
|
||||
})
|
||||
|
||||
it("starts the impersonator, then shuts it down, then starts it again", func() {
|
||||
startInformersAndController()
|
||||
|
||||
r.NoError(runControllerSync())
|
||||
r.Len(kubeAPIClient.Actions(), 3)
|
||||
requireNodesListed(kubeAPIClient.Actions()[0])
|
||||
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca)
|
||||
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
|
||||
requireCredentialIssuer(newSuccessStrategy(fakeHostname, ca))
|
||||
|
||||
// load when enabled
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
requireTLSSecretProviderHasLoadedCerts()
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Secrets())
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[2], kubeInformers.Core().V1().Secrets())
|
||||
|
||||
// Update the CredentialIssuer.
|
||||
updateCredentialIssuerInInformerAndWait(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{
|
||||
Mode: v1alpha1.ImpersonationProxyModeDisabled,
|
||||
},
|
||||
}, pinnipedInformers.Config().V1alpha1().CredentialIssuers())
|
||||
|
||||
r.NoError(runControllerSync())
|
||||
requireTLSServerIsNoLongerRunning()
|
||||
r.Len(kubeAPIClient.Actions(), 4)
|
||||
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3])
|
||||
requireCredentialIssuer(newManuallyDisabledStrategy())
|
||||
|
||||
// only unload when disabled
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireTLSSecretProviderIsEmpty()
|
||||
|
||||
deleteSecretFromTracker(tlsSecretName, kubeInformerClient)
|
||||
waitForObjectToBeDeletedFromInformer(tlsSecretName, kubeInformers.Core().V1().Secrets())
|
||||
|
||||
// Update the CredentialIssuer again.
|
||||
updateCredentialIssuerInInformerAndWait(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{
|
||||
Mode: v1alpha1.ImpersonationProxyModeEnabled,
|
||||
ExternalEndpoint: fakeHostname,
|
||||
Service: v1alpha1.ImpersonationProxyServiceSpec{
|
||||
Type: v1alpha1.ImpersonationProxyServiceTypeNone,
|
||||
},
|
||||
},
|
||||
}, pinnipedInformers.Config().V1alpha1().CredentialIssuers())
|
||||
|
||||
r.NoError(runControllerSync())
|
||||
requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()})
|
||||
r.Len(kubeAPIClient.Actions(), 5)
|
||||
requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], ca)
|
||||
requireCredentialIssuer(newSuccessStrategy(fakeHostname, ca))
|
||||
|
||||
// load again when enabled
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
requireTLSSecretProviderHasLoadedCerts()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2310,9 +2452,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Len(kubeAPIClient.Actions(), 5)
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[3])
|
||||
requireTLSSecretWasDeleted(kubeAPIClient.Actions()[4]) // the Secret was deleted because it contained a cert with the wrong IP
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireTLSServerIsRunning(ca, testServerAddr(), nil) // serving certificate is not unloaded in this case
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[3], kubeInformers.Core().V1().Services())
|
||||
@@ -2321,10 +2463,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
// The controller should be waiting for the load balancer's ingress to become available.
|
||||
r.NoError(runControllerSync())
|
||||
r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress
|
||||
requireTLSServerIsRunning(ca, testServerAddr(), nil) // serving certificate is not unloaded in this case
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Update the ingress of the LB in the informer's client and run Sync again.
|
||||
fakeIP := "127.0.0.123"
|
||||
@@ -2762,7 +2904,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2772,7 +2914,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.Equal(1, impersonatorFuncWasCalled) // wasn't started a second time
|
||||
requireTLSServerIsRunningWithoutCerts() // still running
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
r.Len(kubeAPIClient.Actions(), 3) // no new API calls
|
||||
})
|
||||
|
||||
@@ -2785,7 +2927,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2822,7 +2964,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2913,6 +3055,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
when("the impersonator start function returned by the impersonatorFunc returns an error immediately", func() {
|
||||
it.Before(func() {
|
||||
addSecretToTrackers(signingCASecret, kubeInformerClient)
|
||||
addNodeWithRoleToTracker("worker", kubeAPIClient)
|
||||
impersonatorFuncReturnedFuncError = errors.New("some immediate impersonator startup error")
|
||||
addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{
|
||||
@@ -2943,7 +3086,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services())
|
||||
@@ -2961,7 +3104,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
// sync should be able to detect the error and return it.
|
||||
r.EqualError(runControllerSync(), "some immediate impersonator startup error")
|
||||
requireCredentialIssuer(newErrorStrategy("some immediate impersonator startup error"))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Next time the controller starts the server, the server will start successfully.
|
||||
impersonatorFuncReturnedFuncError = nil
|
||||
@@ -2971,12 +3114,13 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.NoError(runControllerSync())
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
when("the impersonator server dies for no apparent reason after running for a while", func() {
|
||||
it.Before(func() {
|
||||
addSecretToTrackers(signingCASecret, kubeInformerClient)
|
||||
addNodeWithRoleToTracker("worker", kubeAPIClient)
|
||||
addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: credentialIssuerResourceName},
|
||||
@@ -2999,7 +3143,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1])
|
||||
requireCASecretWasCreated(kubeAPIClient.Actions()[2])
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
|
||||
// Simulate the informer cache's background update from its watch.
|
||||
@@ -3021,7 +3165,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
// sync should be able to detect the error and return it.
|
||||
r.EqualError(runControllerSync(), "unexpected shutdown of proxy server")
|
||||
requireCredentialIssuer(newErrorStrategy("unexpected shutdown of proxy server"))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
|
||||
// Next time the controller starts the server, the server should behave as normal.
|
||||
testHTTPServerInterruptCh = nil
|
||||
@@ -3031,7 +3175,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.NoError(runControllerSync())
|
||||
requireTLSServerIsRunningWithoutCerts()
|
||||
requireCredentialIssuer(newPendingStrategyWaitingForLB())
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3466,16 +3610,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
},
|
||||
}, pinnipedInformerClient, pinnipedAPIClient)
|
||||
addNodeWithRoleToTracker("worker", kubeAPIClient)
|
||||
tlsSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tlsSecretName,
|
||||
Namespace: installedInNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
// "aGVsbG8gd29ybGQK" is "hello world" base64 encoded which is not a valid cert
|
||||
corev1.TLSCertKey: []byte("-----BEGIN CERTIFICATE-----\naGVsbG8gd29ybGQK\n-----END CERTIFICATE-----\n"),
|
||||
},
|
||||
}
|
||||
tlsSecret := newSecretWithData(tlsSecretName, map[string][]byte{
|
||||
// "aGVsbG8gd29ybGQK" is "hello world" base64 encoded which is not a valid cert
|
||||
corev1.TLSCertKey: []byte("-----BEGIN CERTIFICATE-----\naGVsbG8gd29ybGQK\n-----END CERTIFICATE-----\n"),
|
||||
})
|
||||
addSecretToTrackers(tlsSecret, kubeAPIClient, kubeInformerClient)
|
||||
})
|
||||
|
||||
@@ -3700,7 +3838,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
it("returns the error", func() {
|
||||
startInformersAndController()
|
||||
errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
r.EqualError(runControllerSync(), errString)
|
||||
requireCredentialIssuer(newErrorStrategy(errString))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
@@ -3715,7 +3853,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
|
||||
it("returns the error", func() {
|
||||
startInformersAndController()
|
||||
errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
r.EqualError(runControllerSync(), errString)
|
||||
requireCredentialIssuer(newErrorStrategy(errString))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
@@ -3751,10 +3889,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
addSecretToTrackers(updatedSigner, kubeInformerClient)
|
||||
waitForObjectToAppearInInformer(updatedSigner, kubeInformers.Core().V1().Secrets())
|
||||
|
||||
errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input`
|
||||
r.EqualError(runControllerSync(), errString)
|
||||
requireCredentialIssuer(newErrorStrategy(errString))
|
||||
requireSigningCertProviderIsEmpty()
|
||||
requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ package kubecertagent
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -309,22 +310,30 @@ func (c *agentController) loadSigningKey(agentPod *corev1.Pod) error {
|
||||
}
|
||||
|
||||
// Exec into the agent pod and cat out the certificate and the key.
|
||||
combinedPEM, err := c.executor.Exec(
|
||||
agentPod.Namespace, agentPod.Name,
|
||||
"sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}",
|
||||
)
|
||||
outputJSON, err := c.executor.Exec(agentPod.Namespace, agentPod.Name, "pinniped-concierge-kube-cert-agent", "print")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not exec into agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err)
|
||||
}
|
||||
|
||||
// Split up the output by looking for the block of newlines.
|
||||
var certPEM, keyPEM string
|
||||
if parts := strings.Split(combinedPEM, "\n\n\n"); len(parts) == 2 {
|
||||
certPEM, keyPEM = parts[0], parts[1]
|
||||
// Parse and decode the JSON output from the "pinniped-concierge-kube-cert-agent print" command.
|
||||
var output struct {
|
||||
Cert string `json:"tls.crt"`
|
||||
Key string `json:"tls.key"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(outputJSON), &output); err != nil {
|
||||
return fmt.Errorf("failed to decode signing cert/key JSON from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err)
|
||||
}
|
||||
certPEM, err := base64.StdEncoding.DecodeString(output.Cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode signing cert base64 from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err)
|
||||
}
|
||||
keyPEM, err := base64.StdEncoding.DecodeString(output.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode signing key base64 from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err)
|
||||
}
|
||||
|
||||
// Load the certificate and key into the dynamic signer.
|
||||
if err := c.dynamicCertProvider.SetCertKeyContent([]byte(certPEM), []byte(keyPEM)); err != nil {
|
||||
if err := c.dynamicCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil {
|
||||
return fmt.Errorf("failed to set signing cert/key content from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err)
|
||||
}
|
||||
|
||||
@@ -461,7 +470,7 @@ func (c *agentController) newAgentDeployment(controllerManagerPod *corev1.Pod) *
|
||||
Name: "sleeper",
|
||||
Image: c.cfg.ContainerImage,
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
Command: []string{"/bin/sleep", "infinity"},
|
||||
Command: []string{"pinniped-concierge-kube-cert-agent", "sleep"},
|
||||
VolumeMounts: volumeMounts,
|
||||
Env: []corev1.EnvVar{
|
||||
{Name: "CERT_PATH", Value: getContainerArgByName(controllerManagerPod, "cluster-signing-cert-file", "/etc/kubernetes/ca/ca.pem")},
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestAgentController(t *testing.T) {
|
||||
Containers: []corev1.Container{{
|
||||
Name: "sleeper",
|
||||
Image: "pinniped-server-image",
|
||||
Command: []string{"/bin/sleep", "infinity"},
|
||||
Command: []string{"pinniped-concierge-kube-cert-agent", "sleep"},
|
||||
Env: []corev1.EnvVar{
|
||||
{Name: "CERT_PATH", Value: "/path/to/signing.crt"},
|
||||
{Name: "KEY_PATH", Value: "/path/to/signing.key"},
|
||||
@@ -200,8 +200,8 @@ func TestAgentController(t *testing.T) {
|
||||
}
|
||||
|
||||
mockExecSucceeds := func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}").
|
||||
Return("test-cert\n\n\ntest-key", nil)
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil) // "test-cert" / "test-key"
|
||||
dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")).
|
||||
Return(nil)
|
||||
}
|
||||
@@ -573,7 +573,7 @@ func TestAgentController(t *testing.T) {
|
||||
validClusterInfoConfigMap,
|
||||
},
|
||||
mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}").
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return("", fmt.Errorf("some exec error")).
|
||||
AnyTimes()
|
||||
},
|
||||
@@ -589,6 +589,90 @@ func TestAgentController(t *testing.T) {
|
||||
LastUpdateTime: metav1.NewTime(now),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deployment exists, configmap is valid, exec into agent pod returns invalid JSON",
|
||||
pinnipedObjects: []runtime.Object{
|
||||
initialCredentialIssuer,
|
||||
},
|
||||
kubeObjects: []runtime.Object{
|
||||
healthyKubeControllerManagerPod,
|
||||
healthyAgentDeployment,
|
||||
healthyAgentPod,
|
||||
validClusterInfoConfigMap,
|
||||
},
|
||||
mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return("bogus-data", nil).
|
||||
AnyTimes()
|
||||
},
|
||||
wantDistinctErrors: []string{
|
||||
`failed to decode signing cert/key JSON from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: invalid character 'b' looking for beginning of value`,
|
||||
},
|
||||
wantAgentDeployment: healthyAgentDeployment,
|
||||
wantStrategy: &configv1alpha1.CredentialIssuerStrategy{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.ErrorStrategyStatus,
|
||||
Reason: configv1alpha1.CouldNotFetchKeyStrategyReason,
|
||||
Message: `failed to decode signing cert/key JSON from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: invalid character 'b' looking for beginning of value`,
|
||||
LastUpdateTime: metav1.NewTime(now),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deployment exists, configmap is valid, exec into agent pod returns invalid cert base64",
|
||||
pinnipedObjects: []runtime.Object{
|
||||
initialCredentialIssuer,
|
||||
},
|
||||
kubeObjects: []runtime.Object{
|
||||
healthyKubeControllerManagerPod,
|
||||
healthyAgentDeployment,
|
||||
healthyAgentPod,
|
||||
validClusterInfoConfigMap,
|
||||
},
|
||||
mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return(`{"tls.crt": "invalid"}`, nil).
|
||||
AnyTimes()
|
||||
},
|
||||
wantDistinctErrors: []string{
|
||||
`failed to decode signing cert base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`,
|
||||
},
|
||||
wantAgentDeployment: healthyAgentDeployment,
|
||||
wantStrategy: &configv1alpha1.CredentialIssuerStrategy{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.ErrorStrategyStatus,
|
||||
Reason: configv1alpha1.CouldNotFetchKeyStrategyReason,
|
||||
Message: `failed to decode signing cert base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`,
|
||||
LastUpdateTime: metav1.NewTime(now),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deployment exists, configmap is valid, exec into agent pod returns invalid key base64",
|
||||
pinnipedObjects: []runtime.Object{
|
||||
initialCredentialIssuer,
|
||||
},
|
||||
kubeObjects: []runtime.Object{
|
||||
healthyKubeControllerManagerPod,
|
||||
healthyAgentDeployment,
|
||||
healthyAgentPod,
|
||||
validClusterInfoConfigMap,
|
||||
},
|
||||
mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return(`{"tls.crt": "dGVzdAo=", "tls.key": "invalid"}`, nil).
|
||||
AnyTimes()
|
||||
},
|
||||
wantDistinctErrors: []string{
|
||||
`failed to decode signing key base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`,
|
||||
},
|
||||
wantAgentDeployment: healthyAgentDeployment,
|
||||
wantStrategy: &configv1alpha1.CredentialIssuerStrategy{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.ErrorStrategyStatus,
|
||||
Reason: configv1alpha1.CouldNotFetchKeyStrategyReason,
|
||||
Message: `failed to decode signing key base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`,
|
||||
LastUpdateTime: metav1.NewTime(now),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deployment exists, configmap is valid, exec into agent pod returns bogus certs",
|
||||
pinnipedObjects: []runtime.Object{
|
||||
@@ -601,10 +685,10 @@ func TestAgentController(t *testing.T) {
|
||||
validClusterInfoConfigMap,
|
||||
},
|
||||
mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) {
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}").
|
||||
Return("bogus-data", nil).
|
||||
executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print").
|
||||
Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil). // "test-cert" / "test-key"
|
||||
AnyTimes()
|
||||
dynamicCert.SetCertKeyContent([]byte(""), []byte("")).
|
||||
dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")).
|
||||
Return(fmt.Errorf("some dynamic cert error")).
|
||||
AnyTimes()
|
||||
},
|
||||
@@ -877,7 +961,6 @@ func runControllerUntilQuiet(ctx context.Context, t *testing.T, controller contr
|
||||
|
||||
errorStream := make(chan error)
|
||||
controllerlib.TestWrap(t, controller, func(syncer controllerlib.Syncer) controllerlib.Syncer {
|
||||
controller.Name()
|
||||
return controllerlib.SyncFunc(func(ctx controllerlib.Context) error {
|
||||
err := syncer.Sync(ctx)
|
||||
errorStream <- err
|
||||
|
||||
Reference in New Issue
Block a user