Secrets: Provide grpc and local inline secure value client (#109179)
* TestUtils: Add helper to create certs * InlineSecureValue: Move to its own package rather than service * Settings: Rename decrypt server configs to grpc prefixed as it is more generic * InlineSecureValue: Add gRPC client * InlineSecureValue: Add service provider grpc/local depending on setting.Cfg * Wire: Add ProvideInlineSecureValueService to basic wireset * InlineSecureValue: Re-export in root secret pkg and generate mocks for interface * Settings: Unify decrypt+inline setting into generic grpc client
This commit is contained in:
@@ -76,8 +76,8 @@ func (s *secretsService) Encrypt(ctx context.Context, namespace, name string, da
|
||||
}
|
||||
|
||||
decrypters := []string{svcName}
|
||||
if s.cfg.SecretsManagement.DecryptGrafanaServiceName != "" {
|
||||
decrypters = append(decrypters, s.cfg.SecretsManagement.DecryptGrafanaServiceName)
|
||||
if s.cfg.SecretsManagement.GrpcGrafanaServiceName != "" {
|
||||
decrypters = append(decrypters, s.cfg.SecretsManagement.GrpcGrafanaServiceName)
|
||||
}
|
||||
|
||||
// Create the secret directly as unstructured
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
@@ -13,24 +12,15 @@ import (
|
||||
)
|
||||
|
||||
func ProvideDecryptService(cfg *setting.Cfg, tracer trace.Tracer, decryptStorage contracts.DecryptStorage) (contracts.DecryptService, error) {
|
||||
switch cfg.SecretsManagement.DecryptServerType {
|
||||
case "grpc":
|
||||
if cfg.SecretsManagement.GrpcClientEnable {
|
||||
grpcClientConfig := grpcutils.ReadGrpcClientConfig(cfg)
|
||||
|
||||
if cfg.SecretsManagement.DecryptServerAddress == "" {
|
||||
return nil, fmt.Errorf("decrypt_server_address is required when decrypt_server_type is grpc")
|
||||
if cfg.SecretsManagement.GrpcServerAddress == "" {
|
||||
return nil, fmt.Errorf("grpc_server_address is required when grpc client is enabled")
|
||||
}
|
||||
|
||||
if grpcClientConfig.Token == "" || grpcClientConfig.TokenExchangeURL == "" {
|
||||
return nil, fmt.Errorf("grpc_client_authentication.token and grpc_client_authentication.token_exchange_url are required when secrets_manager.decrypt_server_type is grpc")
|
||||
}
|
||||
|
||||
nsInfo, err := claims.ParseNamespace(grpcClientConfig.TokenNamespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse token namespace %v: %w", grpcClientConfig.TokenNamespace, err)
|
||||
}
|
||||
if nsInfo.OrgID < 1 {
|
||||
return nil, fmt.Errorf("invalid token namepsace %v", grpcClientConfig.TokenNamespace)
|
||||
return nil, fmt.Errorf("grpc_client_authentication.token and grpc_client_authentication.token_exchange_url are required when grpc client is enabled")
|
||||
}
|
||||
|
||||
tokenExchangeClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
|
||||
@@ -43,22 +33,19 @@ func ProvideDecryptService(cfg *setting.Cfg, tracer trace.Tracer, decryptStorage
|
||||
|
||||
tlsConfig := readTLSFromConfig(cfg)
|
||||
|
||||
client, err := NewGRPCDecryptClientWithTLS(tokenExchangeClient, tracer, cfg.SecretsManagement.DecryptServerAddress, tlsConfig)
|
||||
client, err := NewGRPCDecryptClientWithTLS(tokenExchangeClient, tracer, cfg.SecretsManagement.GrpcServerAddress, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create grpc decrypt client: %w", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
|
||||
case "local", "":
|
||||
return NewLocalDecryptClient(decryptStorage)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported storage type: %s", cfg.SecretsManagement.DecryptServerType)
|
||||
return NewLocalDecryptClient(decryptStorage)
|
||||
}
|
||||
|
||||
func readTLSFromConfig(cfg *setting.Cfg) TLSConfig {
|
||||
if !cfg.SecretsManagement.DecryptServerUseTLS {
|
||||
if !cfg.SecretsManagement.GrpcServerUseTLS {
|
||||
return TLSConfig{
|
||||
UseTLS: false,
|
||||
InsecureSkipVerify: true,
|
||||
@@ -72,7 +59,7 @@ func readTLSFromConfig(cfg *setting.Cfg) TLSConfig {
|
||||
CertFile: apiServer.Key("proxy_client_cert_file").MustString(""),
|
||||
KeyFile: apiServer.Key("proxy_client_key_file").MustString(""),
|
||||
CAFile: apiServer.Key("apiservice_ca_bundle_file").MustString(""),
|
||||
ServerName: cfg.SecretsManagement.DecryptServerTLSServerName,
|
||||
InsecureSkipVerify: cfg.SecretsManagement.DecryptServerTLSSkipVerify,
|
||||
ServerName: cfg.SecretsManagement.GrpcServerTLSServerName,
|
||||
InsecureSkipVerify: cfg.SecretsManagement.GrpcServerTLSSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package decrypt
|
||||
package decrypt_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
decryptv1beta1 "github.com/grafana/grafana/apps/secret/decrypt/v1beta1"
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/madflojo/testcerts"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
@@ -21,6 +20,8 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/decrypt"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
"github.com/grafana/grafana/pkg/services/authn/clients"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -43,9 +44,8 @@ func TestDecryptService(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "local"
|
||||
|
||||
decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
decryptService, err := decrypt.ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1"})
|
||||
@@ -72,9 +72,8 @@ func TestDecryptService(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "local"
|
||||
|
||||
decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
decryptService, err := decrypt.ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1", "secure-value-2"})
|
||||
@@ -100,9 +99,9 @@ func TestDecryptService(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "local"
|
||||
cfg.SecretsManagement.GrpcClientEnable = false
|
||||
|
||||
decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
decryptService, err := decrypt.ProvideDecryptService(cfg, tracer, mockStorage)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1", "secure-value-2"})
|
||||
@@ -111,39 +110,27 @@ func TestDecryptService(t *testing.T) {
|
||||
require.EqualValues(t, decryptedValuesResp, resp)
|
||||
})
|
||||
|
||||
t.Run("when storage type is unsupported, it returns an error", func(t *testing.T) {
|
||||
t.Run("when grpc server is enabled but token exchange config is missing, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "unsupported"
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
cfg.SecretsManagement.GrpcServerAddress = "127.0.0.1:10000"
|
||||
|
||||
decryptService, err := ProvideDecryptService(cfg, tracer, nil)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "unsupported storage type")
|
||||
require.Nil(t, decryptService)
|
||||
})
|
||||
|
||||
t.Run("when storage type is grpc but token exchange config is missing, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "grpc"
|
||||
cfg.SecretsManagement.DecryptServerAddress = "127.0.0.1:10000"
|
||||
|
||||
_, err := ProvideDecryptService(cfg, tracer, nil)
|
||||
_, err := decrypt.ProvideDecryptService(cfg, tracer, nil)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "grpc_client_authentication.token and grpc_client_authentication.token_exchange_url are required")
|
||||
})
|
||||
|
||||
t.Run("when storage type is grpc but storage address is missing, it returns an error", func(t *testing.T) {
|
||||
t.Run("when grpc server is enabled but storage address is missing, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "grpc"
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
|
||||
_, err := ProvideDecryptService(cfg, tracer, nil)
|
||||
_, err := decrypt.ProvideDecryptService(cfg, tracer, nil)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "decrypt_server_address is required")
|
||||
require.Contains(t, err.Error(), "grpc_server_address is required")
|
||||
})
|
||||
|
||||
t.Run("happy path with grpc+tls server with fake toke exchanger and server", func(t *testing.T) {
|
||||
@@ -165,9 +152,9 @@ func TestDecryptService(t *testing.T) {
|
||||
listener, err := nettest.NewLocalListener("tcp")
|
||||
require.NoError(t, err)
|
||||
|
||||
certPaths := createX509TestDir(t)
|
||||
certPaths := testutils.CreateX509TestDir(t)
|
||||
|
||||
serverCert, err := tls.LoadX509KeyPair(certPaths.serverCert, certPaths.serverKey)
|
||||
serverCert, err := tls.LoadX509KeyPair(certPaths.ServerCert, certPaths.ServerKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
@@ -204,12 +191,12 @@ func TestDecryptService(t *testing.T) {
|
||||
namespace := "stacks-1234"
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.DecryptServerType = "grpc"
|
||||
cfg.SecretsManagement.DecryptServerAddress = listener.Addr().String()
|
||||
cfg.SecretsManagement.DecryptGrafanaServiceName = grafanaSvcIdentity
|
||||
cfg.SecretsManagement.DecryptServerUseTLS = true
|
||||
cfg.SecretsManagement.DecryptServerTLSServerName = "localhost"
|
||||
cfg.SecretsManagement.DecryptServerTLSSkipVerify = false
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
cfg.SecretsManagement.GrpcServerAddress = listener.Addr().String()
|
||||
cfg.SecretsManagement.GrpcGrafanaServiceName = grafanaSvcIdentity
|
||||
cfg.SecretsManagement.GrpcServerUseTLS = true
|
||||
cfg.SecretsManagement.GrpcServerTLSServerName = "localhost"
|
||||
cfg.SecretsManagement.GrpcServerTLSSkipVerify = false
|
||||
|
||||
grpcClientAuth := cfg.Raw.Section("grpc_client_authentication")
|
||||
_, err = grpcClientAuth.NewKey("token", "test-token")
|
||||
@@ -220,15 +207,15 @@ func TestDecryptService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
apiServer := cfg.Raw.Section("grafana-apiserver")
|
||||
_, err = apiServer.NewKey("proxy_client_cert_file", certPaths.clientCert)
|
||||
_, err = apiServer.NewKey("proxy_client_cert_file", certPaths.ClientCert)
|
||||
require.NoError(t, err)
|
||||
_, err = apiServer.NewKey("proxy_client_key_file", certPaths.clientKey)
|
||||
_, err = apiServer.NewKey("proxy_client_key_file", certPaths.ClientKey)
|
||||
require.NoError(t, err)
|
||||
_, err = apiServer.NewKey("apiservice_ca_bundle_file", certPaths.ca)
|
||||
_, err = apiServer.NewKey("apiservice_ca_bundle_file", certPaths.CA)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and test decryption, using the mock grpc server as we dont test the business logic here
|
||||
decryptService, err := ProvideDecryptService(cfg, tracer, nil)
|
||||
decryptService, err := decrypt.ProvideDecryptService(cfg, tracer, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, decryptService)
|
||||
|
||||
@@ -271,40 +258,3 @@ func (m *mockDecryptServer) DecryptSecureValues(ctx context.Context, req *decryp
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*decryptv1beta1.SecureValueDecryptResponseCollection), args.Error(1)
|
||||
}
|
||||
|
||||
type certPaths struct {
|
||||
clientCert string
|
||||
clientKey string
|
||||
serverCert string
|
||||
serverKey string
|
||||
ca string
|
||||
}
|
||||
|
||||
func createX509TestDir(t *testing.T) certPaths {
|
||||
t.Helper()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
ca := testcerts.NewCA()
|
||||
caCertFile, _, err := ca.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
serverKp, err := ca.NewKeyPair("localhost")
|
||||
require.NoError(t, err)
|
||||
|
||||
serverCertFile, serverKeyFile, err := serverKp.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
clientKp, err := ca.NewKeyPair()
|
||||
require.NoError(t, err)
|
||||
clientCertFile, clientKeyFile, err := clientKp.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
return certPaths{
|
||||
clientCert: clientCertFile.Name(),
|
||||
clientKey: clientKeyFile.Name(),
|
||||
serverCert: serverCertFile.Name(),
|
||||
serverKey: serverKeyFile.Name(),
|
||||
ca: caCertFile.Name(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package secret
|
||||
|
||||
import "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
|
||||
// InlineSecureValueSupport allows resources to manage secrets inline
|
||||
//
|
||||
//go:generate mockery --name InlineSecureValueSupport --structname MockInlineSecureValueSupport --inpackage --filename inline_mock.go --with-expecter
|
||||
type InlineSecureValueSupport = contracts.InlineSecureValueSupport
|
||||
@@ -0,0 +1,200 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
inlinev1beta1 "github.com/grafana/grafana/apps/secret/inline/v1beta1"
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
)
|
||||
|
||||
type GRPCInlineClient struct {
|
||||
conn *grpc.ClientConn
|
||||
tracer trace.Tracer
|
||||
tokenExchanger authnlib.TokenExchanger
|
||||
}
|
||||
|
||||
var _ contracts.InlineSecureValueSupport = &GRPCInlineClient{}
|
||||
|
||||
type TLSConfig struct {
|
||||
UseTLS bool
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CAFile string
|
||||
ServerName string
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
func NewGRPCInlineClient(tokenExchanger authnlib.TokenExchanger, tracer trace.Tracer, address string, tlsConfig TLSConfig) (*GRPCInlineClient, error) {
|
||||
var opts []grpc.DialOption
|
||||
if tlsConfig.UseTLS {
|
||||
creds, err := createTLSCredentials(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to setup TLS: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(address, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to grpc server at %s: %w", address, err)
|
||||
}
|
||||
|
||||
return &GRPCInlineClient{
|
||||
conn: conn,
|
||||
tracer: tracer,
|
||||
tokenExchanger: tokenExchanger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createTLSCredentials(config TLSConfig) (credentials.TransportCredentials, error) {
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
if config.CAFile != "" {
|
||||
caCert, err := os.ReadFile(config.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CA: %w", err)
|
||||
}
|
||||
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("failed to append CA")
|
||||
}
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
if config.CertFile != "" && config.KeyFile != "" {
|
||||
cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load client certificate: %w", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
if config.ServerName != "" {
|
||||
tlsConfig.ServerName = config.ServerName
|
||||
}
|
||||
|
||||
if config.InsecureSkipVerify {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
return credentials.NewTLS(tlsConfig), nil
|
||||
}
|
||||
|
||||
// Close will close the underlying gRPC connection. After it is closed, the client cannot be used anymore.
|
||||
func (g *GRPCInlineClient) Close() error {
|
||||
if g.conn != nil {
|
||||
return g.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GRPCInlineClient) CanReference(ctx context.Context, owner v0alpha1.ObjectReference, names ...string) error {
|
||||
client, err := g.getClient(owner.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &inlinev1beta1.CanReferenceRequest{
|
||||
Owner: &inlinev1beta1.ObjectReference{
|
||||
ApiGroup: owner.APIGroup,
|
||||
ApiVersion: owner.APIVersion,
|
||||
Kind: owner.Kind,
|
||||
Namespace: owner.Namespace,
|
||||
Name: owner.Name,
|
||||
},
|
||||
Names: names,
|
||||
}
|
||||
|
||||
_, err = client.CanReference(ctx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GRPCInlineClient) CreateInline(ctx context.Context, owner v0alpha1.ObjectReference, value v0alpha1.RawSecureValue) (string, error) {
|
||||
client, err := g.getClient(owner.Namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if value.IsZero() {
|
||||
return "", fmt.Errorf("empty value provided for CreateInline")
|
||||
}
|
||||
|
||||
req := &inlinev1beta1.CreateInlineRequest{
|
||||
Owner: &inlinev1beta1.ObjectReference{
|
||||
ApiGroup: owner.APIGroup,
|
||||
ApiVersion: owner.APIVersion,
|
||||
Kind: owner.Kind,
|
||||
Namespace: owner.Namespace,
|
||||
Name: owner.Name,
|
||||
},
|
||||
Value: value.DangerouslyExposeAndConsumeValue(),
|
||||
}
|
||||
|
||||
resp, err := client.CreateInline(ctx, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.GetName(), nil
|
||||
}
|
||||
|
||||
func (g *GRPCInlineClient) DeleteWhenOwnedByResource(ctx context.Context, owner v0alpha1.ObjectReference, name string) error {
|
||||
client, err := g.getClient(owner.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &inlinev1beta1.DeleteWhenOwnedByResourceRequest{
|
||||
Owner: &inlinev1beta1.ObjectReference{
|
||||
ApiGroup: owner.APIGroup,
|
||||
ApiVersion: owner.APIVersion,
|
||||
Kind: owner.Kind,
|
||||
Namespace: owner.Namespace,
|
||||
Name: owner.Name,
|
||||
},
|
||||
Name: name,
|
||||
}
|
||||
|
||||
_, err = client.DeleteWhenOwnedByResource(ctx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GRPCInlineClient) getClient(namespace string) (inlinev1beta1.InlineSecureValueServiceClient, error) {
|
||||
_, err := types.ParseNamespace(namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenExchangerInterceptor := authnlib.NewGrpcClientInterceptor(
|
||||
g.tokenExchanger,
|
||||
authnlib.WithClientInterceptorTracer(g.tracer),
|
||||
authnlib.WithClientInterceptorNamespace(namespace),
|
||||
authnlib.WithClientInterceptorAudience([]string{secretv1beta1.APIGroup}),
|
||||
)
|
||||
|
||||
clientConn := grpchan.InterceptClientConn(
|
||||
g.conn,
|
||||
tokenExchangerInterceptor.UnaryClientInterceptor,
|
||||
tokenExchangerInterceptor.StreamClientInterceptor,
|
||||
)
|
||||
|
||||
return inlinev1beta1.NewInlineSecureValueServiceClient(clientConn), nil
|
||||
}
|
||||
+9
-9
@@ -1,4 +1,4 @@
|
||||
package service
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -20,25 +20,25 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type inlineSecureValueService struct {
|
||||
type LocalInlineSecureValueService struct {
|
||||
tracer trace.Tracer
|
||||
secureValueService contracts.SecureValueService
|
||||
accessChecker authlib.AccessChecker
|
||||
}
|
||||
|
||||
func ProvideInlineSecureValueService(
|
||||
func NewLocalInlineSecureValueService(
|
||||
tracer trace.Tracer,
|
||||
secureValueService contracts.SecureValueService,
|
||||
accessClient authlib.AccessClient,
|
||||
) contracts.InlineSecureValueSupport {
|
||||
return &inlineSecureValueService{
|
||||
return &LocalInlineSecureValueService{
|
||||
tracer: tracer,
|
||||
secureValueService: secureValueService,
|
||||
accessChecker: accessClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlineSecureValueService) CanReference(ctx context.Context, owner common.ObjectReference, names ...string) error {
|
||||
func (s *LocalInlineSecureValueService) CanReference(ctx context.Context, owner common.ObjectReference, names ...string) error {
|
||||
ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.CanReference", trace.WithAttributes(
|
||||
attribute.String("owner.namespace", owner.Namespace),
|
||||
attribute.String("owner.apiGroup", owner.APIGroup),
|
||||
@@ -86,7 +86,7 @@ func (s *inlineSecureValueService) CanReference(ctx context.Context, owner commo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *inlineSecureValueService) isSecureValueOwnedByResource(ctx context.Context, owner common.ObjectReference, name string) (bool, error) {
|
||||
func (s *LocalInlineSecureValueService) isSecureValueOwnedByResource(ctx context.Context, owner common.ObjectReference, name string) (bool, error) {
|
||||
sv, err := s.secureValueService.Read(ctx, xkube.Namespace(owner.Namespace), name)
|
||||
if err != nil {
|
||||
if errors.Is(err, contracts.ErrSecureValueNotFound) {
|
||||
@@ -127,7 +127,7 @@ func (s *inlineSecureValueService) isSecureValueOwnedByResource(ctx context.Cont
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *inlineSecureValueService) canIdentityReadSecureValue(ctx context.Context, namespace xkube.Namespace, name string) error {
|
||||
func (s *LocalInlineSecureValueService) canIdentityReadSecureValue(ctx context.Context, namespace xkube.Namespace, name string) error {
|
||||
authInfo, ok := authlib.AuthInfoFrom(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("missing auth info in context")
|
||||
@@ -156,7 +156,7 @@ func (s *inlineSecureValueService) canIdentityReadSecureValue(ctx context.Contex
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *inlineSecureValueService) CreateInline(ctx context.Context, owner common.ObjectReference, value common.RawSecureValue) (string, error) {
|
||||
func (s *LocalInlineSecureValueService) CreateInline(ctx context.Context, owner common.ObjectReference, value common.RawSecureValue) (string, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.CreateInline", trace.WithAttributes(
|
||||
attribute.String("owner.namespace", owner.Namespace),
|
||||
attribute.String("owner.apiGroup", owner.APIGroup),
|
||||
@@ -219,7 +219,7 @@ func (s *inlineSecureValueService) CreateInline(ctx context.Context, owner commo
|
||||
return createdSv.GetName(), nil
|
||||
}
|
||||
|
||||
func (s *inlineSecureValueService) DeleteWhenOwnedByResource(ctx context.Context, owner common.ObjectReference, name string) error {
|
||||
func (s *LocalInlineSecureValueService) DeleteWhenOwnedByResource(ctx context.Context, owner common.ObjectReference, name string) error {
|
||||
ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.DeleteWhenOwnedByResource", trace.WithAttributes(
|
||||
attribute.String("owner.namespace", owner.Namespace),
|
||||
attribute.String("owner.apiGroup", owner.APIGroup),
|
||||
+26
-26
@@ -1,4 +1,4 @@
|
||||
package service_test
|
||||
package inline_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/inline"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -54,7 +54,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
"securevalues:read": {"securevalues:uid:" + sv2},
|
||||
})
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient)
|
||||
|
||||
err = svc.CanReference(ctx, owner, sv1, sv2)
|
||||
require.NoError(t, err)
|
||||
@@ -63,7 +63,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Run("when the auth info is missing it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
err := svc.CanReference(t.Context(), common.ObjectReference{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
@@ -71,7 +71,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Run("when the owner namespace does not match auth info namespace it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
reqNs := "org-2345"
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), reqNs, map[string][]string{})
|
||||
@@ -83,7 +83,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Run("when the owner namespace is empty it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{})
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Run("when the owner reference has empty fields it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
owner := common.ObjectReference{
|
||||
Namespace: defaultNs,
|
||||
@@ -125,7 +125,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Run("when no secure values are provided it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{})
|
||||
|
||||
@@ -137,7 +137,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tu := testutils.Setup(t)
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{})
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{})
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
err = svc.CanReference(ctx, owner, sv1)
|
||||
require.Error(t, err)
|
||||
@@ -189,7 +189,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
|
||||
ctx := identity.WithServiceIdentityContext(t.Context(), 1234)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
err = svc.CanReference(ctx, owner, sv1)
|
||||
require.Error(t, err)
|
||||
@@ -207,7 +207,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient)
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{
|
||||
"securevalues:read": {"securevalues:uid:another-sv"}, // can read, but another resource!
|
||||
@@ -248,7 +248,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
|
||||
createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), serviceIdentity, owner.Namespace, nil, nil)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
createdName, err := svc.CreateInline(createAuthCtx, owner, secret)
|
||||
require.NoError(t, err)
|
||||
@@ -265,7 +265,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when the auth info is missing it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
_, err := svc.CreateInline(t.Context(), common.ObjectReference{}, "")
|
||||
require.Error(t, err)
|
||||
})
|
||||
@@ -273,7 +273,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when the request identity is not a user nor a service account, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
createAuthCtx := testutils.CreateServiceAuthContext(t.Context(), "service-identity", defaultNs, nil)
|
||||
|
||||
@@ -284,7 +284,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when the owner namespace does not match auth info namespace it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
reqNs := "org-2345"
|
||||
createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", reqNs, nil, nil)
|
||||
@@ -296,7 +296,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when the owner namespace is empty it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil)
|
||||
|
||||
@@ -307,7 +307,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when the owner reference has empty fields it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
owner := common.ObjectReference{
|
||||
Namespace: defaultNs,
|
||||
@@ -339,7 +339,7 @@ func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) {
|
||||
t.Run("when an empty secret is provided it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil)
|
||||
|
||||
@@ -376,7 +376,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdSv1)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil)
|
||||
|
||||
@@ -392,7 +392,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
t.Run("when the auth info is missing it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
err := svc.DeleteWhenOwnedByResource(t.Context(), common.ObjectReference{}, "")
|
||||
require.Error(t, err)
|
||||
})
|
||||
@@ -400,7 +400,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
t.Run("when the owner namespace does not match auth info namespace it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
reqNs := "org-2345"
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), reqNs, map[string][]string{})
|
||||
@@ -412,7 +412,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
t.Run("when the owner namespace is empty it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{})
|
||||
|
||||
@@ -423,7 +423,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
t.Run("when the owner reference has empty fields it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, nil, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, nil, nil)
|
||||
|
||||
owner := common.ObjectReference{
|
||||
Namespace: defaultNs,
|
||||
@@ -467,7 +467,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdSv1)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil)
|
||||
|
||||
@@ -494,7 +494,7 @@ func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdSv1)
|
||||
|
||||
svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
svc := inline.NewLocalInlineSecureValueService(tracer, tu.SecureValueService, nil)
|
||||
|
||||
ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func ProvideInlineSecureValueService(
|
||||
cfg *setting.Cfg,
|
||||
tracer trace.Tracer,
|
||||
secureValueService contracts.SecureValueService,
|
||||
accessClient authlib.AccessClient,
|
||||
) (contracts.InlineSecureValueSupport, error) {
|
||||
if cfg.SecretsManagement.GrpcClientEnable {
|
||||
grpcClientConfig := grpcutils.ReadGrpcClientConfig(cfg)
|
||||
|
||||
if cfg.SecretsManagement.GrpcServerAddress == "" {
|
||||
return nil, fmt.Errorf("grpc_server_address is required when grpc client is enabled")
|
||||
}
|
||||
|
||||
if grpcClientConfig.Token == "" || grpcClientConfig.TokenExchangeURL == "" {
|
||||
return nil, fmt.Errorf("grpc_client_authentication.token and grpc_client_authentication.token_exchange_url are required when grpc client is enabled")
|
||||
}
|
||||
|
||||
tokenExchangeClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
|
||||
Token: grpcClientConfig.Token,
|
||||
TokenExchangeURL: grpcClientConfig.TokenExchangeURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create token exchange client: %w", err)
|
||||
}
|
||||
|
||||
tlsConfig := readTLSFromConfig(cfg)
|
||||
|
||||
client, err := NewGRPCInlineClient(tokenExchangeClient, tracer, cfg.SecretsManagement.GrpcServerAddress, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create grpc inline secure value client: %w", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
return NewLocalInlineSecureValueService(tracer, secureValueService, accessClient), nil
|
||||
}
|
||||
|
||||
func readTLSFromConfig(cfg *setting.Cfg) TLSConfig {
|
||||
if !cfg.SecretsManagement.GrpcServerUseTLS {
|
||||
return TLSConfig{
|
||||
UseTLS: false,
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
apiServer := cfg.SectionWithEnvOverrides("grafana-apiserver")
|
||||
|
||||
return TLSConfig{
|
||||
UseTLS: true,
|
||||
CertFile: apiServer.Key("proxy_client_cert_file").MustString(""),
|
||||
KeyFile: apiServer.Key("proxy_client_key_file").MustString(""),
|
||||
CAFile: apiServer.Key("apiservice_ca_bundle_file").MustString(""),
|
||||
ServerName: cfg.SecretsManagement.GrpcServerTLSServerName,
|
||||
InsecureSkipVerify: cfg.SecretsManagement.GrpcServerTLSSkipVerify,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package inline_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
inlinev1beta1 "github.com/grafana/grafana/apps/secret/inline/v1beta1"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"golang.org/x/net/nettest"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/inline"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
|
||||
"github.com/grafana/grafana/pkg/services/authn/clients"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestProvideInlineSecureValueService(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tracer := noop.NewTracerProvider().Tracer("test")
|
||||
|
||||
t.Run("when the grpc server is disabled, it returns local inline service", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.GrpcClientEnable = false
|
||||
|
||||
service, err := inline.ProvideInlineSecureValueService(cfg, nil, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.IsType(t, &inline.LocalInlineSecureValueService{}, service)
|
||||
})
|
||||
|
||||
t.Run("when grpc server is enabled but server address is missing, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
|
||||
service, err := inline.ProvideInlineSecureValueService(cfg, nil, nil, nil)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, service)
|
||||
})
|
||||
|
||||
t.Run("when grpc server is enabled but token exchange config is missing, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
cfg.SecretsManagement.GrpcServerAddress = "127.0.0.1:10000"
|
||||
|
||||
service, err := inline.ProvideInlineSecureValueService(cfg, nil, nil, nil)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, service)
|
||||
})
|
||||
|
||||
t.Run("happy path with grpc+tls server with fake token exchanger and server", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
respTokenExchanged := "test-token"
|
||||
tokenExchangeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
response := `{
|
||||
"data": {
|
||||
"token": "` + respTokenExchanged + `"
|
||||
}
|
||||
}`
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(response))
|
||||
}))
|
||||
t.Cleanup(tokenExchangeServer.Close)
|
||||
|
||||
// Set up gRPC Server with TLS
|
||||
listener, err := nettest.NewLocalListener("tcp")
|
||||
require.NoError(t, err)
|
||||
|
||||
certPaths := testutils.CreateX509TestDir(t)
|
||||
|
||||
serverCert, err := tls.LoadX509KeyPair(certPaths.ServerCert, certPaths.ServerKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{serverCert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
InsecureSkipVerify: false,
|
||||
ServerName: "localhost",
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))
|
||||
t.Cleanup(grpcServer.Stop)
|
||||
|
||||
inlineServer := &mockInlineServer{}
|
||||
inlineServer.On("CreateInline", mock.Anything, mock.Anything).Return(&inlinev1beta1.CreateInlineResponse{Name: "test-value"}, nil)
|
||||
inlineServer.On("CanReference", mock.Anything, mock.Anything).Return(&inlinev1beta1.CanReferenceResponse{}, nil)
|
||||
inlineServer.On("DeleteWhenOwnedByResource", mock.Anything, mock.Anything).Return(&inlinev1beta1.DeleteWhenOwnedByResourceResponse{}, nil)
|
||||
|
||||
inlinev1beta1.RegisterInlineSecureValueServiceServer(grpcServer, inlineServer)
|
||||
|
||||
go func() {
|
||||
_ = grpcServer.Serve(listener)
|
||||
<-t.Context().Done()
|
||||
}()
|
||||
|
||||
// Populate configuration with gRPC+TLS options and mock token exchanger
|
||||
namespace := "stacks-1234"
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement.GrpcClientEnable = true
|
||||
cfg.SecretsManagement.GrpcServerAddress = listener.Addr().String()
|
||||
cfg.SecretsManagement.GrpcServerUseTLS = true
|
||||
cfg.SecretsManagement.GrpcServerTLSServerName = "localhost"
|
||||
cfg.SecretsManagement.GrpcServerTLSSkipVerify = false
|
||||
|
||||
grpcClientAuth := cfg.Raw.Section("grpc_client_authentication")
|
||||
_, err = grpcClientAuth.NewKey("token", "test-token")
|
||||
require.NoError(t, err)
|
||||
_, err = grpcClientAuth.NewKey("token_exchange_url", tokenExchangeServer.URL)
|
||||
require.NoError(t, err)
|
||||
_, err = grpcClientAuth.NewKey("token_namespace", namespace)
|
||||
require.NoError(t, err)
|
||||
|
||||
apiServer := cfg.Raw.Section("grafana-apiserver")
|
||||
_, err = apiServer.NewKey("proxy_client_cert_file", certPaths.ClientCert)
|
||||
require.NoError(t, err)
|
||||
_, err = apiServer.NewKey("proxy_client_key_file", certPaths.ClientKey)
|
||||
require.NoError(t, err)
|
||||
_, err = apiServer.NewKey("apiservice_ca_bundle_file", certPaths.CA)
|
||||
require.NoError(t, err)
|
||||
|
||||
inlineService, err := inline.ProvideInlineSecureValueService(cfg, tracer, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.IsType(t, &inline.GRPCInlineClient{}, inlineService)
|
||||
|
||||
owner := common.ObjectReference{
|
||||
APIGroup: "example.com",
|
||||
APIVersion: "v1",
|
||||
Kind: "TestResource",
|
||||
Namespace: namespace,
|
||||
Name: "test-resource",
|
||||
}
|
||||
|
||||
name, err := inlineService.CreateInline(t.Context(), owner, common.NewSecretValue("test-value"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test-value", name)
|
||||
|
||||
err = inlineService.CanReference(t.Context(), owner, "test-value")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = inlineService.DeleteWhenOwnedByResource(t.Context(), owner, "test-value")
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.AssertExpectationsForObjects(t, inlineServer)
|
||||
|
||||
requestContext := inlineServer.Calls[0].Arguments[0].(context.Context)
|
||||
|
||||
md, ok := metadata.FromIncomingContext(requestContext)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, md)
|
||||
require.Equal(t, respTokenExchanged, md[strings.ToLower(clients.ExtJWTAuthenticationHeaderName)][0])
|
||||
})
|
||||
}
|
||||
|
||||
type mockInlineServer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
var _ inlinev1beta1.InlineSecureValueServiceServer = (*mockInlineServer)(nil)
|
||||
|
||||
func (m *mockInlineServer) CanReference(ctx context.Context, req *inlinev1beta1.CanReferenceRequest) (*inlinev1beta1.CanReferenceResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*inlinev1beta1.CanReferenceResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockInlineServer) CreateInline(ctx context.Context, req *inlinev1beta1.CreateInlineRequest) (*inlinev1beta1.CreateInlineResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*inlinev1beta1.CreateInlineResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockInlineServer) DeleteWhenOwnedByResource(ctx context.Context, req *inlinev1beta1.DeleteWhenOwnedByResourceRequest) (*inlinev1beta1.DeleteWhenOwnedByResourceResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*inlinev1beta1.DeleteWhenOwnedByResourceResponse), args.Error(1)
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
// MockInlineSecureValueSupport is an autogenerated mock type for the InlineSecureValueSupport type
|
||||
type MockInlineSecureValueSupport struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockInlineSecureValueSupport_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockInlineSecureValueSupport) EXPECT() *MockInlineSecureValueSupport_Expecter {
|
||||
return &MockInlineSecureValueSupport_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// CanReference provides a mock function with given fields: ctx, owner, names
|
||||
func (_m *MockInlineSecureValueSupport) CanReference(ctx context.Context, owner v0alpha1.ObjectReference, names ...string) error {
|
||||
_va := make([]interface{}, len(names))
|
||||
for _i := range names {
|
||||
_va[_i] = names[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, owner)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CanReference")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, v0alpha1.ObjectReference, ...string) error); ok {
|
||||
r0 = rf(ctx, owner, names...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockInlineSecureValueSupport_CanReference_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanReference'
|
||||
type MockInlineSecureValueSupport_CanReference_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CanReference is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - owner v0alpha1.ObjectReference
|
||||
// - names ...string
|
||||
func (_e *MockInlineSecureValueSupport_Expecter) CanReference(ctx interface{}, owner interface{}, names ...interface{}) *MockInlineSecureValueSupport_CanReference_Call {
|
||||
return &MockInlineSecureValueSupport_CanReference_Call{Call: _e.mock.On("CanReference",
|
||||
append([]interface{}{ctx, owner}, names...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CanReference_Call) Run(run func(ctx context.Context, owner v0alpha1.ObjectReference, names ...string)) *MockInlineSecureValueSupport_CanReference_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]string, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(string)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(v0alpha1.ObjectReference), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CanReference_Call) Return(_a0 error) *MockInlineSecureValueSupport_CanReference_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CanReference_Call) RunAndReturn(run func(context.Context, v0alpha1.ObjectReference, ...string) error) *MockInlineSecureValueSupport_CanReference_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreateInline provides a mock function with given fields: ctx, owner, value
|
||||
func (_m *MockInlineSecureValueSupport) CreateInline(ctx context.Context, owner v0alpha1.ObjectReference, value v0alpha1.RawSecureValue) (string, error) {
|
||||
ret := _m.Called(ctx, owner, value)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateInline")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, v0alpha1.ObjectReference, v0alpha1.RawSecureValue) (string, error)); ok {
|
||||
return rf(ctx, owner, value)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, v0alpha1.ObjectReference, v0alpha1.RawSecureValue) string); ok {
|
||||
r0 = rf(ctx, owner, value)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, v0alpha1.ObjectReference, v0alpha1.RawSecureValue) error); ok {
|
||||
r1 = rf(ctx, owner, value)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockInlineSecureValueSupport_CreateInline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateInline'
|
||||
type MockInlineSecureValueSupport_CreateInline_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreateInline is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - owner v0alpha1.ObjectReference
|
||||
// - value v0alpha1.RawSecureValue
|
||||
func (_e *MockInlineSecureValueSupport_Expecter) CreateInline(ctx interface{}, owner interface{}, value interface{}) *MockInlineSecureValueSupport_CreateInline_Call {
|
||||
return &MockInlineSecureValueSupport_CreateInline_Call{Call: _e.mock.On("CreateInline", ctx, owner, value)}
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CreateInline_Call) Run(run func(ctx context.Context, owner v0alpha1.ObjectReference, value v0alpha1.RawSecureValue)) *MockInlineSecureValueSupport_CreateInline_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(v0alpha1.ObjectReference), args[2].(v0alpha1.RawSecureValue))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CreateInline_Call) Return(_a0 string, _a1 error) *MockInlineSecureValueSupport_CreateInline_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_CreateInline_Call) RunAndReturn(run func(context.Context, v0alpha1.ObjectReference, v0alpha1.RawSecureValue) (string, error)) *MockInlineSecureValueSupport_CreateInline_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// DeleteWhenOwnedByResource provides a mock function with given fields: ctx, owner, name
|
||||
func (_m *MockInlineSecureValueSupport) DeleteWhenOwnedByResource(ctx context.Context, owner v0alpha1.ObjectReference, name string) error {
|
||||
ret := _m.Called(ctx, owner, name)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteWhenOwnedByResource")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, v0alpha1.ObjectReference, string) error); ok {
|
||||
r0 = rf(ctx, owner, name)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteWhenOwnedByResource'
|
||||
type MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// DeleteWhenOwnedByResource is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - owner v0alpha1.ObjectReference
|
||||
// - name string
|
||||
func (_e *MockInlineSecureValueSupport_Expecter) DeleteWhenOwnedByResource(ctx interface{}, owner interface{}, name interface{}) *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call {
|
||||
return &MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call{Call: _e.mock.On("DeleteWhenOwnedByResource", ctx, owner, name)}
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call) Run(run func(ctx context.Context, owner v0alpha1.ObjectReference, name string)) *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(v0alpha1.ObjectReference), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call) Return(_a0 error) *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call) RunAndReturn(run func(context.Context, v0alpha1.ObjectReference, string) error) *MockInlineSecureValueSupport_DeleteWhenOwnedByResource_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockInlineSecureValueSupport creates a new instance of MockInlineSecureValueSupport. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockInlineSecureValueSupport(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockInlineSecureValueSupport {
|
||||
mock := &MockInlineSecureValueSupport{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/madflojo/testcerts"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -80,7 +81,6 @@ func Setup(t *testing.T, opts ...func(*SetupConfig)) Sut {
|
||||
defaultKey := "SdlklWklckeLS"
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SecretsManagement = setting.SecretsManagerSettings{
|
||||
DecryptServerType: "local",
|
||||
CurrentEncryptionProvider: "secret_key.v1",
|
||||
ConfiguredKMSProviders: map[string]map[string]string{"secret_key.v1": {"secret_key": defaultKey}},
|
||||
}
|
||||
@@ -131,9 +131,6 @@ func Setup(t *testing.T, opts ...func(*SetupConfig)) Sut {
|
||||
require.NoError(t, err)
|
||||
|
||||
testCfg := setting.NewCfg()
|
||||
testCfg.SecretsManagement = setting.SecretsManagerSettings{
|
||||
DecryptServerType: "local",
|
||||
}
|
||||
|
||||
decryptService, err := decrypt.ProvideDecryptService(testCfg, tracer, decryptStorage)
|
||||
require.NoError(t, err)
|
||||
@@ -287,3 +284,40 @@ func CreateOBOAuthContext(
|
||||
|
||||
return types.WithAuthInfo(ctx, requester)
|
||||
}
|
||||
|
||||
type TestCertPaths struct {
|
||||
ClientCert string
|
||||
ClientKey string
|
||||
ServerCert string
|
||||
ServerKey string
|
||||
CA string
|
||||
}
|
||||
|
||||
func CreateX509TestDir(t *testing.T) TestCertPaths {
|
||||
t.Helper()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
ca := testcerts.NewCA()
|
||||
caCertFile, _, err := ca.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
serverKp, err := ca.NewKeyPair("localhost")
|
||||
require.NoError(t, err)
|
||||
|
||||
serverCertFile, serverKeyFile, err := serverKp.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
clientKp, err := ca.NewKeyPair()
|
||||
require.NoError(t, err)
|
||||
clientCertFile, clientKeyFile, err := clientKp.ToTempFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
return TestCertPaths{
|
||||
ClientCert: clientCertFile.Name(),
|
||||
ClientKey: clientKeyFile.Name(),
|
||||
ServerCert: serverCertFile.Name(),
|
||||
ServerKey: serverKeyFile.Name(),
|
||||
CA: caCertFile.Name(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
secretdecrypt "github.com/grafana/grafana/pkg/registry/apis/secret/decrypt"
|
||||
cipher "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher/service"
|
||||
encryptionManager "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/manager"
|
||||
secretinline "github.com/grafana/grafana/pkg/registry/apis/secret/inline"
|
||||
secretsecurevalueservice "github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||
secretvalidator "github.com/grafana/grafana/pkg/registry/apis/secret/validator"
|
||||
appregistry "github.com/grafana/grafana/pkg/registry/apps"
|
||||
@@ -428,6 +429,7 @@ var wireBasicSet = wire.NewSet(
|
||||
secretmetadata.ProvideDecryptStorage,
|
||||
secretdecrypt.ProvideDecryptAuthorizer,
|
||||
secretdecrypt.ProvideDecryptService,
|
||||
secretinline.ProvideInlineSecureValueService,
|
||||
secretencryption.ProvideDataKeyStorage,
|
||||
secretencryption.ProvideGlobalDataKeyStorage,
|
||||
secretencryption.ProvideEncryptedValueStorage,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,24 +16,24 @@ type SecretsManagerSettings struct {
|
||||
// In OSS, the provider type can only be "secret_key". In Enterprise, it can additionally be one of: "aws_kms", "azure_keyvault", "google_kms", "hashicorp_vault"
|
||||
ConfiguredKMSProviders map[string]map[string]string
|
||||
|
||||
DecryptServerType string // "local" or "grpc"
|
||||
DecryptServerUseTLS bool // Applicable for decrypt_server_type=grpc. Whether to use TLS for the decrypt server
|
||||
DecryptServerTLSSkipVerify bool // Applicable for decrypt_server_type=grpc. Whether to skip TLS verification for the decrypt server
|
||||
DecryptServerTLSServerName string // Applicable for decrypt_server_type=grpc. Server name to use for TLS verification
|
||||
DecryptServerAddress string // Applicable for decrypt_server_type=grpc. Address for external secrets server
|
||||
DecryptGrafanaServiceName string // Service name to use for background grafana decryption
|
||||
GrpcClientEnable bool // Whether to enable the gRPC client. If disabled, it will use the in-process services implementations.
|
||||
GrpcServerUseTLS bool // Whether to use TLS when communicating with the gRPC server
|
||||
GrpcServerTLSSkipVerify bool // Whether to skip TLS verification when communicating with the gRPC server
|
||||
GrpcServerTLSServerName string // Server name to use for TLS verification
|
||||
GrpcServerAddress string // Address for gRPC secrets server
|
||||
GrpcGrafanaServiceName string // Service name to use for background grafana decryption/inline
|
||||
}
|
||||
|
||||
func (cfg *Cfg) readSecretsManagerSettings() {
|
||||
secretsMgmt := cfg.Raw.Section("secrets_manager")
|
||||
cfg.SecretsManagement.CurrentEncryptionProvider = secretsMgmt.Key("encryption_provider").MustString(MisconfiguredProvider)
|
||||
|
||||
cfg.SecretsManagement.DecryptServerType = valueAsString(secretsMgmt, "decrypt_server_type", "local")
|
||||
cfg.SecretsManagement.DecryptServerUseTLS = secretsMgmt.Key("decrypt_server_use_tls").MustBool(false)
|
||||
cfg.SecretsManagement.DecryptServerTLSSkipVerify = secretsMgmt.Key("decrypt_server_tls_skip_verify").MustBool(false)
|
||||
cfg.SecretsManagement.DecryptServerTLSServerName = valueAsString(secretsMgmt, "decrypt_server_tls_server_name", "")
|
||||
cfg.SecretsManagement.DecryptServerAddress = valueAsString(secretsMgmt, "decrypt_server_address", "")
|
||||
cfg.SecretsManagement.DecryptGrafanaServiceName = valueAsString(secretsMgmt, "decrypt_grafana_service_name", "")
|
||||
cfg.SecretsManagement.GrpcClientEnable = secretsMgmt.Key("grpc_client_enable").MustBool(false)
|
||||
cfg.SecretsManagement.GrpcServerUseTLS = secretsMgmt.Key("grpc_server_use_tls").MustBool(false)
|
||||
cfg.SecretsManagement.GrpcServerTLSSkipVerify = secretsMgmt.Key("grpc_server_tls_skip_verify").MustBool(false)
|
||||
cfg.SecretsManagement.GrpcServerTLSServerName = valueAsString(secretsMgmt, "grpc_server_tls_server_name", "")
|
||||
cfg.SecretsManagement.GrpcServerAddress = valueAsString(secretsMgmt, "grpc_server_address", "")
|
||||
cfg.SecretsManagement.GrpcGrafanaServiceName = valueAsString(secretsMgmt, "grpc_grafana_service_name", "")
|
||||
|
||||
// Extract available KMS providers from configuration sections
|
||||
providers := make(map[string]map[string]string)
|
||||
|
||||
Reference in New Issue
Block a user