From f28887f47d36ee23b440fc86abd0d5c617a6cadc Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Thu, 14 Nov 2019 09:49:25 +0100 Subject: [PATCH 1/7] Add webhook Secret and ValidatingWebhookConfiguration management --- cmd/manager/main.go | 97 +++++++------- .../all-in-one/operator.template.yaml | 13 +- .../operator/all-in-one/webhook.template.yaml | 1 + config/operator/global/operator.template.yaml | 13 +- config/operator/global/webhook.template.yaml | 42 ++++++ config/webhook/manifests.yaml | 2 +- pkg/apis/elasticsearch/v1beta1/webhook.go | 2 +- .../common/certificates/ca_reconcile.go | 18 +-- .../common/certificates/ca_reconcile_test.go | 14 +- pkg/controller/webhook/cert.go | 126 ++++++++++++++++++ pkg/controller/webhook/reconcile.go | 66 +++++++++ pkg/controller/webhook/reconcile_test.go | 116 ++++++++++++++++ .../webhook_certificates_controller.go | 114 ++++++++++++++++ 13 files changed, 556 insertions(+), 68 deletions(-) create mode 120000 config/operator/all-in-one/webhook.template.yaml create mode 100644 config/operator/global/webhook.template.yaml create mode 100644 pkg/controller/webhook/cert.go create mode 100644 pkg/controller/webhook/reconcile.go create mode 100644 pkg/controller/webhook/reconcile_test.go create mode 100644 pkg/controller/webhook/webhook_certificates_controller.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index cdc5e1d4b0..a55fa9bdd0 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -12,10 +12,13 @@ import ( "strings" "time" - "github.com/elastic/cloud-on-k8s/pkg/about" + "github.com/elastic/cloud-on-k8s/pkg/controller/webhook" + // allow gcp authentication _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "github.com/elastic/cloud-on-k8s/pkg/about" + estype "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1beta1" "github.com/elastic/cloud-on-k8s/pkg/controller/apmserver" asesassn "github.com/elastic/cloud-on-k8s/pkg/controller/apmserverelasticsearchassociation" "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" @@ -29,16 +32,11 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/dev" "github.com/elastic/cloud-on-k8s/pkg/dev/portforward" "github.com/elastic/cloud-on-k8s/pkg/utils/net" - - // TODO (sabo): re-enable when webhooks are usable - // "github.com/elastic/cloud-on-k8s/pkg/webhook" - - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/manager/signals" @@ -47,6 +45,7 @@ import ( const ( MetricsPortFlag = "metrics-port" DefaultMetricPort = 0 // disabled + WebhookPort = 9443 AutoPortForwardFlagName = "auto-port-forward" NamespacesFlag = "namespaces" @@ -56,10 +55,11 @@ const ( CertValidityFlag = "cert-validity" CertRotateBeforeFlag = "cert-rotate-before" - AutoInstallWebhooksFlag = "auto-install-webhooks" - OperatorNamespaceFlag = "operator-namespace" - WebhookSecretFlag = "webhook-secret" - WebhookPodsLabelFlag = "webhook-pods-label" + AutoCertificatesWebhookFlag = "webhook-certificates-management" + OperatorNamespaceFlag = "operator-namespace" + WebhookSecretFlag = "webhook-secret" + WebhookDomainNameFlag = "webhook-domain-name" + DefaultWebhookServiceFlag = "elastic-webhook-server" DebugHTTPServerListenAddressFlag = "debug-http-listen" ) @@ -123,19 +123,19 @@ func init() { "Duration representing how long before expiration TLS certificates should be reissued", ) Cmd.Flags().Bool( - AutoInstallWebhooksFlag, + AutoCertificatesWebhookFlag, true, - "enables automatic webhook installation (RBAC permission for service, secret and validatingwebhookconfigurations needed)", + "enables automatic certificates management for the webhook, the Secret and the ValidatingWebhookConfiguration must be created before running the operator", ) Cmd.Flags().String( - OperatorNamespaceFlag, - "", - "k8s namespace the operator runs in", + WebhookDomainNameFlag, + DefaultWebhookServiceFlag, + "k8s service name used to automatically the webhook certificate", ) Cmd.Flags().String( - WebhookPodsLabelFlag, + OperatorNamespaceFlag, "", - "k8s label to select pods running the operator", + "k8s namespace the operator runs in", ) Cmd.Flags().String( WebhookSecretFlag, @@ -236,6 +236,7 @@ func execute() { } opts.MetricsBindAddress = fmt.Sprintf(":%d", metricsPort) // 0 to disable + opts.Port = WebhookPort mgr, err := ctrl.NewManager(cfg, opts) if err != nil { log.Error(err, "unable to create controller manager") @@ -281,6 +282,37 @@ func execute() { }, } + if operator.HasRole(operator.WebhookServer, roles) { + autoWebhookCertificatesMngt := viper.GetBool(AutoCertificatesWebhookFlag) + if autoWebhookCertificatesMngt { + log.Info("Automatic management of the webhook certificates enabled") + // Ensure that all the certificates needed by the webhook server are already created + webhookParams := webhook.Params{ + Namespace: viper.GetString(OperatorNamespaceFlag), + SecretName: viper.GetString(WebhookSecretFlag), + ServerDomainName: viper.GetString(WebhookDomainNameFlag), + WebhookConfigurationName: "elastic-webhook.k8s.elastic.co", + Rotation: params.CertRotation, + } + + // Force a first reconciliation to create the resources before the server is started + if err := webhookParams.ReconcileResources(clientset); err != nil { + log.Error(err, "unable to setup and fill the webhook certificates") + os.Exit(1) + } + + if err = webhook.Add(mgr, webhookParams, clientset); err != nil { + log.Error(err, "unable to create controller", "controller", webhook.ControllerName) + os.Exit(1) + } + } + + if err = (&estype.Elasticsearch{}).SetupWebhookWithManager(mgr); err != nil { + log.Error(err, "unable to create webhook", "webhook", "Elasticsearch") + os.Exit(1) + } + } + if operator.HasRole(operator.NamespaceOperator, roles) { if err = apmserver.Add(mgr, params); err != nil { log.Error(err, "unable to create controller", "controller", "ApmServer") @@ -314,13 +346,6 @@ func execute() { } } - // TODO (sabo): re-enable when webhooks are usable - // log.Info("Setting up webhooks") - // if err := webhook.AddToManager(mgr, roles, newWebhookParameters); err != nil { - // log.Error(err, "unable to register webhooks to the manager") - // os.Exit(1) - // } - log.Info("Starting the manager", "uuid", operatorInfo.OperatorUUID, "namespace", operatorNamespace, "version", operatorInfo.BuildInfo.Version, "build_hash", operatorInfo.BuildInfo.Hash, "build_date", operatorInfo.BuildInfo.Date, @@ -331,26 +356,6 @@ func execute() { } } -// TODO (sabo): re-enable when webhooks are usable -// func newWebhookParameters() (*webhook.Parameters, error) { -// autoInstall := viper.GetBool(AutoInstallWebhooksFlag) -// ns := viper.GetString(OperatorNamespaceFlag) -// if ns == "" && autoInstall { -// return nil, fmt.Errorf("%s needs to be set for webhook auto installation", OperatorNamespaceFlag) -// } -// svcSelector := viper.GetString(WebhookPodsLabelFlag) -// sec := viper.GetString(WebhookSecretFlag) -// return &webhook.Parameters{ -// Bootstrap: webhook.NewBootstrapOptions(webhook.BootstrapOptionsParams{ -// Namespace: ns, -// ManagedNamespace: viper.GetString(NamespaceFlagName), -// SecretName: sec, -// ServiceSelector: svcSelector, -// }), -// AutoInstall: autoInstall, -// }, nil -// } - func ValidateCertExpirationFlags(validityFlag string, rotateBeforeFlag string) (time.Duration, time.Duration) { certValidity := viper.GetDuration(validityFlag) certRotateBefore := viper.GetDuration(rotateBeforeFlag) diff --git a/config/operator/all-in-one/operator.template.yaml b/config/operator/all-in-one/operator.template.yaml index 973c36b873..7d32d7efc5 100644 --- a/config/operator/all-in-one/operator.template.yaml +++ b/config/operator/all-in-one/operator.template.yaml @@ -28,7 +28,7 @@ spec: fieldRef: fieldPath: metadata.namespace - name: WEBHOOK_SECRET - value: webhook-server-secret + value: elastic-webhook-server-cert - name: WEBHOOK_PODS_LABEL value: elastic-operator - name: OPERATOR_IMAGE @@ -41,7 +41,16 @@ spec: cpu: 100m memory: 50Mi ports: - - containerPort: 9876 + - containerPort: 9443 name: webhook-server protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: elastic-webhook-server-cert diff --git a/config/operator/all-in-one/webhook.template.yaml b/config/operator/all-in-one/webhook.template.yaml new file mode 120000 index 0000000000..5f6fd90613 --- /dev/null +++ b/config/operator/all-in-one/webhook.template.yaml @@ -0,0 +1 @@ +../global/webhook.template.yaml \ No newline at end of file diff --git a/config/operator/global/operator.template.yaml b/config/operator/global/operator.template.yaml index b30d2095fe..41683ce392 100644 --- a/config/operator/global/operator.template.yaml +++ b/config/operator/global/operator.template.yaml @@ -28,7 +28,7 @@ spec: fieldRef: fieldPath: metadata.namespace - name: WEBHOOK_SECRET - value: webhook-server-secret + value: elastic-webhook-server-cert - name: WEBHOOK_PODS_LABEL value: elastic-global-operator - name: OPERATOR_IMAGE @@ -41,8 +41,17 @@ spec: cpu: 100m memory: 20Mi ports: - - containerPort: 9876 + - containerPort: 9443 name: webhook-server protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: elastic-webhook-server-cert diff --git a/config/operator/global/webhook.template.yaml b/config/operator/global/webhook.template.yaml new file mode 100644 index 0000000000..7996b3eee3 --- /dev/null +++ b/config/operator/global/webhook.template.yaml @@ -0,0 +1,42 @@ +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: elastic-webhook.k8s.elastic.co +webhooks: + - clientConfig: + caBundle: Cg== + service: + name: elastic-webhook-server + namespace: + # this is the path controller-runtime automatically generates + path: /validate-elasticsearch-k8s-elastic-co-v1beta1-elasticsearch + failurePolicy: Ignore + name: elastic-es-validation.k8s.elastic.co + rules: + - apiGroups: + - elasticsearch.k8s.elastic.co + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - elasticsearches +--- +apiVersion: v1 +kind: Service +metadata: + name: elastic-webhook-server + namespace: +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: elastic-operator +--- +apiVersion: v1 +kind: Secret +metadata: + name: elastic-webhook-server-cert + namespace: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index f14a7c5f03..64c1765a4f 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -13,7 +13,7 @@ webhooks: namespace: system path: /validate-elasticsearch failurePolicy: Ignore - name: elastic-es-validation + name: elastic-es-validation.k8s.elastic.co rules: - apiGroups: - elasticsearch.k8s.elastic.co diff --git a/pkg/apis/elasticsearch/v1beta1/webhook.go b/pkg/apis/elasticsearch/v1beta1/webhook.go index 816bcd2840..19eaa85261 100644 --- a/pkg/apis/elasticsearch/v1beta1/webhook.go +++ b/pkg/apis/elasticsearch/v1beta1/webhook.go @@ -16,7 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" ) -// +kubebuilder:webhook:path=/validate-elasticsearch,mutating=false,failurePolicy=ignore,groups=elasticsearch.k8s.elastic.co,resources=elasticsearches,verbs=create;update,versions=v1beta1,name=elastic-es-validation +// +kubebuilder:webhook:path=/validate-elasticsearch,mutating=false,failurePolicy=ignore,groups=elasticsearch.k8s.elastic.co,resources=elasticsearches,verbs=create;update,versions=v1beta1,name=elastic-es-validation.k8s.elastic.co func (r *Elasticsearch) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). diff --git a/pkg/controller/common/certificates/ca_reconcile.go b/pkg/controller/common/certificates/ca_reconcile.go index 344dd0ab6c..b95e724064 100644 --- a/pkg/controller/common/certificates/ca_reconcile.go +++ b/pkg/controller/common/certificates/ca_reconcile.go @@ -70,14 +70,14 @@ func ReconcileCAForOwner( } // build CA - ca := buildCAFromSecret(caInternalSecret) + ca := BuildCAFromSecret(caInternalSecret) if ca == nil { log.Info("Cannot build CA from secret, creating a new one", "owner_namespace", owner.GetNamespace(), "owner_name", owner.GetName(), "ca_type", caType) return renewCA(cl, namer, owner, labels, rotationParams.Validity, scheme, caType) } // renew if cannot reuse - if !canReuseCA(ca, rotationParams.RotateBefore) { + if !CanReuseCA(ca, rotationParams.RotateBefore) { log.Info("Cannot reuse existing CA, creating a new one", "owner_namespace", owner.GetNamespace(), "owner_name", owner.GetName(), "ca_type", caType) return renewCA(cl, namer, owner, labels, rotationParams.Validity, scheme, caType) } @@ -125,14 +125,14 @@ func renewCA( return ca, nil } -// canReuseCA returns true if the given CA is valid for reuse -func canReuseCA(ca *CA, expirationSafetyMargin time.Duration) bool { - return PrivateMatchesPublicKey(ca.Cert.PublicKey, *ca.PrivateKey) && certIsValid(*ca.Cert, expirationSafetyMargin) +// CanReuseCA returns true if the given CA is valid for reuse +func CanReuseCA(ca *CA, expirationSafetyMargin time.Duration) bool { + return PrivateMatchesPublicKey(ca.Cert.PublicKey, *ca.PrivateKey) && CertIsValid(*ca.Cert, expirationSafetyMargin) } -// certIsValid returns true if the given cert is valid, +// CertIsValid returns true if the given cert is valid, // according to a safety time margin. -func certIsValid(cert x509.Certificate, expirationSafetyMargin time.Duration) bool { +func CertIsValid(cert x509.Certificate, expirationSafetyMargin time.Duration) bool { now := time.Now() if now.Before(cert.NotBefore) { log.Info("CA cert is not valid yet", "subject", cert.Subject) @@ -166,9 +166,9 @@ func internalSecretForCA( } } -// buildCAFromSecret parses the given secret into a CA. +// BuildCAFromSecret parses the given secret into a CA. // It returns nil if the secrets could not be parsed into a CA. -func buildCAFromSecret(caInternalSecret corev1.Secret) *CA { +func BuildCAFromSecret(caInternalSecret corev1.Secret) *CA { if caInternalSecret.Data == nil { return nil } diff --git a/pkg/controller/common/certificates/ca_reconcile_test.go b/pkg/controller/common/certificates/ca_reconcile_test.go index 592e2a23b9..8a52ff6caa 100644 --- a/pkg/controller/common/certificates/ca_reconcile_test.go +++ b/pkg/controller/common/certificates/ca_reconcile_test.go @@ -87,8 +87,8 @@ func Test_certIsValid(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := certIsValid(tt.cert, tt.safetyMargin); got != tt.want { - t.Errorf("certIsValid() = %v, want %v", got, tt.want) + if got := CertIsValid(tt.cert, tt.safetyMargin); got != tt.want { + t.Errorf("CertIsValid() = %v, want %v", got, tt.want) } }) } @@ -134,8 +134,8 @@ func Test_canReuseCA(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := canReuseCA(tt.ca(), DefaultRotateBefore); got != tt.want { - t.Errorf("canReuseCA() = %v, want %v", got, tt.want) + if got := CanReuseCA(tt.ca(), DefaultRotateBefore); got != tt.want { + t.Errorf("CanReuseCA() = %v, want %v", got, tt.want) } }) } @@ -152,7 +152,7 @@ func checkCASecrets( expectedExpiration time.Duration, ) { // ca cert should be valid - require.True(t, certIsValid(*ca.Cert, DefaultRotateBefore)) + require.True(t, CertIsValid(*ca.Cert, DefaultRotateBefore)) // expiration date should be correctly set require.True(t, ca.Cert.NotBefore.After(time.Now().Add(-1*time.Hour))) @@ -181,7 +181,7 @@ func checkCASecrets( require.NotEmpty(t, internalCASecret.Data[KeyFileName]) // secret should be ok to parse as a CA - parsedCa := buildCAFromSecret(internalCASecret) + parsedCa := BuildCAFromSecret(internalCASecret) require.NotNil(t, parsedCa) // and return the ca require.True(t, ca.Cert.Equal(parsedCa.Cert)) @@ -377,7 +377,7 @@ func Test_buildCAFromSecret(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ca := buildCAFromSecret(tt.internalSecret) + ca := BuildCAFromSecret(tt.internalSecret) if !reflect.DeepEqual(ca, tt.wantCa) { t.Errorf("CaFromSecrets() got = %v, want %v", ca, tt.wantCa) } diff --git a/pkg/controller/webhook/cert.go b/pkg/controller/webhook/cert.go new file mode 100644 index 0000000000..2274cf0dbd --- /dev/null +++ b/pkg/controller/webhook/cert.go @@ -0,0 +1,126 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package webhook + +import ( + cryptorand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "time" + + "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" + "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" + "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// WebhookCertificates holds the artifacts used by the webhook server and the webhook configuration. +type WebhookCertificates struct { + caCert []byte + + serverKey []byte + serverCert []byte +} + +func (w *Params) shouldRenewCertificates(serverCertificates *corev1.Secret, webhookConfiguration *v1beta1.ValidatingWebhookConfiguration) bool { + // Read the current certificate used by the server + serverCA := certificates.BuildCAFromSecret(*serverCertificates) + if serverCA == nil { + return true + } + if !certificates.CanReuseCA(serverCA, w.Rotation.RotateBefore) { + return true + } + // Read the certificate in the webhook configuration + for _, webhook := range webhookConfiguration.Webhooks { + caBytes := webhook.ClientConfig.CABundle + if len(caBytes) == 0 { + return true + } + // Parse the certificates + certs, err := certificates.ParsePEMCerts(caBytes) + if err != nil { + log.Error(err, "Cannot parse PEM cert from webhook configuration, will create a new one", "webhook_name", webhookConfiguration.Name) + return true + } + if len(certs) == 0 { + return true + } + for _, cert := range certs { + if !certificates.CertIsValid(*cert, w.Rotation.RotateBefore) { + return true + } + } + } + return false +} + +func (w *Params) newCertificates() (WebhookCertificates, error) { + webhookCertificates := WebhookCertificates{} + + // Create a new CA + ca, err := certificates.NewSelfSignedCA(certificates.CABuilderOptions{ + Subject: pkix.Name{ + CommonName: "elastic-webhook-ca", + OrganizationalUnit: []string{"elastic-webhook"}, + }, + ExpireIn: &w.Rotation.Validity, + }) + if err != nil { + return webhookCertificates, err + } + webhookCertificates.caCert = certificates.EncodePEMCert(ca.Cert.Raw) + + // Create a new certificate for the webhook server + privateKey, err := rsa.GenerateKey(cryptorand.Reader, 2048) + if err != nil { + return webhookCertificates, err + } + webhookCertificates.serverKey = certificates.EncodePEMPrivateKey(*privateKey) + + csr, err := x509.CreateCertificateRequest(cryptorand.Reader, &x509.CertificateRequest{}, privateKey) + if err != nil { + return webhookCertificates, err + } + parsedCSR, err := x509.ParseCertificateRequest(csr) + if err != nil { + return webhookCertificates, err + } + + certificateTemplate := certificates.ValidatedCertificateTemplate(x509.Certificate{ + Subject: pkix.Name{ + CommonName: "elastic-webhook", + OrganizationalUnit: []string{"elastic-webhook"}, + }, + + DNSNames: k8s.GetServiceDNSName(corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: w.Namespace, + Name: w.ServerDomainName, + }, + }), + + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: time.Now().Add(w.Rotation.Validity), + + PublicKeyAlgorithm: parsedCSR.PublicKeyAlgorithm, + PublicKey: parsedCSR.PublicKey, + + Signature: parsedCSR.Signature, + SignatureAlgorithm: parsedCSR.SignatureAlgorithm, + + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }) + + cert, err := ca.CreateCertificate(certificateTemplate) + if err != nil { + return webhookCertificates, err + } + webhookCertificates.serverCert = certificates.EncodePEMCert(cert) + return webhookCertificates, nil +} diff --git a/pkg/controller/webhook/reconcile.go b/pkg/controller/webhook/reconcile.go new file mode 100644 index 0000000000..438693789d --- /dev/null +++ b/pkg/controller/webhook/reconcile.go @@ -0,0 +1,66 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package webhook + +import ( + "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// Params are params to create and manage the webhook resources (Cert secret and ValidatingWebhookConfiguration) +type Params struct { + Namespace string + SecretName string + ServerDomainName string + WebhookConfigurationName string + + // Certificate options + Rotation certificates.RotationParams +} + +// ReconcileResources reconciles the certificates used by the webhook client and the webhook server. +// It also returns the duration after which a certificate rotation should be scheduled. +func (w *Params) ReconcileResources(clientset kubernetes.Interface) error { + // retrieve current webhook server cert secret + webhookServerSecret, err := clientset.CoreV1().Secrets(w.Namespace).Get(w.SecretName, metav1.GetOptions{}) + if err != nil { + // 404 is still considered as an error, webhook secret is expected to be created before the operator is started + return err + } + + // retrieve the current webhook configuration + webhookConfiguration, err := clientset.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(w.WebhookConfigurationName, metav1.GetOptions{}) + if err != nil { + // 404 is also considered as an error, webhook configuration is expected to be created before the operator is started + return err + } + + // check if we need to renew the certificates used in the resources + if w.shouldRenewCertificates(webhookServerSecret, webhookConfiguration) { + newCertificates, err := w.newCertificates() + if err != nil { + return err + } + // update the webhook configuration + for i := range webhookConfiguration.Webhooks { + webhookConfiguration.Webhooks[i].ClientConfig.CABundle = newCertificates.caCert + } + if _, err := clientset.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Update(webhookConfiguration); err != nil { + return err + } + + // update server secret + webhookServerSecret.Data = map[string][]byte{ + certificates.CertFileName: newCertificates.serverCert, + certificates.KeyFileName: newCertificates.serverKey, + } + if _, err := clientset.CoreV1().Secrets(w.Namespace).Update(webhookServerSecret); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/controller/webhook/reconcile_test.go b/pkg/controller/webhook/reconcile_test.go new file mode 100644 index 0000000000..7f73fe552b --- /dev/null +++ b/pkg/controller/webhook/reconcile_test.go @@ -0,0 +1,116 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package webhook + +import ( + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" + "github.com/stretchr/testify/assert" + "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +const ( + sampleServiceName = "elastic-webhook-server" +) + +func TestParams_ReconcileResources(t *testing.T) { + + w := Params{ + Namespace: "elastic-system", + SecretName: "elastic-webhook-server-cert", + //ServerDomainName: sampleServiceName, + WebhookConfigurationName: "elastic-webhook.k8s.elastic.co", + Rotation: certificates.RotationParams{ + Validity: certificates.DefaultCertValidity, + RotateBefore: certificates.DefaultRotateBefore, + }, + } + + clientset := + fake.NewSimpleClientset( + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "elastic-system", + Name: "elastic-webhook-server-cert", + }, + }, + &v1beta1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "elastic-webhook.k8s.elastic.co", + }, + Webhooks: []v1beta1.ValidatingWebhook{ + { + Name: "elastic-es-validation.k8s.elastic.co", + ClientConfig: v1beta1.WebhookClientConfig{}, + }, + }, + }, + ) + + if err := w.ReconcileResources(clientset); err != nil { + t.Errorf("Params.ReconcileResources() error = %v", err) + } + + // Secret and ValidatingWebhookConfiguration must have been filled with the certificates + // retrieve current webhook server cert secret + webhookServerSecret, err := clientset.CoreV1().Secrets(w.Namespace).Get(w.SecretName, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, 2, len(webhookServerSecret.Data)) + + // retrieve the current webhook configuration + webhookConfiguration, err := clientset.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(w.WebhookConfigurationName, metav1.GetOptions{}) + assert.NoError(t, err) + caBundle := webhookConfiguration.Webhooks[0].ClientConfig.CABundle + assert.True(t, len(caBundle) > 0) + + // Check that the cert in the secret has been signed by the caBundle + verifyCertificates(t, caBundle, webhookServerSecret.Data["tls.crt"]) + + // Delete the content of the secret, certificates should be recreated + webhookServerSecret.Data = map[string][]byte{} + _, err = clientset.CoreV1().Secrets(w.Namespace).Update(webhookServerSecret) + assert.NoError(t, err) + webhookServerSecret, err = clientset.CoreV1().Secrets(w.Namespace).Get(w.SecretName, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, 0, len(webhookServerSecret.Data)) + + if err := w.ReconcileResources(clientset); err != nil { + t.Errorf("Params.ReconcileResources() error = %v", err) + } + + webhookServerSecret, err = clientset.CoreV1().Secrets(w.Namespace).Get(w.SecretName, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, 2, len(webhookServerSecret.Data)) + + // retrieve the new ca + webhookConfiguration, err = clientset.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(w.WebhookConfigurationName, metav1.GetOptions{}) + assert.NoError(t, err) + caBundle = webhookConfiguration.Webhooks[0].ClientConfig.CABundle + // Check again that the cert in the secret has been signed by the caBundle + verifyCertificates(t, caBundle, webhookServerSecret.Data["tls.crt"]) + +} + +func verifyCertificates(t *testing.T, rootCert []byte, serverCert []byte) { + ca := x509.NewCertPool() + ok := ca.AppendCertsFromPEM(rootCert) + assert.True(t, ok) + block, _ := pem.Decode(serverCert) + assert.NotNil(t, block) + cert, err := x509.ParseCertificate(block.Bytes) + assert.NoError(t, err) + opts := x509.VerifyOptions{ + DNSName: ServerCertCommonName, + Roots: ca, + } + _, err = cert.Verify(opts) + assert.NoError(t, err) +} diff --git a/pkg/controller/webhook/webhook_certificates_controller.go b/pkg/controller/webhook/webhook_certificates_controller.go new file mode 100644 index 0000000000..2869dcd8a8 --- /dev/null +++ b/pkg/controller/webhook/webhook_certificates_controller.go @@ -0,0 +1,114 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package webhook + +import ( + "fmt" + "time" + + "github.com/elastic/cloud-on-k8s/pkg/controller/common" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/watches" + "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" + "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/controller" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +const ( + ControllerName = "webhook-certificates-controller" +) + +var log = logf.Log.WithName(ControllerName) + +var _ reconcile.Reconciler = &ReconcileWebhookResources{} + +// ReconcileWebhookResources reconciles the certificates used by the webhook server. +type ReconcileWebhookResources struct { + // k8s.Client is used to watch resources + k8s.Client + // iteration is the number of times this controller has run its Reconcile method + iteration uint64 + + // webhook parameters + webhookParams Params + // resources are updated with a native Kubernetes client + clientset kubernetes.Interface +} + +func (r *ReconcileWebhookResources) Reconcile(request reconcile.Request) (reconcile.Result, error) { + defer common.LogReconciliationRun(log, request, &r.iteration)() + if err := r.webhookParams.ReconcileResources(r.clientset); err != nil { + return reconcile.Result{}, err + } + + // Get the latest content of the webhook CA + webhookServerSecret, err := r.clientset.CoreV1().Secrets(r.webhookParams.Namespace).Get(r.webhookParams.SecretName, metav1.GetOptions{}) + if err != nil { + return reconcile.Result{}, err + } + serverCA := certificates.BuildCAFromSecret(*webhookServerSecret) + if serverCA == nil { + return reconcile.Result{}, fmt.Errorf("cannot find CA in webhook secret %s/%s", r.webhookParams.Namespace, r.webhookParams.SecretName) + } + + return reconcile.Result{ + RequeueAfter: certificates.ShouldRotateIn(time.Now(), serverCA.Cert.NotAfter, r.webhookParams.Rotation.RotateBefore), + }, nil +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager, webhookParams Params, clientset kubernetes.Interface) *ReconcileWebhookResources { + c := k8s.WrapClient(mgr.GetClient()) + return &ReconcileWebhookResources{ + Client: c, + webhookParams: webhookParams, + clientset: clientset, + } +} + +// Add adds a new Controller to mgr with r as the reconcile.Reconciler +func Add(mgr manager.Manager, webhookParams Params, clientset kubernetes.Interface) error { + r := newReconciler(mgr, webhookParams, clientset) + // Create a new controller + c, err := controller.New(ControllerName, mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + secret := types.NamespacedName{ + Namespace: webhookParams.Namespace, + Name: webhookParams.SecretName, + } + + if err := c.Watch(&source.Kind{Type: &corev1.Secret{}}, &watches.NamedWatch{ + Name: "webhook-server-cert", + Watched: []types.NamespacedName{secret}, + Watcher: secret, + }); err != nil { + return err + } + + webhookConfiguration := types.NamespacedName{ + Name: webhookParams.WebhookConfigurationName, + } + + if err := c.Watch(&source.Kind{Type: &v1beta1.ValidatingWebhookConfiguration{}}, &watches.NamedWatch{ + Name: "validatingwebhookconfiguration", + Watched: []types.NamespacedName{webhookConfiguration}, + Watcher: webhookConfiguration, + }); err != nil { + return err + } + + return nil +} From d9b496d0a77ade7f74badcec3b56dde8d7fef7b5 Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Mon, 18 Nov 2019 10:07:20 +0100 Subject: [PATCH 2/7] fix unit test --- pkg/controller/webhook/reconcile_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/controller/webhook/reconcile_test.go b/pkg/controller/webhook/reconcile_test.go index 7f73fe552b..631cbd1ad5 100644 --- a/pkg/controller/webhook/reconcile_test.go +++ b/pkg/controller/webhook/reconcile_test.go @@ -24,9 +24,9 @@ const ( func TestParams_ReconcileResources(t *testing.T) { w := Params{ - Namespace: "elastic-system", - SecretName: "elastic-webhook-server-cert", - //ServerDomainName: sampleServiceName, + Namespace: "elastic-system", + SecretName: "elastic-webhook-server-cert", + ServerDomainName: sampleServiceName, WebhookConfigurationName: "elastic-webhook.k8s.elastic.co", Rotation: certificates.RotationParams{ Validity: certificates.DefaultCertValidity, @@ -108,7 +108,7 @@ func verifyCertificates(t *testing.T, rootCert []byte, serverCert []byte) { cert, err := x509.ParseCertificate(block.Bytes) assert.NoError(t, err) opts := x509.VerifyOptions{ - DNSName: ServerCertCommonName, + DNSName: "elastic-webhook-server.elastic-system.svc", Roots: ca, } _, err = cert.Verify(opts) From 7e52b4339065131c7be26695cc9b7837531438d0 Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Mon, 18 Nov 2019 10:51:41 +0100 Subject: [PATCH 3/7] fix import --- cmd/manager/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index a55fa9bdd0..0772824968 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -12,8 +12,6 @@ import ( "strings" "time" - "github.com/elastic/cloud-on-k8s/pkg/controller/webhook" - // allow gcp authentication _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -29,6 +27,7 @@ import ( kbassn "github.com/elastic/cloud-on-k8s/pkg/controller/kibanaassociation" "github.com/elastic/cloud-on-k8s/pkg/controller/license" licensetrial "github.com/elastic/cloud-on-k8s/pkg/controller/license/trial" + "github.com/elastic/cloud-on-k8s/pkg/controller/webhook" "github.com/elastic/cloud-on-k8s/pkg/dev" "github.com/elastic/cloud-on-k8s/pkg/dev/portforward" "github.com/elastic/cloud-on-k8s/pkg/utils/net" From 291ba74ae5b04c305a2d38e0d09b6fad6dd9c331 Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Tue, 19 Nov 2019 09:06:49 +0100 Subject: [PATCH 4/7] changes from review --- cmd/manager/main.go | 79 +++++++++---------- pkg/controller/webhook/cert.go | 9 ++- pkg/controller/webhook/reconcile.go | 7 +- pkg/controller/webhook/reconcile_test.go | 5 -- .../webhook_certificates_controller.go | 20 +++-- 5 files changed, 68 insertions(+), 52 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 0772824968..939cff586f 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -38,13 +38,13 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager/signals" ) const ( MetricsPortFlag = "metrics-port" DefaultMetricPort = 0 // disabled - WebhookPort = 9443 AutoPortForwardFlagName = "auto-port-forward" NamespacesFlag = "namespaces" @@ -54,11 +54,12 @@ const ( CertValidityFlag = "cert-validity" CertRotateBeforeFlag = "cert-rotate-before" - AutoCertificatesWebhookFlag = "webhook-certificates-management" - OperatorNamespaceFlag = "operator-namespace" - WebhookSecretFlag = "webhook-secret" - WebhookDomainNameFlag = "webhook-domain-name" - DefaultWebhookServiceFlag = "elastic-webhook-server" + OperatorNamespaceFlag = "operator-namespace" + + SetupWebhookCertsFlag = "setup-webhook-certs" + WebhookSecretFlag = "webhook-secret" + WebhookConfigurationName = "elastic-webhook.k8s.elastic.co" + WebhookPort = 9443 DebugHTTPServerListenAddressFlag = "debug-http-listen" ) @@ -122,15 +123,10 @@ func init() { "Duration representing how long before expiration TLS certificates should be reissued", ) Cmd.Flags().Bool( - AutoCertificatesWebhookFlag, + SetupWebhookCertsFlag, true, "enables automatic certificates management for the webhook, the Secret and the ValidatingWebhookConfiguration must be created before running the operator", ) - Cmd.Flags().String( - WebhookDomainNameFlag, - DefaultWebhookServiceFlag, - "k8s service name used to automatically the webhook certificate", - ) Cmd.Flags().String( OperatorNamespaceFlag, "", @@ -282,34 +278,7 @@ func execute() { } if operator.HasRole(operator.WebhookServer, roles) { - autoWebhookCertificatesMngt := viper.GetBool(AutoCertificatesWebhookFlag) - if autoWebhookCertificatesMngt { - log.Info("Automatic management of the webhook certificates enabled") - // Ensure that all the certificates needed by the webhook server are already created - webhookParams := webhook.Params{ - Namespace: viper.GetString(OperatorNamespaceFlag), - SecretName: viper.GetString(WebhookSecretFlag), - ServerDomainName: viper.GetString(WebhookDomainNameFlag), - WebhookConfigurationName: "elastic-webhook.k8s.elastic.co", - Rotation: params.CertRotation, - } - - // Force a first reconciliation to create the resources before the server is started - if err := webhookParams.ReconcileResources(clientset); err != nil { - log.Error(err, "unable to setup and fill the webhook certificates") - os.Exit(1) - } - - if err = webhook.Add(mgr, webhookParams, clientset); err != nil { - log.Error(err, "unable to create controller", "controller", webhook.ControllerName) - os.Exit(1) - } - } - - if err = (&estype.Elasticsearch{}).SetupWebhookWithManager(mgr); err != nil { - log.Error(err, "unable to create webhook", "webhook", "Elasticsearch") - os.Exit(1) - } + setupWebhook(mgr, params.CertRotation, clientset) } if operator.HasRole(operator.NamespaceOperator, roles) { @@ -364,3 +333,33 @@ func ValidateCertExpirationFlags(validityFlag string, rotateBeforeFlag string) ( } return certValidity, certRotateBefore } + +func setupWebhook(mgr manager.Manager, certRotation certificates.RotationParams, clientset kubernetes.Interface) { + setupWebhookCerts := viper.GetBool(SetupWebhookCertsFlag) + if setupWebhookCerts { + log.Info("Automatic management of the webhook certificates enabled") + // Ensure that all the certificates needed by the webhook server are already created + webhookParams := webhook.Params{ + Namespace: viper.GetString(OperatorNamespaceFlag), + SecretName: viper.GetString(WebhookSecretFlag), + WebhookConfigurationName: WebhookConfigurationName, + Rotation: certRotation, + } + + // Force a first reconciliation to create the resources before the server is started + if err := webhookParams.ReconcileResources(clientset); err != nil { + log.Error(err, "unable to setup and fill the webhook certificates") + os.Exit(1) + } + + if err := webhook.Add(mgr, webhookParams, clientset); err != nil { + log.Error(err, "unable to create controller", "controller", webhook.ControllerName) + os.Exit(1) + } + } + + if err := (&estype.Elasticsearch{}).SetupWebhookWithManager(mgr); err != nil { + log.Error(err, "unable to create webhook", "webhook", "Elasticsearch") + os.Exit(1) + } +} diff --git a/pkg/controller/webhook/cert.go b/pkg/controller/webhook/cert.go index 2274cf0dbd..fe7edf3a31 100644 --- a/pkg/controller/webhook/cert.go +++ b/pkg/controller/webhook/cert.go @@ -18,6 +18,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const WebhookServiceName = "elastic-webhook-server" + // WebhookCertificates holds the artifacts used by the webhook server and the webhook configuration. type WebhookCertificates struct { caCert []byte @@ -59,6 +61,11 @@ func (w *Params) shouldRenewCertificates(serverCertificates *corev1.Secret, webh return false } +// newCertificates creates a new certificate authority and uses it to sign a new key/cert pair for the webhook server. +// The certificate of the CA is used in the webhook configuration so it can be used by the API server to verify the +// certificate of the webhook server. +// The private key is not retained or persisted, all the artifacts are regenerated and updated if needed when the +// certificate is about to expire or is missing. func (w *Params) newCertificates() (WebhookCertificates, error) { webhookCertificates := WebhookCertificates{} @@ -100,7 +107,7 @@ func (w *Params) newCertificates() (WebhookCertificates, error) { DNSNames: k8s.GetServiceDNSName(corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: w.Namespace, - Name: w.ServerDomainName, + Name: WebhookServiceName, }, }), diff --git a/pkg/controller/webhook/reconcile.go b/pkg/controller/webhook/reconcile.go index 438693789d..d0a360785a 100644 --- a/pkg/controller/webhook/reconcile.go +++ b/pkg/controller/webhook/reconcile.go @@ -14,7 +14,6 @@ import ( type Params struct { Namespace string SecretName string - ServerDomainName string WebhookConfigurationName string // Certificate options @@ -40,6 +39,12 @@ func (w *Params) ReconcileResources(clientset kubernetes.Interface) error { // check if we need to renew the certificates used in the resources if w.shouldRenewCertificates(webhookServerSecret, webhookConfiguration) { + log.Info( + "Creating new webhook certificates", + "webhook", webhookConfiguration.Name, + "secret_namespace", webhookServerSecret.Namespace, + "secret_name", webhookServerSecret.Name, + ) newCertificates, err := w.newCertificates() if err != nil { return err diff --git a/pkg/controller/webhook/reconcile_test.go b/pkg/controller/webhook/reconcile_test.go index 631cbd1ad5..3c55474faa 100644 --- a/pkg/controller/webhook/reconcile_test.go +++ b/pkg/controller/webhook/reconcile_test.go @@ -17,16 +17,11 @@ import ( "k8s.io/client-go/kubernetes/fake" ) -const ( - sampleServiceName = "elastic-webhook-server" -) - func TestParams_ReconcileResources(t *testing.T) { w := Params{ Namespace: "elastic-system", SecretName: "elastic-webhook-server-cert", - ServerDomainName: sampleServiceName, WebhookConfigurationName: "elastic-webhook.k8s.elastic.co", Rotation: certificates.RotationParams{ Validity: certificates.DefaultCertValidity, diff --git a/pkg/controller/webhook/webhook_certificates_controller.go b/pkg/controller/webhook/webhook_certificates_controller.go index 2869dcd8a8..4773c01e42 100644 --- a/pkg/controller/webhook/webhook_certificates_controller.go +++ b/pkg/controller/webhook/webhook_certificates_controller.go @@ -10,6 +10,7 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/controller/common" "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/reconciler" "github.com/elastic/cloud-on-k8s/pkg/controller/common/watches" "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" "k8s.io/api/admissionregistration/v1beta1" @@ -47,23 +48,32 @@ type ReconcileWebhookResources struct { func (r *ReconcileWebhookResources) Reconcile(request reconcile.Request) (reconcile.Result, error) { defer common.LogReconciliationRun(log, request, &r.iteration)() + res := r.reconcileInternal(request) + return res.Aggregate() +} + +func (r *ReconcileWebhookResources) reconcileInternal(request reconcile.Request) *reconciler.Results { + res := &reconciler.Results{} if err := r.webhookParams.ReconcileResources(r.clientset); err != nil { - return reconcile.Result{}, err + return res.WithError(err) } // Get the latest content of the webhook CA webhookServerSecret, err := r.clientset.CoreV1().Secrets(r.webhookParams.Namespace).Get(r.webhookParams.SecretName, metav1.GetOptions{}) if err != nil { - return reconcile.Result{}, err + return res.WithError(err) } serverCA := certificates.BuildCAFromSecret(*webhookServerSecret) if serverCA == nil { - return reconcile.Result{}, fmt.Errorf("cannot find CA in webhook secret %s/%s", r.webhookParams.Namespace, r.webhookParams.SecretName) + return res.WithError( + fmt.Errorf("cannot find CA in webhook secret %s/%s", r.webhookParams.Namespace, r.webhookParams.SecretName), + ) } - return reconcile.Result{ + res.WithResult(reconcile.Result{ RequeueAfter: certificates.ShouldRotateIn(time.Now(), serverCA.Cert.NotAfter, r.webhookParams.Rotation.RotateBefore), - }, nil + }) + return res } // newReconciler returns a new reconcile.Reconciler From 22eed5960928f65b84c3278f5de67b176a576e63 Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Tue, 19 Nov 2019 12:05:32 +0100 Subject: [PATCH 5/7] make linter happy --- pkg/controller/webhook/webhook_certificates_controller.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controller/webhook/webhook_certificates_controller.go b/pkg/controller/webhook/webhook_certificates_controller.go index 4773c01e42..01cd181798 100644 --- a/pkg/controller/webhook/webhook_certificates_controller.go +++ b/pkg/controller/webhook/webhook_certificates_controller.go @@ -48,11 +48,11 @@ type ReconcileWebhookResources struct { func (r *ReconcileWebhookResources) Reconcile(request reconcile.Request) (reconcile.Result, error) { defer common.LogReconciliationRun(log, request, &r.iteration)() - res := r.reconcileInternal(request) + res := r.reconcileInternal() return res.Aggregate() } -func (r *ReconcileWebhookResources) reconcileInternal(request reconcile.Request) *reconciler.Results { +func (r *ReconcileWebhookResources) reconcileInternal() *reconciler.Results { res := &reconciler.Results{} if err := r.webhookParams.ReconcileResources(r.clientset); err != nil { return res.WithError(err) From 73c4a67b8b3c5738372867638d5d3ebc3fe739dc Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Wed, 20 Nov 2019 15:13:42 +0100 Subject: [PATCH 6/7] setup-webhook-certs => manage-webhook-certs --- cmd/manager/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 939cff586f..0f69c3ba30 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -56,7 +56,7 @@ const ( OperatorNamespaceFlag = "operator-namespace" - SetupWebhookCertsFlag = "setup-webhook-certs" + ManageWebhookCertsFlag = "manage-webhook-certs" WebhookSecretFlag = "webhook-secret" WebhookConfigurationName = "elastic-webhook.k8s.elastic.co" WebhookPort = 9443 @@ -123,7 +123,7 @@ func init() { "Duration representing how long before expiration TLS certificates should be reissued", ) Cmd.Flags().Bool( - SetupWebhookCertsFlag, + ManageWebhookCertsFlag, true, "enables automatic certificates management for the webhook, the Secret and the ValidatingWebhookConfiguration must be created before running the operator", ) @@ -335,8 +335,8 @@ func ValidateCertExpirationFlags(validityFlag string, rotateBeforeFlag string) ( } func setupWebhook(mgr manager.Manager, certRotation certificates.RotationParams, clientset kubernetes.Interface) { - setupWebhookCerts := viper.GetBool(SetupWebhookCertsFlag) - if setupWebhookCerts { + manageWebhookCerts := viper.GetBool(ManageWebhookCertsFlag) + if manageWebhookCerts { log.Info("Automatic management of the webhook certificates enabled") // Ensure that all the certificates needed by the webhook server are already created webhookParams := webhook.Params{ From decb46c263d9587a41ad438332c93ca0939dec4c Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Wed, 20 Nov 2019 15:40:13 +0100 Subject: [PATCH 7/7] update usage --- cmd/manager/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 0f69c3ba30..5c0d438f4b 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -125,7 +125,7 @@ func init() { Cmd.Flags().Bool( ManageWebhookCertsFlag, true, - "enables automatic certificates management for the webhook, the Secret and the ValidatingWebhookConfiguration must be created before running the operator", + "enables automatic certificates management for the webhook. The Secret and the ValidatingWebhookConfiguration must be created before running the operator", ) Cmd.Flags().String( OperatorNamespaceFlag,