Просмотр исходного кода

Add flag that disables certificate renewal by the cert controller (#2721)

Signed-off-by: Leonardo M. Miranda <leonardomichalskim@gmail.com>
Leonardo M. Miranda 2 лет назад
Родитель
Сommit
c71230ff20

+ 3 - 1
cmd/certcontroller.go

@@ -89,7 +89,8 @@ var certcontrollerCmd = &cobra.Command{
 		}
 		crdctrl := crds.New(mgr.GetClient(), mgr.GetScheme(),
 			ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
-			crdRequeueInterval, serviceName, serviceNamespace, secretName, secretNamespace, crdNames)
+			crdRequeueInterval, enableCertRenewal,
+			serviceName, serviceNamespace, secretName, secretNamespace, crdNames)
 		if err := crdctrl.SetupWithManager(mgr, controller.Options{
 			MaxConcurrentReconciles: concurrent,
 		}); err != nil {
@@ -143,4 +144,5 @@ func init() {
 	certcontrollerCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
 	certcontrollerCmd.Flags().StringVar(&zapTimeEncoding, "zap-time-encoding", "epoch", "Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano')")
 	certcontrollerCmd.Flags().DurationVar(&crdRequeueInterval, "crd-requeue-interval", time.Minute*5, "Time duration between reconciling CRDs for new certs")
+	certcontrollerCmd.Flags().BoolVar(&enableCertRenewal, "enable-cert-renewal", true, "Enable renewal of the webhook certificate by the cert controller. ")
 }

+ 1 - 0
cmd/root.go

@@ -81,6 +81,7 @@ var (
 	crdRequeueInterval                    time.Duration
 	certCheckInterval                     time.Duration
 	certLookaheadInterval                 time.Duration
+	enableCertRenewal                     bool
 	tlsCiphers                            string
 	tlsMinVersion                         string
 )

+ 1 - 0
deploy/charts/external-secrets/README.md

@@ -38,6 +38,7 @@ The command removes all the Kubernetes components associated with the chart and
 | certController.affinity | object | `{}` |  |
 | certController.create | bool | `true` | Specifies whether a certificate controller deployment be created. |
 | certController.deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
+| certController.enableCertRenewal | bool | `true` | Whether the certificate controller should renew certificates, or just handle their injection |
 | certController.extraArgs | object | `{}` |  |
 | certController.extraEnv | list | `[]` |  |
 | certController.extraVolumeMounts | list | `[]` |  |

+ 1 - 0
deploy/charts/external-secrets/templates/cert-controller-deployment.yaml

@@ -56,6 +56,7 @@ spec:
           - --secret-namespace={{ .Release.Namespace }}
           - --metrics-addr=:{{ .Values.certController.prometheus.service.port }}
           - --healthz-addr={{ .Values.certController.readinessProbe.address }}:{{ .Values.certController.readinessProbe.port }}
+          - --enable-cert-renewal={{ .Values.certController.enableCertRenewal }}
           {{ if not .Values.crds.createClusterSecretStore -}}
           - --crd-names=externalsecrets.external-secrets.io
           - --crd-names=secretstores.external-secrets.io

+ 1 - 0
deploy/charts/external-secrets/tests/__snapshot__/cert_controller_test.yaml.snap

@@ -38,6 +38,7 @@ should match snapshot of default values:
                 - --secret-namespace=NAMESPACE
                 - --metrics-addr=:8080
                 - --healthz-addr=:8081
+                - --enable-cert-renewal=true
               image: ghcr.io/external-secrets/external-secrets:v0.9.5
               imagePullPolicy: IfNotPresent
               name: cert-controller

+ 3 - 0
deploy/charts/external-secrets/values.yaml

@@ -392,6 +392,9 @@ certController:
   requeueInterval: "5m"
   replicaCount: 1
 
+  # -- Whether the certificate controller should renew certificates, or just handle their injection
+  enableCertRenewal: true
+
   # -- Specifies the amount of historic ReplicaSets k8s should keep (see https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy)
   revisionHistoryLimit: 10
 

+ 46 - 35
pkg/controllers/crds/crds_controller.go

@@ -58,40 +58,42 @@ const (
 
 type Reconciler struct {
 	client.Client
-	Log             logr.Logger
-	Scheme          *runtime.Scheme
-	recorder        record.EventRecorder
-	SvcName         string
-	SvcNamespace    string
-	SecretName      string
-	SecretNamespace string
-	CrdResources    []string
-	dnsName         string
-	CAName          string
-	CAOrganization  string
-	RequeueInterval time.Duration
-
+	Log               logr.Logger
+	Scheme            *runtime.Scheme
+	recorder          record.EventRecorder
+	SvcName           string
+	SvcNamespace      string
+	SecretName        string
+	SecretNamespace   string
+	CrdResources      []string
+	dnsName           string
+	CAName            string
+	CAOrganization    string
+	RequeueInterval   time.Duration
+	EnableCertRenewal bool
 	// the controller is ready when all crds are injected
 	rdyMu          *sync.Mutex
 	readyStatusMap map[string]bool
 }
 
 func New(k8sClient client.Client, scheme *runtime.Scheme, logger logr.Logger,
-	interval time.Duration, svcName, svcNamespace, secretName, secretNamespace string, resources []string) *Reconciler {
+	interval time.Duration, enableCertRenewal bool,
+	svcName, svcNamespace, secretName, secretNamespace string, resources []string) *Reconciler {
 	return &Reconciler{
-		Client:          k8sClient,
-		Log:             logger,
-		Scheme:          scheme,
-		SvcName:         svcName,
-		SvcNamespace:    svcNamespace,
-		SecretName:      secretName,
-		SecretNamespace: secretNamespace,
-		RequeueInterval: interval,
-		CrdResources:    resources,
-		CAName:          "external-secrets",
-		CAOrganization:  "external-secrets",
-		rdyMu:           &sync.Mutex{},
-		readyStatusMap:  map[string]bool{},
+		Client:            k8sClient,
+		Log:               logger,
+		Scheme:            scheme,
+		SvcName:           svcName,
+		SvcNamespace:      svcNamespace,
+		SecretName:        secretName,
+		SecretNamespace:   secretNamespace,
+		RequeueInterval:   interval,
+		EnableCertRenewal: enableCertRenewal,
+		CrdResources:      resources,
+		CAName:            "external-secrets",
+		CAOrganization:    "external-secrets",
+		rdyMu:             &sync.Mutex{},
+		readyStatusMap:    map[string]bool{},
 	}
 }
 
@@ -187,16 +189,21 @@ func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
 		return err
 	}
 	r.dnsName = fmt.Sprintf("%v.%v.svc", r.SvcName, r.SvcNamespace)
-	need, err := r.refreshCertIfNeeded(&secret)
-	if err != nil {
-		return err
+	refreshedCert := false
+	if r.EnableCertRenewal {
+		refreshedCert, err = r.refreshCertIfNeeded(&secret)
+		if err != nil {
+			return err
+		}
 	}
-	if need {
+	// Injects the certificates if they were refreshed or changed
+	if refreshedCert || !r.EnableCertRenewal {
 		artifacts, err := buildArtifactsFromSecret(&secret)
 		if err != nil {
 			return err
 		}
-		if err := injectCert(&updatedResource, artifacts.CertPEM); err != nil {
+		// Only injects if artifacts.CertPEM changed
+		if _, err := injectCert(&updatedResource, artifacts.CertPEM); err != nil {
 			return err
 		}
 	}
@@ -215,14 +222,18 @@ func injectService(crd *apiext.CustomResourceDefinition, svc types.NamespacedNam
 	return nil
 }
 
-func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) error {
+func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) (bool, error) {
 	if crd.Spec.Conversion == nil ||
 		crd.Spec.Conversion.Webhook == nil ||
 		crd.Spec.Conversion.Webhook.ClientConfig == nil {
-		return fmt.Errorf("unexpected crd conversion webhook config")
+		return false, fmt.Errorf("unexpected crd conversion webhook config")
+	}
+	if bytes.Equal(crd.Spec.Conversion.Webhook.ClientConfig.CABundle, certPem) {
+		// CABundle unchanged
+		return false, nil
 	}
 	crd.Spec.Conversion.Webhook.ClientConfig.CABundle = certPem
-	return nil
+	return true, nil
 }
 
 type KeyPairArtifacts struct {

+ 25 - 6
pkg/controllers/crds/crds_controller_test.go

@@ -40,11 +40,12 @@ const (
 
 func newReconciler() Reconciler {
 	return Reconciler{
-		CrdResources:    []string{"one", "two", "three"},
-		SvcName:         "foo",
-		SvcNamespace:    "default",
-		SecretName:      "foo",
-		SecretNamespace: "default",
+		CrdResources:      []string{"one", "two", "three"},
+		SvcName:           "foo",
+		SvcNamespace:      "default",
+		SecretName:        "foo",
+		SecretNamespace:   "default",
+		EnableCertRenewal: true,
 	}
 }
 
@@ -107,6 +108,11 @@ func TestUpdateCRD(t *testing.T) {
 	if err != nil {
 		t.Errorf("Failed updating CRD: %v", err)
 	}
+	rec.EnableCertRenewal = false
+	err = rec.updateCRD(ctx, req)
+	if err != nil {
+		t.Errorf("Failed updating CRD: %v", err)
+	}
 }
 
 func TestInjectSvcToConversionWebhook(t *testing.T) {
@@ -133,13 +139,26 @@ func TestInjectSvcToConversionWebhook(t *testing.T) {
 func TestInjectCertToConversionWebhook(t *testing.T) {
 	certPEM := []byte("foobar")
 	crd := newCRD()
-	err := injectCert(&crd, certPEM)
+	injected, err := injectCert(&crd, certPEM)
 	if err != nil {
 		t.Errorf("Failed: error when injecting: %v", err)
 	}
 	if string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle) != "foobar" {
 		t.Errorf("Wrong certificate name injected: %v", string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle))
 	}
+	if injected != true {
+		t.Errorf("Certificate not injected")
+	}
+	injected, err = injectCert(&crd, certPEM)
+	if err != nil {
+		t.Errorf("Failed: error when injecting: %v", err)
+	}
+	if string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle) != "foobar" {
+		t.Errorf("Wrong certificate name injected: %v", string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle))
+	}
+	if injected == true {
+		t.Errorf("Certificate was unnecessarily injected")
+	}
 }
 func TestPopulateSecret(t *testing.T) {
 	secret := newSecret()

+ 2 - 1
pkg/controllers/crds/suite_test.go

@@ -77,7 +77,8 @@ var _ = BeforeSuite(func() {
 	Expect(err).ToNot(HaveOccurred())
 	Expect(k8sClient).ToNot(BeNil())
 
-	rec := New(k8sClient, k8sManager.GetScheme(), log, time.Second*1,
+	rec := New(k8sClient, k8sManager.GetScheme(), log,
+		time.Second*1, true,
 		"foo", "default", "foo", "default", []string{
 			"secretstores.test.io",
 		})