mirror of
https://github.com/grafana/grafana.git
synced 2024-11-21 16:38:03 -06:00
Chore: Add encryption codec to the remote cache (#59871)
* add encryption codec to the remote cache * change config files too * fix test constructor * pass codec into the test cache
This commit is contained in:
parent
2ff6ae65df
commit
f1fb202284
@ -165,6 +165,9 @@ connstr =
|
||||
# prefix prepended to all the keys in the remote cache
|
||||
prefix =
|
||||
|
||||
# This enables encryption of values stored in the remote cache
|
||||
encryption =
|
||||
|
||||
#################################### Data proxy ###########################
|
||||
[dataproxy]
|
||||
|
||||
|
@ -172,6 +172,9 @@
|
||||
# prefix prepended to all the keys in the remote cache
|
||||
; prefix =
|
||||
|
||||
# This enables encryption of values stored in the remote cache
|
||||
;encryption =
|
||||
|
||||
#################################### Data proxy ###########################
|
||||
[dataproxy]
|
||||
|
||||
|
@ -14,12 +14,14 @@ const databaseCacheType = "database"
|
||||
|
||||
type databaseCache struct {
|
||||
SQLStore db.DB
|
||||
codec codec
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func newDatabaseCache(sqlstore db.DB) *databaseCache {
|
||||
func newDatabaseCache(sqlstore db.DB, codec codec) *databaseCache {
|
||||
dc := &databaseCache{
|
||||
SQLStore: sqlstore,
|
||||
codec: codec,
|
||||
log: log.New("remotecache.database"),
|
||||
}
|
||||
|
||||
@ -78,7 +80,7 @@ func (dc *databaseCache) Get(ctx context.Context, key string) (interface{}, erro
|
||||
}
|
||||
}
|
||||
|
||||
if err = decodeGob(cacheHit.Data, item); err != nil {
|
||||
if err = dc.codec.Decode(ctx, cacheHit.Data, item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -90,7 +92,7 @@ func (dc *databaseCache) Get(ctx context.Context, key string) (interface{}, erro
|
||||
|
||||
func (dc *databaseCache) Set(ctx context.Context, key string, value interface{}, expire time.Duration) error {
|
||||
item := &cachedItem{Val: value}
|
||||
data, err := encodeGob(item)
|
||||
data, err := dc.codec.Encode(ctx, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ func TestDatabaseStorageGarbageCollection(t *testing.T) {
|
||||
|
||||
db := &databaseCache{
|
||||
SQLStore: sqlstore,
|
||||
codec: &gobCodec{},
|
||||
log: log.New("remotecache.database"),
|
||||
}
|
||||
|
||||
@ -64,6 +65,7 @@ func TestSecondSet(t *testing.T) {
|
||||
|
||||
db := &databaseCache{
|
||||
SQLStore: sqlstore,
|
||||
codec: &gobCodec{},
|
||||
log: log.New("remotecache.database"),
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,14 @@ import (
|
||||
const memcachedCacheType = "memcached"
|
||||
|
||||
type memcachedStorage struct {
|
||||
c *memcache.Client
|
||||
c *memcache.Client
|
||||
codec codec
|
||||
}
|
||||
|
||||
func newMemcachedStorage(opts *setting.RemoteCacheOptions) *memcachedStorage {
|
||||
func newMemcachedStorage(opts *setting.RemoteCacheOptions, codec codec) *memcachedStorage {
|
||||
return &memcachedStorage{
|
||||
c: memcache.New(opts.ConnStr),
|
||||
c: memcache.New(opts.ConnStr),
|
||||
codec: codec,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +33,7 @@ func newItem(sid string, data []byte, expire int32) *memcache.Item {
|
||||
// Set sets value to given key in the cache.
|
||||
func (s *memcachedStorage) Set(ctx context.Context, key string, val interface{}, expires time.Duration) error {
|
||||
item := &cachedItem{Val: val}
|
||||
bytes, err := encodeGob(item)
|
||||
bytes, err := s.codec.Encode(ctx, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -58,7 +60,7 @@ func (s *memcachedStorage) Get(ctx context.Context, key string) (interface{}, er
|
||||
|
||||
item := &cachedItem{}
|
||||
|
||||
err = decodeGob(memcachedItem.Value, item)
|
||||
err = s.codec.Decode(ctx, memcachedItem.Value, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ import (
|
||||
const redisCacheType = "redis"
|
||||
|
||||
type redisStorage struct {
|
||||
c *redis.Client
|
||||
c *redis.Client
|
||||
codec codec
|
||||
}
|
||||
|
||||
// parseRedisConnStr parses k=v pairs in csv and builds a redis Options object
|
||||
@ -76,18 +77,18 @@ func parseRedisConnStr(connStr string) (*redis.Options, error) {
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func newRedisStorage(opts *setting.RemoteCacheOptions) (*redisStorage, error) {
|
||||
func newRedisStorage(opts *setting.RemoteCacheOptions, codec codec) (*redisStorage, error) {
|
||||
opt, err := parseRedisConnStr(opts.ConnStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &redisStorage{c: redis.NewClient(opt)}, nil
|
||||
return &redisStorage{c: redis.NewClient(opt), codec: codec}, nil
|
||||
}
|
||||
|
||||
// Set sets value to given key in session.
|
||||
func (s *redisStorage) Set(ctx context.Context, key string, val interface{}, expires time.Duration) error {
|
||||
item := &cachedItem{Val: val}
|
||||
value, err := encodeGob(item)
|
||||
value, err := s.codec.Encode(ctx, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -100,7 +101,7 @@ func (s *redisStorage) Get(ctx context.Context, key string) (interface{}, error)
|
||||
v := s.c.Get(ctx, key)
|
||||
|
||||
item := &cachedItem{}
|
||||
err := decodeGob([]byte(v.Val()), item)
|
||||
err := s.codec.Decode(ctx, []byte(v.Val()), item)
|
||||
|
||||
if err == nil {
|
||||
return item.Val, nil
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
glog "github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -29,8 +30,14 @@ const (
|
||||
ServiceName = "RemoteCache"
|
||||
)
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, sqlStore db.DB) (*RemoteCache, error) {
|
||||
client, err := createClient(cfg.RemoteCacheOptions, sqlStore)
|
||||
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, secretsService secrets.Service) (*RemoteCache, error) {
|
||||
var codec codec
|
||||
if cfg.RemoteCacheOptions.Encryption {
|
||||
codec = &encryptionCodec{secretsService}
|
||||
} else {
|
||||
codec = &gobCodec{}
|
||||
}
|
||||
client, err := createClient(cfg.RemoteCacheOptions, sqlStore, codec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -97,14 +104,14 @@ func (ds *RemoteCache) Run(ctx context.Context) error {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
func createClient(opts *setting.RemoteCacheOptions, sqlstore db.DB) (cache CacheStorage, err error) {
|
||||
func createClient(opts *setting.RemoteCacheOptions, sqlstore db.DB, codec codec) (cache CacheStorage, err error) {
|
||||
switch opts.Name {
|
||||
case redisCacheType:
|
||||
cache, err = newRedisStorage(opts)
|
||||
cache, err = newRedisStorage(opts, codec)
|
||||
case memcachedCacheType:
|
||||
cache = newMemcachedStorage(opts)
|
||||
cache = newMemcachedStorage(opts, codec)
|
||||
case databaseCacheType:
|
||||
cache = newDatabaseCache(sqlstore)
|
||||
cache = newDatabaseCache(sqlstore, codec)
|
||||
default:
|
||||
return nil, ErrInvalidCacheType
|
||||
}
|
||||
@ -131,17 +138,46 @@ type cachedItem struct {
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
func encodeGob(item *cachedItem) ([]byte, error) {
|
||||
type codec interface {
|
||||
Encode(context.Context, *cachedItem) ([]byte, error)
|
||||
Decode(context.Context, []byte, *cachedItem) error
|
||||
}
|
||||
|
||||
type gobCodec struct{}
|
||||
|
||||
func (c *gobCodec) Encode(_ context.Context, item *cachedItem) ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := gob.NewEncoder(buf).Encode(item)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func decodeGob(data []byte, out *cachedItem) error {
|
||||
func (c *gobCodec) Decode(_ context.Context, data []byte, out *cachedItem) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
return gob.NewDecoder(buf).Decode(&out)
|
||||
}
|
||||
|
||||
type encryptionCodec struct {
|
||||
secretsService secrets.Service
|
||||
}
|
||||
|
||||
func (c *encryptionCodec) Encode(ctx context.Context, item *cachedItem) ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := gob.NewEncoder(buf).Encode(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.secretsService.Encrypt(ctx, buf.Bytes(), secrets.WithoutScope())
|
||||
}
|
||||
|
||||
func (c *encryptionCodec) Decode(ctx context.Context, data []byte, out *cachedItem) error {
|
||||
decrypted, err := c.secretsService.Decrypt(ctx, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer(decrypted)
|
||||
return gob.NewDecoder(buf).Decode(&out)
|
||||
}
|
||||
|
||||
type prefixCacheStorage struct {
|
||||
cache CacheStorage
|
||||
prefix string
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ func createTestClient(t *testing.T, opts *setting.RemoteCacheOptions, sqlstore d
|
||||
cfg := &setting.Cfg{
|
||||
RemoteCacheOptions: opts,
|
||||
}
|
||||
dc, err := ProvideService(cfg, sqlstore)
|
||||
dc, err := ProvideService(cfg, sqlstore, fakes.NewFakeSecretsService())
|
||||
require.Nil(t, err, "Failed to init client for test")
|
||||
|
||||
return dc
|
||||
@ -46,7 +47,7 @@ func TestCachedBasedOnConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidCacheTypeReturnsError(t *testing.T) {
|
||||
_, err := createClient(&setting.RemoteCacheOptions{Name: "invalid"}, nil)
|
||||
_, err := createClient(&setting.RemoteCacheOptions{Name: "invalid"}, nil, &gobCodec{})
|
||||
assert.Equal(t, err, ErrInvalidCacheType)
|
||||
}
|
||||
|
||||
@ -95,6 +96,7 @@ func TestCachePrefix(t *testing.T) {
|
||||
cache := &databaseCache{
|
||||
SQLStore: db,
|
||||
log: log.New("remotecache.database"),
|
||||
codec: &gobCodec{},
|
||||
}
|
||||
prefixCache := &prefixCacheStorage{cache: cache, prefix: "test/"}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -22,7 +23,7 @@ func NewFakeStore(t *testing.T) *RemoteCache {
|
||||
|
||||
dc, err := ProvideService(&setting.Cfg{
|
||||
RemoteCacheOptions: opts,
|
||||
}, sqlStore)
|
||||
}, sqlStore, fakes.NewFakeSecretsService())
|
||||
require.NoError(t, err, "Failed to init remote cache for test")
|
||||
|
||||
return dc
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -79,7 +80,7 @@ func getContextHandler(t *testing.T) *ContextHandler {
|
||||
cfg.AuthProxyHeaderName = "X-Killa"
|
||||
cfg.AuthProxyEnabled = true
|
||||
cfg.AuthProxyHeaderProperty = "username"
|
||||
remoteCacheSvc, err := remotecache.ProvideService(cfg, sqlStore)
|
||||
remoteCacheSvc, err := remotecache.ProvideService(cfg, sqlStore, fakes.NewFakeSecretsService())
|
||||
require.NoError(t, err)
|
||||
userAuthTokenSvc := authtest.NewFakeUserAuthTokenService()
|
||||
renderSvc := &fakeRenderService{}
|
||||
|
@ -1121,11 +1121,13 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
|
||||
dbName := valueAsString(cacheServer, "type", "database")
|
||||
connStr := valueAsString(cacheServer, "connstr", "")
|
||||
prefix := valueAsString(cacheServer, "prefix", "")
|
||||
encryption := cacheServer.Key("encryption").MustBool(false)
|
||||
|
||||
cfg.RemoteCacheOptions = &RemoteCacheOptions{
|
||||
Name: dbName,
|
||||
ConnStr: connStr,
|
||||
Prefix: prefix,
|
||||
Name: dbName,
|
||||
ConnStr: connStr,
|
||||
Prefix: prefix,
|
||||
Encryption: encryption,
|
||||
}
|
||||
|
||||
geomapSection := iniFile.Section("geomap")
|
||||
@ -1159,9 +1161,10 @@ func valueAsString(section *ini.Section, keyName string, defaultValue string) st
|
||||
}
|
||||
|
||||
type RemoteCacheOptions struct {
|
||||
Name string
|
||||
ConnStr string
|
||||
Prefix string
|
||||
Name string
|
||||
ConnStr string
|
||||
Prefix string
|
||||
Encryption bool
|
||||
}
|
||||
|
||||
func (cfg *Cfg) readLDAPConfig() {
|
||||
|
Loading…
Reference in New Issue
Block a user