From 88c5b73e19b2cdf46877d101b24f958ea5038004 Mon Sep 17 00:00:00 2001 From: Kuromesi Date: Sat, 6 Jul 2024 11:39:30 +0800 Subject: [PATCH] add renewBefore to validate certs Signed-off-by: Kuromesi --- pkg/webhook/util/util.go | 34 +++++++++++++++++++++++++++ pkg/webhook/util/writer/certwriter.go | 7 +++--- pkg/webhook/util/writer/external.go | 26 ++++++++++++++++++-- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/pkg/webhook/util/util.go b/pkg/webhook/util/util.go index 442c25b268..c1018dd1a6 100644 --- a/pkg/webhook/util/util.go +++ b/pkg/webhook/util/util.go @@ -19,6 +19,7 @@ package util import ( "os" "strconv" + "time" "k8s.io/klog/v2" @@ -69,3 +70,36 @@ func GetCertDir() string { func GetCertWriter() string { return os.Getenv("WEBHOOK_CERT_WRITER") } + +var ( + renewBefore time.Duration +) + +func GetRenewBeforeTime() time.Duration { + if renewBefore != 0 { + return renewBefore + } + renewBefore = 6 * 30 * 24 * time.Hour + if s := os.Getenv("CERTS_RENEW_BEFORE"); len(s) > 0 { + t, err := strconv.Atoi(s[0 : len(s)-1]) + if err != nil { + klog.Errorf("failed to parese time %s: %v", s[0:len(s)-1], err) + return renewBefore + } + suffix := s[len(s)-1] + if suffix == 'd' { + renewBefore = time.Duration(t) * 7 * time.Hour + } else if suffix == 'm' { + renewBefore = time.Duration(t) * 30 * time.Hour + } else if suffix == 'y' { + renewBefore = time.Duration(t) * 365 * time.Hour + } else { + klog.Errorf("unknown date suffix %c", suffix) + } + } + if renewBefore <= 0 { + klog.Error("renewBefore time can not be less or equal than 0") + renewBefore = 6 * 30 * 24 * time.Hour + } + return renewBefore +} diff --git a/pkg/webhook/util/writer/certwriter.go b/pkg/webhook/util/writer/certwriter.go index 7fe3271d84..ec77e11d49 100644 --- a/pkg/webhook/util/writer/certwriter.go +++ b/pkg/webhook/util/writer/certwriter.go @@ -23,6 +23,7 @@ import ( "k8s.io/klog/v2" + "github.com/openkruise/kruise/pkg/webhook/util" "github.com/openkruise/kruise/pkg/webhook/util/generator" ) @@ -61,7 +62,8 @@ func handleCommon(dnsName string, ch certReadWriter) (*generator.Artifacts, bool } // Recreate the cert if it's invalid. - valid := validCert(certs, dnsName) + renewBefore := util.GetRenewBeforeTime() + valid := validCert(certs, dnsName, time.Now().Add(renewBefore)) if !valid { klog.Info("cert is invalid or expired, regenerating a new one") certs, err = ch.overwrite(certs.ResourceVersion) @@ -98,10 +100,9 @@ type certReadWriter interface { overwrite(resourceVersion string) (*generator.Artifacts, error) } -func validCert(certs *generator.Artifacts, dnsName string) bool { +func validCert(certs *generator.Artifacts, dnsName string, expired time.Time) bool { if certs == nil { return false } - expired := time.Now().AddDate(0, 6, 0) return generator.ValidCACert(certs.Key, certs.Cert, certs.CACert, dnsName, expired) } diff --git a/pkg/webhook/util/writer/external.go b/pkg/webhook/util/writer/external.go index 522cadfeb8..e914fbc6d5 100644 --- a/pkg/webhook/util/writer/external.go +++ b/pkg/webhook/util/writer/external.go @@ -18,16 +18,20 @@ limitations under the License. package writer import ( + "bytes" "context" "errors" "fmt" + "time" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + "github.com/openkruise/kruise/pkg/webhook/util" "github.com/openkruise/kruise/pkg/webhook/util/generator" ) @@ -39,6 +43,8 @@ const ( ExternalServerKey = "tls.key" ) +var currentExternalCerts *generator.Artifacts + // externalCertWriter provisions the certificate by reading from the k8s secrets. type externalCertWriter struct { *ExternalCertWriterOptions @@ -78,16 +84,25 @@ func NewExternalCertWriter(ops ExternalCertWriterOptions) (CertWriter, error) { // EnsureCert read and validate certs from k8s secret. func (s *externalCertWriter) EnsureCert(dnsName string) (*generator.Artifacts, bool, error) { - // Create or refresh the certs based on clientConfig + // Read certs from secrets generated by external s.dnsName = dnsName certs, err := s.read() if err != nil { return nil, false, err } - valid := validCert(certs, dnsName) + // check if the certs are expired + renewBefore := util.GetRenewBeforeTime() + valid := validCert(certs, dnsName, time.Now().Add(renewBefore)) if !valid { return nil, false, fmt.Errorf("cert is invalid or expired in secret %s/%s", s.Secret.Namespace, s.Secret.Name) } + // check if the certs are updated since last read + if currentExternalCerts != nil && compareCerts(certs, currentExternalCerts) { + klog.Info("external certs are not updated") + return certs, false, nil + } + + currentExternalCerts = certs return certs, true, nil } @@ -125,3 +140,10 @@ func externalSecretToCerts(secret *corev1.Secret) *generator.Artifacts { } return ret } + +func compareCerts(certsA, certsB *generator.Artifacts) bool { + if !bytes.Equal(certsA.CACert, certsB.CACert) || !bytes.Equal(certsA.CAKey, certsB.CAKey) || !bytes.Equal(certsA.Cert, certsB.Cert) || !bytes.Equal(certsA.Key, certsB.Key) { + return false + } + return true +}