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, | 	accessClient authlib.AccessClient, | ||||||
| ) (contracts.InlineSecureValueSupport, error) { | ) (contracts.InlineSecureValueSupport, error) { | ||||||
| 	if cfg.SecretsManagement.GrpcClientEnable { | 	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") | 		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") | 		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{ | 	tokenExchangeClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{ | ||||||
| 			Token:            grpcClientConfig.Token, | 		Token:            tokenCfg.Token, | ||||||
| 			TokenExchangeURL: grpcClientConfig.TokenExchangeURL, | 		TokenExchangeURL: tokenCfg.TokenExchangeURL, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to create token exchange client: %w", err) | 		return nil, fmt.Errorf("failed to create token exchange client: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		tlsConfig := readTLSFromConfig(cfg) | 	client, err := NewGRPCInlineClient(tokenExchangeClient, tracer, address, tlsCfg) | ||||||
| 
 |  | ||||||
| 		client, err := NewGRPCInlineClient(tokenExchangeClient, tracer, cfg.SecretsManagement.GrpcServerAddress, tlsConfig) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to create grpc inline secure value client: %w", err) | 		return nil, fmt.Errorf("failed to create grpc inline secure value client: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return client, nil | 	return client, nil | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return NewLocalInlineSecureValueService(tracer, secureValueService, accessClient), nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func readTLSFromConfig(cfg *setting.Cfg) TLSConfig { | func readTLSFromConfig(cfg *setting.Cfg) TLSConfig { | ||||||
|  |  | ||||||
|  | @ -15,6 +15,8 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"github.com/grafana/grafana/pkg/infra/tracing" | 	"github.com/grafana/grafana/pkg/infra/tracing" | ||||||
| 	secret "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" | 	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/setting" | ||||||
| 	"github.com/grafana/grafana/pkg/storage/unified/apistore" | 	"github.com/grafana/grafana/pkg/storage/unified/apistore" | ||||||
| 	"github.com/grafana/grafana/pkg/storage/unified/resource" | 	"github.com/grafana/grafana/pkg/storage/unified/resource" | ||||||
|  | @ -51,6 +53,14 @@ type StorageOptions struct { | ||||||
| 	GrpcClientAuthenticationTokenNamespace   string | 	GrpcClientAuthenticationTokenNamespace   string | ||||||
| 	GrpcClientAuthenticationAllowInsecure    bool | 	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
 | 	// For file storage, this is the requested path
 | ||||||
| 	DataPath string | 	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.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.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") | 	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 { | 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")) | 			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 | 	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 { | 	if o.StorageType != StorageTypeUnifiedGrpc { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | @ -168,6 +195,35 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		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) | 	getter := apistore.NewRESTOptionsGetterForClient(unified, o.InlineSecrets, etcdOptions.StorageConfig, o.ConfigProvider) | ||||||
| 	serverConfig.RESTOptionsGetter = getter | 	serverConfig.RESTOptionsGetter = getter | ||||||
| 	return nil | 	return nil | ||||||
|  |  | ||||||
|  | @ -30,6 +30,47 @@ func TestStorageOptions_Validate(t *testing.T) { | ||||||
| 			}, | 			}, | ||||||
| 			wantErr: false, | 			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 { | 	for _, tt := range tests { | ||||||
| 		t.Run(tt.name, func(t *testing.T) { | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package sql | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | @ -13,6 +14,7 @@ import ( | ||||||
| 	"github.com/grafana/dskit/services" | 	"github.com/grafana/dskit/services" | ||||||
| 	infraDB "github.com/grafana/grafana/pkg/infra/db" | 	infraDB "github.com/grafana/grafana/pkg/infra/db" | ||||||
| 	secrets "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" | 	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/featuremgmt" | ||||||
| 	"github.com/grafana/grafana/pkg/services/sqlstore/migrator" | 	"github.com/grafana/grafana/pkg/services/sqlstore/migrator" | ||||||
| 	"github.com/grafana/grafana/pkg/setting" | 	"github.com/grafana/grafana/pkg/setting" | ||||||
|  | @ -48,6 +50,20 @@ func NewResourceServer( | ||||||
| 	opts ServerOptions, | 	opts ServerOptions, | ||||||
| ) (resource.ResourceServer, error) { | ) (resource.ResourceServer, error) { | ||||||
| 	apiserverCfg := opts.Cfg.SectionWithEnvOverrides("grafana-apiserver") | 	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{ | 	serverOptions := resource.ResourceServerOptions{ | ||||||
| 		Tracer: opts.Tracer, | 		Tracer: opts.Tracer, | ||||||
| 		Blob: resource.BlobConfig{ | 		Blob: resource.BlobConfig{ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue