Skip to content

Commit f5954ef

Browse files
authored
Merge pull request #151141 from angles-n-daemons/backport24.3-151041
release-24.3: security: fix memory accounting issue in client certificate cache
2 parents 45282a7 + 78df4a1 commit f5954ef

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

pkg/security/clientcert/cert_expiry_cache.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ var ClientCertExpirationCacheCapacity = settings.RegisterIntSetting(
3333
1000,
3434
settings.WithPublic)
3535

36+
var certCacheMemLimit = settings.RegisterByteSizeSetting(
37+
settings.ApplicationLevel,
38+
"security.client_cert.cache_memory_limit",
39+
"memory limit for the client certificate expiration cache",
40+
1<<29, // 512MiB
41+
)
42+
3643
// certInfo holds information about a certificate, including its expiration
3744
// time and the last time it was seen.
3845
type certInfo struct {
@@ -89,8 +96,9 @@ func NewClientCertExpirationCache(
8996
}
9097

9198
c.mu.cache = make(map[string]map[string]certInfo)
99+
limit := certCacheMemLimit.Get(&st.SV)
92100
c.mon = mon.NewMonitorInheritWithLimit(
93-
"client-expiration-cache", 0 /* limit */, parentMon, true, /* longLiving */
101+
"client-expiration-cache", limit, parentMon, true, /* longLiving */
94102
)
95103
c.mu.acc = c.mon.MakeBoundAccount()
96104
c.mon.StartNoReserved(ctx, parentMon)
@@ -158,16 +166,19 @@ func (c *ClientCertExpirationCache) MaybeUpsert(
158166
if _, ok := c.mu.cache[user]; !ok {
159167
err := c.mu.acc.Grow(ctx, 2*GaugeSize)
160168
if err != nil {
161-
log.Ops.Warningf(ctx, "no memory available to cache cert expiry: %v", err)
169+
log.Warningf(ctx, "no memory available to cache cert expiry: %v", err)
162170
return
163171
}
164172
c.mu.cache[user] = map[string]certInfo{}
165173
}
166174

167-
err := c.mu.acc.Grow(ctx, CertInfoSize)
168-
if err != nil {
169-
log.Warningf(ctx, "no memory available to cache cert expiry: %v", err)
170-
c.evictLocked(ctx, user, serial)
175+
// if the serial hasn't been seen, report it in the memory accounting.
176+
if _, ok := c.mu.cache[user][serial]; !ok {
177+
err := c.mu.acc.Grow(ctx, CertInfoSize)
178+
if err != nil {
179+
log.Warningf(ctx, "no memory available to cache cert expiry: %v", err)
180+
return
181+
}
171182
}
172183

173184
// insert / update the certificate expiration time.

pkg/security/clientcert/cert_expiry_cache_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ func TestAllocationTracking(t *testing.T) {
371371
cache.MaybeUpsert(ctx, "user1", "serial2", 120)
372372
cache.MaybeUpsert(ctx, "user2", "serial3", 65)
373373
clock.Advance(time.Minute)
374+
require.Equal(t, (3*certInfoSize)+(4*gaugeSize), account.Used())
374375
cache.Purge(ctx)
375376
cache.MaybeUpsert(ctx, "user2", "serial4", 75)
376377
require.Equal(t, certInfoSize+(2*gaugeSize), account.Used())
@@ -383,9 +384,22 @@ func TestAllocationTracking(t *testing.T) {
383384
cache.MaybeUpsert(ctx, "user1", "serial2", 120)
384385
cache.MaybeUpsert(ctx, "user2", "serial3", 65)
385386
cache.MaybeUpsert(ctx, "user2", "serial4", 75)
387+
require.Equal(t, (4*certInfoSize)+(4*gaugeSize), account.Used())
386388
cache.Clear()
387389
require.Equal(t, int64(0), account.Used())
388390
})
391+
392+
t.Run("overwriting an existing certificate does not change the reported memory allocation", func(t *testing.T) {
393+
cache := newCache(ctx, clock, stopper)
394+
account := cache.Account()
395+
require.Equal(t, int64(0), account.Used())
396+
cache.MaybeUpsert(ctx, "user1", "serial1", 100)
397+
require.Equal(t, certInfoSize+(2*gaugeSize), account.Used())
398+
cache.MaybeUpsert(ctx, "user1", "serial1", 100)
399+
require.Equal(t, certInfoSize+(2*gaugeSize), account.Used())
400+
cache.MaybeUpsert(ctx, "user1", "serial1", 100)
401+
require.Equal(t, certInfoSize+(2*gaugeSize), account.Used())
402+
})
389403
}
390404

391405
func TestGetExpiration(t *testing.T) {

0 commit comments

Comments
 (0)