open-vault/builtin/logical/transit/backend.go
divyapola5 2a194a0804
Add missing read unlock calls in transit backend code (#12652)
* Add missing read unlock calls in transit backend code

* Correct formatting in changelog entry
2021-09-28 11:59:30 -05:00

165 lines
3.9 KiB
Go

package transit
import (
"context"
"fmt"
"io"
"strings"
"sync"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/keysutil"
"github.com/hashicorp/vault/sdk/logical"
)
// Minimum cache size for transit backend
const minCacheSize = 10
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
b, err := Backend(ctx, conf)
if err != nil {
return nil, err
}
if err := b.Setup(ctx, conf); err != nil {
return nil, err
}
return b, nil
}
func Backend(ctx context.Context, conf *logical.BackendConfig) (*backend, error) {
var b backend
b.Backend = &framework.Backend{
PathsSpecial: &logical.Paths{
SealWrapStorage: []string{
"archive/",
"policy/",
},
},
Paths: []*framework.Path{
// Rotate/Config needs to come before Keys
// as the handler is greedy
b.pathConfig(),
b.pathRotate(),
b.pathRewrap(),
b.pathKeys(),
b.pathListKeys(),
b.pathExportKeys(),
b.pathEncrypt(),
b.pathDecrypt(),
b.pathDatakey(),
b.pathRandom(),
b.pathHash(),
b.pathHMAC(),
b.pathSign(),
b.pathVerify(),
b.pathBackup(),
b.pathRestore(),
b.pathTrim(),
b.pathCacheConfig(),
},
Secrets: []*framework.Secret{},
Invalidate: b.invalidate,
BackendType: logical.TypeLogical,
}
// determine cacheSize to use. Defaults to 0 which means unlimited
cacheSize := 0
useCache := !conf.System.CachingDisabled()
if useCache {
var err error
cacheSize, err = GetCacheSizeFromStorage(ctx, conf.StorageView)
if err != nil {
return nil, fmt.Errorf("Error retrieving cache size from storage: %w", err)
}
if cacheSize != 0 && cacheSize < minCacheSize {
b.Logger().Warn("size %d is less than minimum %d. Cache size is set to %d", cacheSize, minCacheSize, minCacheSize)
cacheSize = minCacheSize
}
}
var err error
b.lm, err = keysutil.NewLockManager(useCache, cacheSize)
if err != nil {
return nil, err
}
return &b, nil
}
type backend struct {
*framework.Backend
lm *keysutil.LockManager
// Lock to make changes to any of the backend's cache configuration.
configMutex sync.RWMutex
cacheSizeChanged bool
}
func GetCacheSizeFromStorage(ctx context.Context, s logical.Storage) (int, error) {
size := 0
entry, err := s.Get(ctx, "config/cache")
if err != nil {
return 0, err
}
if entry != nil {
var storedCache configCache
if err := entry.DecodeJSON(&storedCache); err != nil {
return 0, err
}
size = storedCache.Size
}
return size, nil
}
// Update cache size and get policy
func (b *backend) GetPolicy(ctx context.Context, polReq keysutil.PolicyRequest, rand io.Reader) (retP *keysutil.Policy, retUpserted bool, retErr error) {
// Acquire read lock to read cacheSizeChanged
b.configMutex.RLock()
if b.lm.GetUseCache() && b.cacheSizeChanged {
var err error
currentCacheSize := b.lm.GetCacheSize()
storedCacheSize, err := GetCacheSizeFromStorage(ctx, polReq.Storage)
if err != nil {
b.configMutex.RUnlock()
return nil, false, err
}
if currentCacheSize != storedCacheSize {
err = b.lm.InitCache(storedCacheSize)
if err != nil {
b.configMutex.RUnlock()
return nil, false, err
}
}
// Release the read lock and acquire the write lock
b.configMutex.RUnlock()
b.configMutex.Lock()
defer b.configMutex.Unlock()
b.cacheSizeChanged = false
} else {
b.configMutex.RUnlock()
}
p, _, err := b.lm.GetPolicy(ctx, polReq, rand)
if err != nil {
return p, false, err
}
return p, true, nil
}
func (b *backend) invalidate(ctx context.Context, key string) {
if b.Logger().IsDebug() {
b.Logger().Debug("invalidating key", "key", key)
}
switch {
case strings.HasPrefix(key, "policy/"):
name := strings.TrimPrefix(key, "policy/")
b.lm.InvalidatePolicy(name)
case strings.HasPrefix(key, "cache-config/"):
// Acquire the lock to set the flag to indicate that cache size needs to be refreshed from storage
b.configMutex.Lock()
defer b.configMutex.Unlock()
b.cacheSizeChanged = true
}
}