ipn/store/kubestore: omit ARI replaces for unstamped shared certs (#22053)

A retained legacy cert may have been issued by a different account even when no pre-adoption local key is available. Require a matching fingerprint before claiming a shared-account renewal replaces it.

Fixes #22041

Signed-off-by: Jaron Kramer <info@jkramertech.com>
This commit is contained in:
Jaron Kramer authored and GitHub committed 2026-10-31 11:43:17 +00:00
1 parent 1334899864
commit 6c6dd62b60
2 files changed
+36 -8

No files matched your search

+5 -3
View File
@@ -284,9 +284,11 @@ func (s *Store) ShouldUseARIReplacesForRenewal(domain string) (bool, error) {
}
certFP := sec.Data[keyACMEAcctFP]
if len(certFP) == 0 {
// Legacy cert, no fingerprint stamp. Assume misaligned only if
// we adopted a foreign shared key.
if len(s.preAdoptedLocalKey) > 0 {
// A legacy cert has no record of its issuing account. With a
// shared account, the current key may have been created or adopted
// after this cert was issued, even without a prior local key.
// Omit replaces until a renewal stamps the issuing fingerprint.
if s.acmeAccountsSecretName != "" || len(s.preAdoptedLocalKey) > 0 {
return false, nil
}
return true, nil
+31 -5
View File
@@ -1107,6 +1107,7 @@ func TestShouldUseARIReplacesForRenewal(t *testing.T) {
tests := []struct {
name string
certShareMode string
sharedSecret string
acmeInMemory []byte // per-pod ACME key present in memory
preAdopted []byte // sha256 of pre-adoption local key (foreign-key path)
certSecret map[string][]byte
@@ -1173,6 +1174,30 @@ func TestShouldUseARIReplacesForRenewal(t *testing.T) {
certSecret: map[string][]byte{}, // no fingerprint field
want: false,
},
{
name: "legacy_cert_with_shared_account_returns_false",
certShareMode: "rw",
sharedSecret: "tailscale-acme-accounts",
acmeInMemory: acmeKey,
certSecret: map[string][]byte{},
want: false,
},
{
name: "shared_account_fingerprint_matches",
certShareMode: "rw",
sharedSecret: "tailscale-acme-accounts",
acmeInMemory: acmeKey,
certSecret: map[string][]byte{keyACMEAcctFP: curFP},
want: true,
},
{
name: "shared_account_fingerprint_differs",
certShareMode: "rw",
sharedSecret: "tailscale-acme-accounts",
acmeInMemory: acmeKey,
certSecret: map[string][]byte{keyACMEAcctFP: otherFP},
want: false,
},
}
for _, tt := range tests {
@@ -1186,11 +1211,12 @@ func TestShouldUseARIReplacesForRenewal(t *testing.T) {
},
}
s := &Store{
client: client,
certShareMode: tt.certShareMode,
memory: mem.Store{},
preAdoptedLocalKey: tt.preAdopted,
logf: t.Logf,
client: client,
certShareMode: tt.certShareMode,
acmeAccountsSecretName: tt.sharedSecret,
memory: mem.Store{},
preAdoptedLocalKey: tt.preAdopted,
logf: t.Logf,
}
if len(tt.acmeInMemory) > 0 {
s.memory.WriteState(ipn.StateKey(acmeAccountStateKey), tt.acmeInMemory)