Clean up old signature hash pools

This commit is contained in:
Patrick Schmidt 2023-09-01 19:57:04 +02:00 committed by Chris Lu
parent cdd817edf9
commit 98dcec0ee2
3 changed files with 55 additions and 20 deletions

View file

@ -33,6 +33,7 @@ type IdentityAccessManagement struct {
isAuthEnabled bool isAuthEnabled bool
domain string domain string
hashes map[string]*sync.Pool hashes map[string]*sync.Pool
hashCounters map[string]*int32
hashMu sync.RWMutex hashMu sync.RWMutex
} }
@ -79,8 +80,9 @@ func (action Action) getPermission() Permission {
func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement { func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
iam := &IdentityAccessManagement{ iam := &IdentityAccessManagement{
domain: option.DomainName, domain: option.DomainName,
hashes: make(map[string]*sync.Pool), hashes: make(map[string]*sync.Pool),
hashCounters: make(map[string]*int32),
} }
if option.Config != "" { if option.Config != "" {
if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil { if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {

View file

@ -32,6 +32,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"unicode/utf8" "unicode/utf8"
@ -463,36 +464,67 @@ func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload s
return identity, s3err.ErrNone return identity, s3err.ErrNone
} }
// getSignature
func (iam *IdentityAccessManagement) getSignature(secretKey string, t time.Time, region string, service string, stringToSign string) string { func (iam *IdentityAccessManagement) getSignature(secretKey string, t time.Time, region string, service string, stringToSign string) string {
pool := iam.getSignatureHashPool(secretKey, t, region, service)
h := pool.Get().(hash.Hash)
defer pool.Put(h)
h.Reset()
h.Write([]byte(stringToSign))
sig := hex.EncodeToString(h.Sum(nil))
return sig
}
func (iam *IdentityAccessManagement) getSignatureHashPool(secretKey string, t time.Time, region string, service string) *sync.Pool {
// Build a caching key for the pool.
date := t.Format(yyyymmdd) date := t.Format(yyyymmdd)
hashID := "AWS4" + secretKey + "/" + date + "/" + region + "/" + service + "/" + "aws4_request" hashID := "AWS4" + secretKey + "/" + date + "/" + region + "/" + service + "/" + "aws4_request"
// Try to find an existing pool and return it.
iam.hashMu.RLock() iam.hashMu.RLock()
pool, ok := iam.hashes[hashID] pool, ok := iam.hashes[hashID]
iam.hashMu.RUnlock() iam.hashMu.RUnlock()
if !ok { if !ok {
iam.hashMu.Lock() iam.hashMu.Lock()
if pool, ok = iam.hashes[hashID]; !ok { defer iam.hashMu.Unlock()
pool = &sync.Pool{ pool, ok = iam.hashes[hashID]
New: func() any {
signingKey := getSigningKey(secretKey, date, region, service)
return hmac.New(sha256.New, signingKey)
},
}
iam.hashes[hashID] = pool
}
iam.hashMu.Unlock()
} }
h := pool.Get().(hash.Hash) if ok {
h.Reset() atomic.StoreInt32(iam.hashCounters[hashID], 1)
h.Write([]byte(stringToSign)) return pool
sig := hex.EncodeToString(h.Sum(nil)) }
pool.Put(h)
return sig // Create a pool that returns HMAC hashers for the requested parameters to avoid expensive re-initializing
// of new instances on every request.
iam.hashes[hashID] = &sync.Pool{
New: func() any {
signingKey := getSigningKey(secretKey, date, region, service)
return hmac.New(sha256.New, signingKey)
},
}
iam.hashCounters[hashID] = new(int32)
// Clean up unused pools automatically after one hour of inactivity
ticker := time.NewTicker(time.Hour)
go func() {
for range ticker.C {
old := atomic.SwapInt32(iam.hashCounters[hashID], 0)
if old == 0 {
break
}
}
ticker.Stop()
iam.hashMu.Lock()
delete(iam.hashes, hashID)
delete(iam.hashCounters, hashID)
iam.hashMu.Unlock()
}()
return iam.hashes[hashID]
} }
func contains(list []string, elem string) bool { func contains(list []string, elem string) bool {

View file

@ -127,7 +127,8 @@ func TestCheckAdminRequestAuthType(t *testing.T) {
func BenchmarkGetSignature(b *testing.B) { func BenchmarkGetSignature(b *testing.B) {
t := time.Now() t := time.Now()
iam := IdentityAccessManagement{ iam := IdentityAccessManagement{
hashes: make(map[string]*sync.Pool), hashes: make(map[string]*sync.Pool),
hashCounters: make(map[string]*int32),
} }
b.ReportAllocs() b.ReportAllocs()