ipn/store/kubestore: don't load write replica certs in memory (#18395)

Fixes a bug where, for kube HA proxies, TLS certs for the replica
responsible for cert issuance where loaded in memory on startup,
although the in-memory store was not updated after renewal (to
avoid failing re-issuance for re-created Ingresses).
Now the 'write' replica always reads certs from the kube Secret.

Updates tailscale/tailscale#18394

Signed-off-by: Irbe Krumina <irbekrm@gmail.com>
This commit is contained in:
Irbe Krumina
2026-01-13 13:43:17 +01:00
committed by GitHub
parent 87e108e10c
commit 8c17d871b3
2 changed files with 9 additions and 9 deletions
+6 -2
View File
@@ -110,8 +110,12 @@ func newWithClient(logf logger.Logf, c kubeclient.Client, secretName string) (*S
if err := s.loadState(); err != nil && err != ipn.ErrStateNotExist { if err := s.loadState(); err != nil && err != ipn.ErrStateNotExist {
return nil, fmt.Errorf("error loading state from kube Secret: %w", err) return nil, fmt.Errorf("error loading state from kube Secret: %w", err)
} }
// If we are in cert share mode, pre-load existing shared certs. // If we are in read-only cert share mode, pre-load existing shared certs.
if s.certShareMode == "rw" || s.certShareMode == "ro" { // Write replicas never load certs in-memory to avoid a situation where,
// after Ingress recreation (and the associated cert Secret recreation), new
// TLS certs don't get issued because the write replica still has certs
// in-memory. Instead, write replicas fetch certs from Secret on each request.
if s.certShareMode == "ro" {
sel := s.certSecretSelector() sel := s.certSecretSelector()
if err := s.loadCerts(context.Background(), sel); err != nil { if err := s.loadCerts(context.Background(), sel); err != nil {
// We will attempt to again retrieve the certs from Secrets when a request for an HTTPS endpoint // We will attempt to again retrieve the certs from Secrets when a request for an HTTPS endpoint
+1 -5
View File
@@ -688,7 +688,7 @@ func TestNewWithClient(t *testing.T) {
}, },
}, },
{ {
name: "load_select_certs_in_read_write_mode", name: "do_not_load_certs_in_read_write_mode",
certMode: "rw", certMode: "rw",
stateSecretContents: map[string][]byte{ stateSecretContents: map[string][]byte{
"foo": []byte("bar"), "foo": []byte("bar"),
@@ -705,10 +705,6 @@ func TestNewWithClient(t *testing.T) {
}, },
wantMemoryStoreContents: map[ipn.StateKey][]byte{ wantMemoryStoreContents: map[ipn.StateKey][]byte{
"foo": []byte("bar"), "foo": []byte("bar"),
"app1.tailnetxyz.ts.net.crt": []byte(testCert + "1"),
"app1.tailnetxyz.ts.net.key": []byte(testKey + "1"),
"app2.tailnetxyz.ts.net.crt": []byte(testCert + "2"),
"app2.tailnetxyz.ts.net.key": []byte(testKey + "2"),
}, },
}, },
{ {