Check certificate fingerprint when deciding if memory store needs to be updated

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 <brad.davidson@rancher.com>
This commit is contained in:
Brad Davidson 2025-06-11 08:49:01 +00:00
parent 74cd23ce3f
commit 74721696ea
No known key found for this signature in database
GPG Key ID: FFB7A9376A9349B9
3 changed files with 21 additions and 4 deletions

View File

@ -17,4 +17,4 @@ jobs:
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: 'go.mod'
- run: go test -race -cover ./...
- run: go test -v -race -cover ./...

View File

@ -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
}

View File

@ -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
}