diff --git a/pkg/services/signingkeys/signingkeystore/store.go b/pkg/services/signingkeys/signingkeystore/store.go index c664a2e51ce..0f6f23b3d4f 100644 --- a/pkg/services/signingkeys/signingkeystore/store.go +++ b/pkg/services/signingkeys/signingkeystore/store.go @@ -13,6 +13,8 @@ import ( "github.com/go-jose/go-jose/v3" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/localcache" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/signingkeys" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -32,9 +34,13 @@ type SigningStore interface { var _ SigningStore = (*Store)(nil) +const cleanupRateLimitKey = "signingkeys-cleanup" + type Store struct { dbStore db.DB secretsService secrets.Service + log log.Logger + localCache *localcache.CacheService } type SigningKey struct { @@ -50,6 +56,8 @@ func NewSigningKeyStore(dbStore db.DB, secretsService secrets.Service) *Store { return &Store{ dbStore: dbStore, secretsService: secretsService, + log: log.New("signing.key_service"), + localCache: localcache.New(12*time.Hour, 4*time.Hour), } } @@ -130,6 +138,24 @@ func (s *Store) AddPrivateKey(ctx context.Context, return signingkeys.ErrSigningKeyAlreadyExists.Errorf("The specified key already exists: %s", keyID) }) + if _, ok := s.localCache.Get(cleanupRateLimitKey); !ok { + go func() { + defer func() { + if err := recover(); err != nil { + s.log.Error("panic during expired signing key cleanup", "err", err) + } + }() + + ctxGo, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + if err := s.cleanupExpiredKeys(ctxGo); err != nil { + s.log.Error("Failed to cleanup expired signing keys", "err", err) + } + }() + s.localCache.Set(cleanupRateLimitKey, true, 1*time.Hour) + } + return signer, err } @@ -218,3 +244,14 @@ func (s *Store) decodePrivateKey(ctx context.Context, signingKey *SigningKey) (c } return assertedKey, nil } + +// cleanupExpiredKeys removes expired keys from the database that have expired more than 61 days ago +func (s *Store) cleanupExpiredKeys(ctx context.Context) error { + err := s.dbStore.WithTransactionalDbSession(ctx, func(tx *sqlstore.DBSession) error { + _, err := tx.Exec("DELETE FROM signing_key WHERE expires_at IS NOT NULL AND expires_at < ?", + time.Now().UTC().Add(-61*24*time.Hour)) + return err + }) + + return err +} diff --git a/pkg/services/signingkeys/signingkeystore/store_test.go b/pkg/services/signingkeys/signingkeystore/store_test.go index 55fef6e4572..01375fad4c7 100644 --- a/pkg/services/signingkeys/signingkeystore/store_test.go +++ b/pkg/services/signingkeys/signingkeystore/store_test.go @@ -162,6 +162,9 @@ func TestIntegrationAddPrivateKey(t *testing.T) { }, } + _, exists := store.localCache.Get(cleanupRateLimitKey) + require.False(t, exists) + for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := store.AddPrivateKey(ctx, tc.keyID, tc.alg, tc.privateKey, tc.expiresAt, tc.force) @@ -184,6 +187,9 @@ func TestIntegrationAddPrivateKey(t *testing.T) { } }) } + + _, exists = store.localCache.Get(cleanupRateLimitKey) + require.True(t, exists) } func generateRSAKey(t *testing.T) *rsa.PrivateKey {