mirror of https://github.com/grafana/grafana.git
				
				
				
			Unistore: Wire up inline secure values (#110072)
* Unistore: Wire up inline secure values * add validation and test * linter
This commit is contained in:
		
							parent
							
								
									e7ccefcf92
								
							
						
					
					
						commit
						1091054c25
					
				|  | @ -19,35 +19,44 @@ func ProvideInlineSecureValueService( | |||
| 	accessClient authlib.AccessClient, | ||||
| ) (contracts.InlineSecureValueSupport, error) { | ||||
| 	if cfg.SecretsManagement.GrpcClientEnable { | ||||
| 		grpcClientConfig := grpcutils.ReadGrpcClientConfig(cfg) | ||||
| 		return NewGRPCSecureValueService( | ||||
| 			grpcutils.ReadGrpcClientConfig(cfg), | ||||
| 			cfg.SecretsManagement.GrpcServerAddress, | ||||
| 			readTLSFromConfig(cfg), | ||||
| 			tracer, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 		if cfg.SecretsManagement.GrpcServerAddress == "" { | ||||
| 	return NewLocalInlineSecureValueService(tracer, secureValueService, accessClient), nil | ||||
| } | ||||
| 
 | ||||
| func NewGRPCSecureValueService(tokenCfg *grpcutils.GrpcClientConfig, | ||||
| 	address string, | ||||
| 	tlsCfg TLSConfig, | ||||
| 	tracer trace.Tracer, | ||||
| ) (contracts.InlineSecureValueSupport, error) { | ||||
| 	if address == "" { | ||||
| 		return nil, fmt.Errorf("grpc_server_address is required when grpc client is enabled") | ||||
| 	} | ||||
| 
 | ||||
| 		if grpcClientConfig.Token == "" || grpcClientConfig.TokenExchangeURL == "" { | ||||
| 	if tokenCfg.Token == "" || tokenCfg.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, | ||||
| 		Token:            tokenCfg.Token, | ||||
| 		TokenExchangeURL: tokenCfg.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) | ||||
| 	client, err := NewGRPCInlineClient(tokenExchangeClient, tracer, address, tlsCfg) | ||||
| 	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 { | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ import ( | |||
| 
 | ||||
| 	"github.com/grafana/grafana/pkg/infra/tracing" | ||||
| 	secret "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" | ||||
| 	inlinesecurevalue "github.com/grafana/grafana/pkg/registry/apis/secret/inline" | ||||
| 	"github.com/grafana/grafana/pkg/services/authn/grpcutils" | ||||
| 	"github.com/grafana/grafana/pkg/setting" | ||||
| 	"github.com/grafana/grafana/pkg/storage/unified/apistore" | ||||
| 	"github.com/grafana/grafana/pkg/storage/unified/resource" | ||||
|  | @ -51,6 +53,14 @@ type StorageOptions struct { | |||
| 	GrpcClientAuthenticationTokenNamespace   string | ||||
| 	GrpcClientAuthenticationAllowInsecure    bool | ||||
| 
 | ||||
| 	// Secrets Manager Configuration for InlineSecureValueSupport
 | ||||
| 	SecretsManagerGrpcClientEnable        bool | ||||
| 	SecretsManagerGrpcServerAddress       string | ||||
| 	SecretsManagerGrpcServerUseTLS        bool | ||||
| 	SecretsManagerGrpcServerTLSSkipVerify bool | ||||
| 	SecretsManagerGrpcServerTLSServerName string | ||||
| 	SecretsManagerGrpcServerTLSCAFile     string | ||||
| 
 | ||||
| 	// For file storage, this is the requested path
 | ||||
| 	DataPath string | ||||
| 
 | ||||
|  | @ -92,6 +102,14 @@ func (o *StorageOptions) AddFlags(fs *pflag.FlagSet) { | |||
| 	fs.StringVar(&o.GrpcClientAuthenticationTokenExchangeURL, "grpc-client-authentication-token-exchange-url", o.GrpcClientAuthenticationTokenExchangeURL, "Token exchange url for grpc client authentication") | ||||
| 	fs.StringVar(&o.GrpcClientAuthenticationTokenNamespace, "grpc-client-authentication-token-namespace", o.GrpcClientAuthenticationTokenNamespace, "Token namespace for grpc client authentication") | ||||
| 	fs.BoolVar(&o.GrpcClientAuthenticationAllowInsecure, "grpc-client-authentication-allow-insecure", o.GrpcClientAuthenticationAllowInsecure, "Allow insecure grpc client authentication") | ||||
| 
 | ||||
| 	// Secrets Manager Configuration flags
 | ||||
| 	fs.BoolVar(&o.SecretsManagerGrpcClientEnable, "grafana.secrets-manager.grpc-client-enable", false, "Enable gRPC client for secrets manager") | ||||
| 	fs.StringVar(&o.SecretsManagerGrpcServerAddress, "grafana.secrets-manager.grpc-server-address", "", "gRPC server address for secrets manager") | ||||
| 	fs.BoolVar(&o.SecretsManagerGrpcServerUseTLS, "grafana.secrets-manager.grpc-server-use-tls", false, "Use TLS for gRPC server communication") | ||||
| 	fs.BoolVar(&o.SecretsManagerGrpcServerTLSSkipVerify, "grafana.secrets-manager.grpc-server-tls-skip-verify", false, "Skip TLS verification for gRPC server") | ||||
| 	fs.StringVar(&o.SecretsManagerGrpcServerTLSServerName, "grafana.secrets-manager.grpc-server-tls-server-name", "", "Server name for TLS verification") | ||||
| 	fs.StringVar(&o.SecretsManagerGrpcServerTLSCAFile, "grafana.secrets-manager.grpc-server-tls-ca-file", "", "CA file for TLS verification") | ||||
| } | ||||
| 
 | ||||
| func (o *StorageOptions) Validate() []error { | ||||
|  | @ -130,10 +148,19 @@ func (o *StorageOptions) Validate() []error { | |||
| 			errs = append(errs, fmt.Errorf("grpc client auth namespace is required for unified-grpc storage")) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if o.SecretsManagerGrpcClientEnable { | ||||
| 		if o.SecretsManagerGrpcServerAddress == "" { | ||||
| 			errs = append(errs, fmt.Errorf("secrets manager grpc server address is required for secrets manager grpc client")) | ||||
| 		} | ||||
| 		if o.SecretsManagerGrpcServerUseTLS && !o.SecretsManagerGrpcServerTLSSkipVerify && o.SecretsManagerGrpcServerTLSCAFile == "" { | ||||
| 			errs = append(errs, fmt.Errorf("secrets manager grpc server ca file is required for secrets manager grpc client")) | ||||
| 		} | ||||
| 	} | ||||
| 	return errs | ||||
| } | ||||
| 
 | ||||
| func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfig, etcdOptions *options.EtcdOptions, tracer tracing.Tracer) error { | ||||
| func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfig, etcdOptions *options.EtcdOptions, tracer tracing.Tracer, secureServing *options.SecureServingOptions) error { | ||||
| 	if o.StorageType != StorageTypeUnifiedGrpc { | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -168,6 +195,35 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi | |||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// setup inline secrets if configured
 | ||||
| 	if o.InlineSecrets == nil && o.SecretsManagerGrpcClientEnable { | ||||
| 		tlsCfg := inlinesecurevalue.TLSConfig{ | ||||
| 			UseTLS:             o.SecretsManagerGrpcServerUseTLS, | ||||
| 			CAFile:             o.SecretsManagerGrpcServerTLSCAFile, | ||||
| 			ServerName:         o.SecretsManagerGrpcServerTLSServerName, | ||||
| 			InsecureSkipVerify: o.SecretsManagerGrpcServerTLSSkipVerify, | ||||
| 		} | ||||
| 		if o.SecretsManagerGrpcServerUseTLS && secureServing != nil { | ||||
| 			tlsCfg.CertFile = secureServing.ServerCert.CertKey.CertFile | ||||
| 			tlsCfg.KeyFile = secureServing.ServerCert.CertKey.KeyFile | ||||
| 		} | ||||
| 		inlineSecureValueService, err := inlinesecurevalue.NewGRPCSecureValueService( | ||||
| 			&grpcutils.GrpcClientConfig{ | ||||
| 				Token:            o.GrpcClientAuthenticationToken, | ||||
| 				TokenExchangeURL: o.GrpcClientAuthenticationTokenExchangeURL, | ||||
| 				TokenNamespace:   o.GrpcClientAuthenticationTokenNamespace, | ||||
| 			}, | ||||
| 			o.SecretsManagerGrpcServerAddress, | ||||
| 			tlsCfg, | ||||
| 			tracer, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to create inline secure value service: %w", err) | ||||
| 		} | ||||
| 		o.InlineSecrets = inlineSecureValueService | ||||
| 	} | ||||
| 
 | ||||
| 	getter := apistore.NewRESTOptionsGetterForClient(unified, o.InlineSecrets, etcdOptions.StorageConfig, o.ConfigProvider) | ||||
| 	serverConfig.RESTOptionsGetter = getter | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -30,6 +30,47 @@ func TestStorageOptions_Validate(t *testing.T) { | |||
| 			}, | ||||
| 			wantErr: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with secrets manager grpc client and no server address", | ||||
| 			Opts: StorageOptions{ | ||||
| 				StorageType:                              StorageTypeUnifiedGrpc, | ||||
| 				Address:                                  "localhost:10000", | ||||
| 				GrpcClientAuthenticationToken:            "1234", | ||||
| 				GrpcClientAuthenticationTokenExchangeURL: "http://localhost:8080", | ||||
| 				GrpcClientAuthenticationTokenNamespace:   "*", | ||||
| 				SecretsManagerGrpcClientEnable:           true, | ||||
| 			}, | ||||
| 			wantErr: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with secrets manager grpc client and no server ca file", | ||||
| 			Opts: StorageOptions{ | ||||
| 				StorageType:                              StorageTypeUnifiedGrpc, | ||||
| 				Address:                                  "localhost:10000", | ||||
| 				GrpcClientAuthenticationToken:            "1234", | ||||
| 				GrpcClientAuthenticationTokenExchangeURL: "http://localhost:8080", | ||||
| 				GrpcClientAuthenticationTokenNamespace:   "*", | ||||
| 				SecretsManagerGrpcClientEnable:           true, | ||||
| 				SecretsManagerGrpcServerAddress:          "localhost:10000", | ||||
| 				SecretsManagerGrpcServerUseTLS:           true, | ||||
| 			}, | ||||
| 			wantErr: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with secrets manager grpc client and server ca file", | ||||
| 			Opts: StorageOptions{ | ||||
| 				StorageType:                              StorageTypeUnifiedGrpc, | ||||
| 				Address:                                  "localhost:10000", | ||||
| 				GrpcClientAuthenticationToken:            "1234", | ||||
| 				GrpcClientAuthenticationTokenExchangeURL: "http://localhost:8080", | ||||
| 				GrpcClientAuthenticationTokenNamespace:   "*", | ||||
| 				SecretsManagerGrpcClientEnable:           true, | ||||
| 				SecretsManagerGrpcServerAddress:          "localhost:10000", | ||||
| 				SecretsManagerGrpcServerUseTLS:           true, | ||||
| 				SecretsManagerGrpcServerTLSCAFile:        "ca.crt", | ||||
| 			}, | ||||
| 			wantErr: false, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package sql | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
|  | @ -13,6 +14,7 @@ import ( | |||
| 	"github.com/grafana/dskit/services" | ||||
| 	infraDB "github.com/grafana/grafana/pkg/infra/db" | ||||
| 	secrets "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" | ||||
| 	inlinesecurevalue "github.com/grafana/grafana/pkg/registry/apis/secret/inline" | ||||
| 	"github.com/grafana/grafana/pkg/services/featuremgmt" | ||||
| 	"github.com/grafana/grafana/pkg/services/sqlstore/migrator" | ||||
| 	"github.com/grafana/grafana/pkg/setting" | ||||
|  | @ -48,6 +50,20 @@ func NewResourceServer( | |||
| 	opts ServerOptions, | ||||
| ) (resource.ResourceServer, error) { | ||||
| 	apiserverCfg := opts.Cfg.SectionWithEnvOverrides("grafana-apiserver") | ||||
| 
 | ||||
| 	if opts.SecureValues == nil && opts.Cfg != nil && opts.Cfg.SecretsManagement.GrpcClientEnable { | ||||
| 		inlineSecureValueService, err := inlinesecurevalue.ProvideInlineSecureValueService( | ||||
| 			opts.Cfg, | ||||
| 			opts.Tracer, | ||||
| 			nil, // not needed for gRPC client mode
 | ||||
| 			nil, // not needed for gRPC client mode
 | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to create inline secure value service: %w", err) | ||||
| 		} | ||||
| 		opts.SecureValues = inlineSecureValueService | ||||
| 	} | ||||
| 
 | ||||
| 	serverOptions := resource.ResourceServerOptions{ | ||||
| 		Tracer: opts.Tracer, | ||||
| 		Blob: resource.BlobConfig{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue