From 2ee4a168468261822a932e485dd61496add2b2a7 Mon Sep 17 00:00:00 2001 From: Brad Davidson Date: Wed, 11 Jun 2025 10:52:11 -0700 Subject: [PATCH] Check certificate fingerprint when deciding if memory store needs to be updated (#180) When using a chained store of Kubernetes -> Memory -> File, a file-backed cert with a valid ResourceVersion could not be updated when the Kubernetes store was offline, as the Memory store was skipping the update if the ResourceVersion was not changed. The Kubernetes store passes through the secret update without a modified ResourceVersion if the Secret controller is not yet available to round-trip the secret through the apiserver, as the apiserver is what handles updating the ResourceVersion when the Secret changes. In RKE2, this caused a deadlock on startup when the certificate is expired, as the apiserver cannot be started until the cert is updated, but the cert cannot be updated until the apiserver is up. Fix this by also considering the certificate hash annotation when deciding if the update can be skipped. Signed-off-by: Brad Davidson (cherry picked from commit 242c2af2db15d1c29283f31efcfff4ae9d5fa0da) Signed-off-by: Brad Davidson --- .github/workflows/ci.yaml | 2 +- factory/gen.go | 4 ++-- storage/memory/memory.go | 19 ++++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8efb9c6..b5da639 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,4 +17,4 @@ jobs: uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - - run: go test -race -cover ./... + - run: go test -v -race -cover ./... diff --git a/factory/gen.go b/factory/gen.go index 7de72f1..9888a3e 100644 --- a/factory/gen.go +++ b/factory/gen.go @@ -25,7 +25,7 @@ import ( const ( cnPrefix = "listener.cattle.io/cn-" Static = "listener.cattle.io/static" - fingerprint = "listener.cattle.io/fingerprint" + Fingerprint = "listener.cattle.io/fingerprint" ) var ( @@ -189,7 +189,7 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e secret.Type = v1.SecretTypeTLS secret.Data[v1.TLSCertKey] = certBytes secret.Data[v1.TLSPrivateKeyKey] = keyBytes - secret.Annotations[fingerprint] = fmt.Sprintf("SHA1=%X", sha1.Sum(newCert.Raw)) + secret.Annotations[Fingerprint] = fmt.Sprintf("SHA1=%X", sha1.Sum(newCert.Raw)) return secret, true, nil } diff --git a/storage/memory/memory.go b/storage/memory/memory.go index cf73996..ae2e920 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -2,6 +2,7 @@ package memory import ( "github.com/rancher/dynamiclistener" + "github.com/rancher/dynamiclistener/factory" "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" ) @@ -32,7 +33,7 @@ func (m *memory) Get() (*v1.Secret, error) { } func (m *memory) Update(secret *v1.Secret) error { - if m.secret == nil || m.secret.ResourceVersion == "" || m.secret.ResourceVersion != secret.ResourceVersion { + if isChanged(m.secret, secret) { if m.storage != nil { if err := m.storage.Update(secret); err != nil { return err @@ -44,3 +45,19 @@ func (m *memory) Update(secret *v1.Secret) error { } return nil } + +func isChanged(old, new *v1.Secret) bool { + if old == nil { + return true + } + if old.ResourceVersion == "" { + return true + } + if old.ResourceVersion != new.ResourceVersion { + return true + } + if old.Annotations[factory.Fingerprint] != new.Annotations[factory.Fingerprint] { + return true + } + return false +}