WIP: Add private Secret Manager Plugins support to plugin platform (#49544)
* Add protobuf config and generated code, and client wrapper * wire up loading of secretsmanager plugin, using renderer plugin as a model * update kvstore provider to check if we should use the grpc plugin. return false always in OSS * add OSS remote plugin check * refactor wire gen file * log which secrets manager is being used * Fix argument types for remote checker * Turns out if err != nil, then the result is always nil. Return empty values if there is an error. * remove duplicate import * Update pkg/services/secrets/kvstore/kvstore.go Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * Update pkg/services/secrets/kvstore/kvstore.go Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * refactor RemotePluginCheck interface to just return the Plugin client directly * rename struct to something less silly * Update pkg/plugins/backendplugin/secretsmanagerplugin/secretsmanager.proto Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Marcus Efraimsson
Will Browne
parent
ef401f5d62
commit
f376c33903
@@ -13,11 +13,26 @@ const (
|
||||
AllOrganizations = -1
|
||||
)
|
||||
|
||||
func ProvideService(sqlStore sqlstore.Store, secretsService secrets.Service) SecretsKVStore {
|
||||
func ProvideService(sqlStore sqlstore.Store, secretsService secrets.Service, remoteCheck UseRemoteSecretsPluginCheck) SecretsKVStore {
|
||||
logger := log.New("secrets.kvstore")
|
||||
if remoteCheck.ShouldUseRemoteSecretsPlugin() {
|
||||
logger.Debug("secrets kvstore is using a remote plugin for secrets management")
|
||||
secretsPlugin, err := remoteCheck.GetPlugin()
|
||||
if err != nil {
|
||||
logger.Error("plugin client was nil, falling back to SQL implementation")
|
||||
} else {
|
||||
return &secretsKVStorePlugin{
|
||||
secretsPlugin: secretsPlugin,
|
||||
secretsService: secretsService,
|
||||
log: logger,
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Debug("secrets kvstore is using the default (SQL) implementation for secrets management")
|
||||
return &secretsKVStoreSQL{
|
||||
sqlStore: sqlStore,
|
||||
secretsService: secretsService,
|
||||
log: log.New("secrets.kvstore"),
|
||||
log: logger,
|
||||
decryptionCache: decryptionCache{
|
||||
cache: make(map[int64]cachedDecrypted),
|
||||
},
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package kvstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
smp "github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
)
|
||||
|
||||
// secretsKVStorePlugin provides a key/value store backed by the Grafana plugin gRPC interface
|
||||
type secretsKVStorePlugin struct {
|
||||
log log.Logger
|
||||
secretsPlugin smp.SecretsManagerPlugin
|
||||
secretsService secrets.Service
|
||||
}
|
||||
|
||||
// Get an item from the store
|
||||
func (kv *secretsKVStorePlugin) Get(ctx context.Context, orgId int64, namespace string, typ string) (string, bool, error) {
|
||||
req := &smp.SecretsGetRequest{
|
||||
KeyDescriptor: &smp.Key{
|
||||
OrgId: orgId,
|
||||
Namespace: namespace,
|
||||
Type: typ,
|
||||
},
|
||||
}
|
||||
res, err := kv.secretsPlugin.Get(ctx, req)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
} else if res.Error != "" {
|
||||
err = fmt.Errorf(res.Error)
|
||||
}
|
||||
|
||||
return res.DecryptedValue, res.Exists, err
|
||||
}
|
||||
|
||||
// Set an item in the store
|
||||
func (kv *secretsKVStorePlugin) Set(ctx context.Context, orgId int64, namespace string, typ string, value string) error {
|
||||
req := &smp.SecretsSetRequest{
|
||||
KeyDescriptor: &smp.Key{
|
||||
OrgId: orgId,
|
||||
Namespace: namespace,
|
||||
Type: typ,
|
||||
},
|
||||
Value: value,
|
||||
}
|
||||
|
||||
res, err := kv.secretsPlugin.Set(ctx, req)
|
||||
if err == nil && res.Error != "" {
|
||||
err = fmt.Errorf(res.Error)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Del deletes an item from the store.
|
||||
func (kv *secretsKVStorePlugin) Del(ctx context.Context, orgId int64, namespace string, typ string) error {
|
||||
req := &smp.SecretsDelRequest{
|
||||
KeyDescriptor: &smp.Key{
|
||||
OrgId: orgId,
|
||||
Namespace: namespace,
|
||||
Type: typ,
|
||||
},
|
||||
}
|
||||
|
||||
res, err := kv.secretsPlugin.Del(ctx, req)
|
||||
if err == nil && res.Error != "" {
|
||||
err = fmt.Errorf(res.Error)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Keys get all keys for a given namespace. To query for all
|
||||
// organizations the constant 'kvstore.AllOrganizations' can be passed as orgId.
|
||||
func (kv *secretsKVStorePlugin) Keys(ctx context.Context, orgId int64, namespace string, typ string) ([]Key, error) {
|
||||
req := &smp.SecretsKeysRequest{
|
||||
KeyDescriptor: &smp.Key{
|
||||
OrgId: orgId,
|
||||
Namespace: namespace,
|
||||
Type: typ,
|
||||
},
|
||||
AllOrganizations: orgId == AllOrganizations,
|
||||
}
|
||||
|
||||
res, err := kv.secretsPlugin.Keys(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if res.Error != "" {
|
||||
err = fmt.Errorf(res.Error)
|
||||
}
|
||||
|
||||
return parseKeys(res.Keys), err
|
||||
}
|
||||
|
||||
// Rename an item in the store
|
||||
func (kv *secretsKVStorePlugin) Rename(ctx context.Context, orgId int64, namespace string, typ string, newNamespace string) error {
|
||||
req := &smp.SecretsRenameRequest{
|
||||
KeyDescriptor: &smp.Key{
|
||||
OrgId: orgId,
|
||||
Namespace: namespace,
|
||||
Type: typ,
|
||||
},
|
||||
NewNamespace: newNamespace,
|
||||
}
|
||||
|
||||
res, err := kv.secretsPlugin.Rename(ctx, req)
|
||||
if err == nil && res.Error != "" {
|
||||
err = fmt.Errorf(res.Error)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func parseKeys(keys []*smp.Key) []Key {
|
||||
var newKeys []Key
|
||||
|
||||
for _, k := range keys {
|
||||
newKey := Key{OrgId: k.OrgId, Namespace: k.Namespace, Type: k.Type}
|
||||
newKeys = append(newKeys, newKey)
|
||||
}
|
||||
|
||||
return newKeys
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package kvstore
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
)
|
||||
|
||||
type UseRemoteSecretsPluginCheck interface {
|
||||
ShouldUseRemoteSecretsPlugin() bool
|
||||
GetPlugin() (secretsmanagerplugin.SecretsManagerPlugin, error)
|
||||
}
|
||||
|
||||
type OSSRemoteSecretsPluginCheck struct {
|
||||
UseRemoteSecretsPluginCheck
|
||||
}
|
||||
|
||||
func ProvideRemotePluginCheck() *OSSRemoteSecretsPluginCheck {
|
||||
return &OSSRemoteSecretsPluginCheck{}
|
||||
}
|
||||
|
||||
func (c *OSSRemoteSecretsPluginCheck) ShouldUseRemoteSecretsPlugin() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *OSSRemoteSecretsPluginCheck) GetPlugin() (secretsmanagerplugin.SecretsManagerPlugin, error) {
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user