Skip to content

Commit

Permalink
Add validity check for certificates on webhook startup (#592)
Browse files Browse the repository at this point in the history
* Add validity check on webhook startup

* Fix unit tests

* Revert imports

* Add constants from certificates controller

(cherry picked from commit b02abbb)
  • Loading branch information
luhi-DT authored and chrismuellner committed Mar 8, 2022
1 parent 405ef56 commit 259b8c3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 25 deletions.
35 changes: 25 additions & 10 deletions src/cmd/operator/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ import (
"path/filepath"
"time"

"github.com/Dynatrace/dynatrace-operator/src/controllers/certificates"
"github.com/Dynatrace/dynatrace-operator/src/kubeobjects"
"github.com/spf13/afero"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
)

const certificateRenewalInterval = 6 * time.Hour

type certificateWatcher struct {
apiReader client.Reader
fs afero.Fs
Expand All @@ -48,7 +52,7 @@ func newCertificateWatcher(mgr manager.Manager, namespace string, secretName str

func (watcher *certificateWatcher) watchForCertificatesSecret() {
for {
<-time.After(6 * time.Hour)
<-time.After(certificateRenewalInterval)
log.Info("checking for new certificates")
if updated, err := watcher.updateCertificatesFromSecret(); err != nil {
log.Info("failed to update certificates", "error", err)
Expand All @@ -74,19 +78,30 @@ func (watcher *certificateWatcher) updateCertificatesFromSecret() (bool, error)
}
}

for _, filename := range []string{"tls.crt", "tls.key"} {
f := filepath.Join(watcher.certificateDirectory, filename)
for _, filename := range []string{certificates.ServerCert, certificates.ServerKey} {
if _, err = watcher.ensureCertificateFile(secret, filename); err != nil {
return false, err
}
}
isValid, err := kubeobjects.ValidateCertificateExpiration(secret.Data[certificates.ServerCert], certificateRenewalInterval, time.Now(), log)
if err != nil {
return false, err
} else if !isValid {
return false, fmt.Errorf("certificate is outdated")
}
return true, nil
}

data, err := afero.ReadFile(watcher.fs, f)
func (watcher *certificateWatcher) ensureCertificateFile(secret corev1.Secret, filename string) (bool, error) {
f := filepath.Join(watcher.certificateDirectory, filename)

if os.IsNotExist(err) || !bytes.Equal(data, secret.Data[filename]) {
if err := afero.WriteFile(watcher.fs, f, secret.Data[filename], 0666); err != nil {
return false, err
}
} else {
data, err := afero.ReadFile(watcher.fs, f)
if os.IsNotExist(err) || !bytes.Equal(data, secret.Data[filename]) {
if err := afero.WriteFile(watcher.fs, f, secret.Data[filename], 0666); err != nil {
return false, err
}
} else {
return false, err
}

return true, nil
}
20 changes: 5 additions & 15 deletions src/controllers/certificates/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"fmt"
"math/big"
"time"

"github.com/Dynatrace/dynatrace-operator/src/kubeobjects"
)

var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
Expand Down Expand Up @@ -101,23 +103,11 @@ func (cs *Certs) validateServerCerts(now time.Time) bool {
return true
}

block, _ := pem.Decode(cs.Data[ServerCert])
if block == nil {
log.Info("failed to parse server certificates, renewing", "error", "can't decode PEM file")
return true
}

cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Info("failed to parse server certificates, renewing", "error", err)
isValid, err := kubeobjects.ValidateCertificateExpiration(cs.Data[ServerCert], renewalThreshold, now, log)
if err != nil || !isValid {
log.Info("server certificate failed to parse or is outdated")
return true
}

if now.After(cert.NotAfter.Add(-renewalThreshold)) {
log.Info("server certificates are about to expire, renewing", "current", now, "expiration", cert.NotAfter)
return true
}

return false
}

Expand Down
23 changes: 23 additions & 0 deletions src/kubeobjects/certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kubeobjects

import (
"crypto/x509"
"encoding/pem"
"time"

"github.com/go-logr/logr"
)

func ValidateCertificateExpiration(certData []byte, renewalThreshold time.Duration, now time.Time, log logr.Logger) (bool, error) {
if block, _ := pem.Decode(certData); block == nil {
log.Info("failed to parse certificate", "error", "can't decode PEM file")
return false, nil
} else if cert, err := x509.ParseCertificate(block.Bytes); err != nil {
log.Info("failed to parse certificate", "error", err)
return false, err
} else if now.After(cert.NotAfter.Add(-renewalThreshold)) {
log.Info("certificate is outdated, waiting for new ones", "Valid until", cert.NotAfter.UTC())
return false, nil
}
return true, nil
}

0 comments on commit 259b8c3

Please sign in to comment.