diff --git a/pkg/tls/certRenewer.go b/pkg/tls/certRenewer.go index 342d72333f..54158fe095 100644 --- a/pkg/tls/certRenewer.go +++ b/pkg/tls/certRenewer.go @@ -14,6 +14,7 @@ import ( client "github.com/kyverno/kyverno/pkg/dclient" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" + k8errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/rest" @@ -21,7 +22,8 @@ import ( const ( // ManagedByLabel is added to Kyverno managed secrets - ManagedByLabel string = "cert.kyverno.io/managed-by" + ManagedByLabel string = "cert.kyverno.io/managed-by" + MasterDeploymentUID string = "cert.kyverno.io/master-deployment-uid" SelfSignedAnnotation string = "self-signed-cert" RootCAKey string = "rootCA.crt" @@ -117,32 +119,58 @@ func (c *CertRenewer) WriteCACertToSecret(caPEM *PemPair, props CertificateProps logger := c.log.WithName("CAcert") name := generateRootCASecretName(props) - secretUnstr, err := c.client.GetResource("", "Secret", props.Namespace, name) - if err != nil { - secret := &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: props.Namespace, - Annotations: map[string]string{ - SelfSignedAnnotation: "true", - }, - Labels: map[string]string{ - ManagedByLabel: "kyverno", - }, - }, - Data: map[string][]byte{ - RootCAKey: caPEM.Certificate, - }, - Type: v1.SecretTypeOpaque, - } + depl, err := c.client.GetResource("", "Deployment", props.Namespace, config.KyvernoDeploymentName) - _, err := c.client.CreateResource("", "Secret", props.Namespace, secret, false) + deplHash := "" + if err == nil { + deplHash = fmt.Sprintf("%v", depl.GetUID()) + } + + var deplHashSec string = "default" + var ok, managedByKyverno bool + + secretUnstr, err := c.client.GetResource("", "Secret", props.Namespace, name) + if err == nil { + if label, ok := secretUnstr.GetLabels()[ManagedByLabel]; ok { + managedByKyverno = label == "kyverno" + } + deplHashSec, ok = secretUnstr.GetAnnotations()[MasterDeploymentUID] + } + + secret := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: props.Namespace, + Annotations: map[string]string{ + SelfSignedAnnotation: "true", + MasterDeploymentUID: deplHash, + }, + Labels: map[string]string{ + ManagedByLabel: "kyverno", + }, + }, + Data: map[string][]byte{ + RootCAKey: caPEM.Certificate, + }, + Type: v1.SecretTypeOpaque, + } + + if err != nil { + if k8errors.IsNotFound(err) { + _, err = c.client.CreateResource("", "Secret", props.Namespace, secret, false) + if err == nil { + logger.Info("secret created", "name", name, "namespace", props.Namespace) + } + } + return err + } else if managedByKyverno && (!ok || deplHashSec != deplHash) { + _, err = c.client.UpdateResource("", "Secret", props.Namespace, secret, false) if err == nil { - logger.Info("secret created", "name", name, "namespace", props.Namespace) + logger.Info("secret updated", "name", name, "namespace", props.Namespace) } return err } @@ -154,7 +182,7 @@ func (c *CertRenewer) WriteCACertToSecret(caPEM *PemPair, props CertificateProps dataMap := map[string]interface{}{ RootCAKey: base64.StdEncoding.EncodeToString(caPEM.Certificate)} - if err := unstructured.SetNestedMap(secretUnstr.Object, dataMap, "data"); err != nil { + if err = unstructured.SetNestedMap(secretUnstr.Object, dataMap, "data"); err != nil { return err } @@ -172,30 +200,59 @@ func (c *CertRenewer) WriteTLSPairToSecret(props CertificateProps, pemPair *PemP logger := c.log.WithName("WriteTLSPair") name := generateTLSPairSecretName(props) - secretUnstr, err := c.client.GetResource("", "Secret", props.Namespace, name) - if err != nil { - secret := &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: props.Namespace, - Labels: map[string]string{ - ManagedByLabel: "kyverno", - }, - }, - Data: map[string][]byte{ - v1.TLSCertKey: pemPair.Certificate, - v1.TLSPrivateKeyKey: pemPair.PrivateKey, - }, - Type: v1.SecretTypeTLS, - } - _, err := c.client.CreateResource("", "Secret", props.Namespace, secret, false) + depl, err := c.client.GetResource("", "Deployment", props.Namespace, config.KyvernoDeploymentName) + + deplHash := "" + if err == nil { + deplHash = fmt.Sprintf("%v", depl.GetUID()) + } + + var deplHashSec string = "default" + var ok, managedByKyverno bool + + secretUnstr, err := c.client.GetResource("", "Secret", props.Namespace, name) + if err == nil { + if label, ok := secretUnstr.GetLabels()[ManagedByLabel]; ok { + managedByKyverno = label == "kyverno" + } + deplHashSec, ok = secretUnstr.GetAnnotations()[MasterDeploymentUID] + } + + secretPtr := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: props.Namespace, + Annotations: map[string]string{ + MasterDeploymentUID: deplHash, + }, + Labels: map[string]string{ + ManagedByLabel: "kyverno", + }, + }, + Data: map[string][]byte{ + v1.TLSCertKey: pemPair.Certificate, + v1.TLSPrivateKeyKey: pemPair.PrivateKey, + }, + Type: v1.SecretTypeTLS, + } + + if err != nil { + if k8errors.IsNotFound(err) { + _, err = c.client.CreateResource("", "Secret", props.Namespace, secretPtr, false) + if err == nil { + logger.Info("secret created", "name", name, "namespace", props.Namespace) + } + } + return err + } else if managedByKyverno && (!ok || deplHashSec != deplHash) { + _, err = c.client.UpdateResource("", "Secret", props.Namespace, secretPtr, false) if err == nil { - logger.Info("secret created", "name", name, "namespace", props.Namespace) + logger.Info("secret updated", "name", name, "namespace", props.Namespace) } return err } diff --git a/pkg/tls/reader.go b/pkg/tls/reader.go index 05ddeccc85..e8ca0fedbf 100644 --- a/pkg/tls/reader.go +++ b/pkg/tls/reader.go @@ -22,12 +22,30 @@ func ReadRootCASecret(restConfig *rest.Config, client *client.Client) (result [] return nil, errors.Wrap(err, "failed to get TLS Cert Properties") } + depl, err := client.GetResource("", "Deployment", certProps.Namespace, config.KyvernoDeploymentName) + + deplHash := "" + if err == nil { + deplHash = fmt.Sprintf("%v", depl.GetUID()) + } + + var deplHashSec string = "default" + var ok, managedByKyverno bool + sname := generateRootCASecretName(certProps) stlsca, err := client.GetResource("", "Secret", certProps.Namespace, sname) if err != nil { return nil, err } + if label, ok := stlsca.GetLabels()[ManagedByLabel]; ok { + managedByKyverno = label == "kyverno" + } + deplHashSec, ok = stlsca.GetAnnotations()[MasterDeploymentUID] + if managedByKyverno && (!ok || deplHashSec != deplHash) { + return nil, fmt.Errorf("outdated secret") + } + tlsca, err := convertToSecret(stlsca) if err != nil { return nil, errors.Wrapf(err, "failed to convert secret %s/%s", certProps.Namespace, sname) @@ -48,11 +66,28 @@ func ReadTLSPair(restConfig *rest.Config, client *client.Client) (*PemPair, erro return nil, errors.Wrap(err, "failed to get TLS Cert Properties") } + depl, err := client.GetResource("", "Deployment", certProps.Namespace, config.KyvernoDeploymentName) + + deplHash := "" + if err == nil { + deplHash = fmt.Sprintf("%v", depl.GetUID()) + } + + var deplHashSec string = "default" + var ok, managedByKyverno bool + sname := generateTLSPairSecretName(certProps) unstrSecret, err := client.GetResource("", "Secret", certProps.Namespace, sname) if err != nil { return nil, fmt.Errorf("failed to get secret %s/%s: %v", certProps.Namespace, sname, err) } + if label, ok := unstrSecret.GetLabels()[ManagedByLabel]; ok { + managedByKyverno = label == "kyverno" + } + deplHashSec, ok = unstrSecret.GetAnnotations()[MasterDeploymentUID] + if managedByKyverno && (!ok || deplHashSec != deplHash) { + return nil, fmt.Errorf("outdated secret") + } // If secret contains annotation 'self-signed-cert', then it's created using helper scripts to setup self-signed certificates. // As the root CA used to sign the certificate is required for webhook configuration, check if the corresponding secret is created diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go index d25ddc997c..a9825e5ea1 100644 --- a/pkg/webhookconfig/registration.go +++ b/pkg/webhookconfig/registration.go @@ -248,10 +248,9 @@ func (wrc *Register) cleanupKyvernoResource() bool { logger := wrc.log.WithName("cleanupKyvernoResource") deploy, err := wrc.client.GetResource("", "Deployment", deployNamespace, deployName) if err != nil { - logger.Error(err, "failed to get deployment, cleanup kyverno resources anyway") - return true + logger.Error(err, "failed to get deployment, not cleaning up kyverno resources") + return false } - if deploy.GetDeletionTimestamp() != nil { logger.Info("Kyverno is terminating, cleanup Kyverno resources") return true