This commit is contained in:
Claudiu Dragalina-Paraipan 2024-08-02 18:54:29 +03:00
parent c46b42a595
commit 763961210c
6 changed files with 95 additions and 3 deletions

View File

@ -19,6 +19,7 @@ const (
StorageTypeLegacy StorageType = "legacy"
StorageTypeUnified StorageType = "unified"
StorageTypeUnifiedGrpc StorageType = "unified-grpc"
// TODO(drclau): add StorageTypeUnifiedCloud?
)
type StorageOptions struct {

View File

@ -288,6 +288,7 @@ func (s *service) start(ctx context.Context) error {
}
// Create a client instance
// TODO(drclau): differentiate based on grpc_mode (e.g. on-prem vs. cloud)
client, err := resource.NewResourceStoreClientGRPC(conn)
if err != nil {
return err

View File

@ -1,6 +1,8 @@
package grpcutils
import (
"fmt"
"github.com/grafana/grafana/pkg/setting"
)
@ -10,7 +12,7 @@ type GrpcClientConfig struct {
TokenNamespace string
}
func ReadCfg(cfg *setting.Cfg) *GrpcClientConfig {
func ReadGrpcClientConfig(cfg *setting.Cfg) *GrpcClientConfig {
section := cfg.SectionWithEnvOverrides("grpc_client_authentication")
return &GrpcClientConfig{
@ -19,3 +21,41 @@ func ReadCfg(cfg *setting.Cfg) *GrpcClientConfig {
TokenNamespace: section.Key("token_namespace").MustString("stack-" + cfg.StackID),
}
}
type Mode string
func (s Mode) IsValid() bool {
switch s {
case ModeGRPC, ModeInProc, ModeCloud:
return true
}
return false
}
const (
ModeGRPC Mode = "grpc"
ModeInProc Mode = "inproc"
ModeCloud Mode = "cloud"
)
type GrpcServerConfig struct {
Mode Mode
SigningKeysURL string
AllowedAudiences []string
}
func ReadGprcServerConfig(cfg *setting.Cfg) (*GrpcServerConfig, error) {
section := cfg.SectionWithEnvOverrides("grpc_server_authentication")
mode := Mode(section.Key("mode").MustString(string(ModeGRPC)))
if !mode.IsValid() {
return nil, fmt.Errorf("grpc_server_authentication: invalid mode %q", mode)
}
return &GrpcServerConfig{
Mode: mode,
SigningKeysURL: section.Key("signing_keys_url").MustString(""),
AllowedAudiences: section.Key("allowed_audiences").Strings(","),
}, nil
}

View File

@ -44,6 +44,12 @@ func newLegacyServer(
return s, nil
}
// AuthFuncOverride
// FIXME(drclau): make sure we only run this when app_mode = development
func (s *legacyServer) AuthFuncOverride(ctx context.Context, _ string) (context.Context, error) {
return ctx, nil
}
func (s *legacyServer) Read(ctx context.Context, req *authzv1.ReadRequest) (*authzv1.ReadResponse, error) {
ctx, span := s.tracer.Start(ctx, "authz.grpc.Read")
defer span.End()

View File

@ -6,6 +6,8 @@ import (
"net"
"time"
authnlib "github.com/grafana/authlib/authn"
authzlib "github.com/grafana/authlib/authz"
"github.com/grafana/dskit/instrument"
"github.com/grafana/dskit/middleware"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@ -70,12 +72,21 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, authe
var opts []grpc.ServerOption
namespaceFmt := authnlib.OnPremNamespaceFormatter
// TODO(drclau): check the actual mode setting here
if cfg.StackID != "" {
namespaceFmt = authnlib.CloudNamespaceFormatter
}
namespaceChecker := authzlib.NewNamespaceAccessChecker(namespaceFmt)
stackIdExtractor := authzlib.MetadataStackIDExtractor(authzlib.DefaultStackIDMetadataKey)
// Default auth is admin token check, but this can be overridden by
// services which implement ServiceAuthFuncOverride interface.
// See https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/interceptors/auth/auth.go#L30.
opts = append(opts, []grpc.ServerOption{
grpc.ChainUnaryInterceptor(
grpcAuth.UnaryServerInterceptor(authenticator.Authenticate),
authzlib.UnaryNamespaceAccessInterceptor(namespaceChecker, stackIdExtractor),
interceptors.TracingUnaryInterceptor(tracer),
interceptors.LoggingUnaryInterceptor(s.cfg, s.logger), // needs to be registered after tracing interceptor to get trace id
middleware.UnaryServerInstrumentInterceptor(grpcRequestDuration),
@ -83,6 +94,7 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, authe
grpc.ChainStreamInterceptor(
interceptors.TracingStreamInterceptor(tracer),
grpcAuth.StreamServerInterceptor(authenticator.Authenticate),
authzlib.StreamNamespaceAccessInterceptor(namespaceChecker, stackIdExtractor),
middleware.StreamServerInstrumentInterceptor(grpcRequestDuration),
),
}...)

View File

@ -3,6 +3,7 @@ package sql
import (
"context"
authnlib "github.com/grafana/authlib/authn"
"github.com/grafana/dskit/services"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc/health/grpc_health_v1"
@ -11,12 +12,12 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/grpcserver"
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
)
var (
@ -62,7 +63,38 @@ func ProvideService(
return nil, err
}
authn := &grpc.Authenticator{}
authCfg, err := grpcutils.ReadGprcServerConfig(cfg)
if err != nil {
return nil, err
}
grpcAuthCfg := authnlib.GrpcAuthenticatorConfig{
KeyRetrieverConfig: authnlib.KeyRetrieverConfig{
SigningKeysURL: authCfg.SigningKeysURL,
},
VerifierConfig: authnlib.VerifierConfig{
AllowedAudiences: authCfg.AllowedAudiences,
},
}
grpcOpts := []authnlib.GrpcAuthenticatorOption{}
switch authCfg.Mode {
case grpcutils.ModeInProc:
// NOOP: IDTokenClaims are added to ctx client-side
// TODO(drclau): do we need orgId?
case grpcutils.ModeGRPC:
grpcOpts = append(grpcOpts,
authnlib.WithDisableAccessTokenAuthOption(),
authnlib.WithIDTokenAuthOption(true),
)
case grpcutils.ModeCloud:
grpcOpts = append(grpcOpts, authnlib.WithIDTokenAuthOption(true))
}
authn, err := authnlib.NewGrpcAuthenticator(
&grpcAuthCfg,
grpcOpts...,
)
s := &service{
cfg: cfg,