diff --git a/pkg/services/authz/README.md b/pkg/services/authz/README.md index c230ba1bb51..fa1b40ed6d4 100644 --- a/pkg/services/authz/README.md +++ b/pkg/services/authz/README.md @@ -38,14 +38,33 @@ Here is an example to connect the authorization client to a remote grpc server. ```ini [authorization] remote_address = "server.example.org:10000" +listen = false mode = "grpc" ``` -Here is an example to register the authorization service on the Grafana GRPC server and connect the client to it through grpc +Here is an example to register the authorization service on the Grafana GRPC server and connect the client to it through grpc. ```ini +app_mode = development + [authorization] remote_address = "localhost:10000" listen = true mode = "grpc" ``` + +Here is an example to connect the authorization client to a remote grpc server and use access token authentication. +```ini +[environment] +stack_id = 11 + +[authorization] +remote_address = "server.example.org:10000" +mode = "grpc" +listen = false + +[grpc_client_authentication] +token = "ReplaceWithToken" +token_exchange_url = "signing-server.example.org/path/to/signing" +token_namespace = "stacks-11" +``` \ No newline at end of file diff --git a/pkg/services/authz/client.go b/pkg/services/authz/client.go index 57fdb464748..df0e3d6ae44 100644 --- a/pkg/services/authz/client.go +++ b/pkg/services/authz/client.go @@ -55,14 +55,16 @@ func ProvideAuthZClient( return nil, err } case ModeGRPC: - client, err = newGrpcLegacyClient(authCfg.remoteAddress) - if err != nil { - return nil, err - } - case ModeCloud: - client, err = newCloudLegacyClient(authCfg) - if err != nil { - return nil, err + if cfg.StackID == "" { + client, err = newGrpcLegacyClient(authCfg) + if err != nil { + return nil, err + } + } else { + client, err = newCloudLegacyClient(authCfg) + if err != nil { + return nil, err + } } } @@ -83,7 +85,7 @@ func ProvideStandaloneAuthZClient( return nil, err } - return newGrpcLegacyClient(authCfg.remoteAddress) + return newGrpcLegacyClient(authCfg) } func newInProcLegacyClient(server *legacyServer) (authzlib.AccessChecker, error) { @@ -108,25 +110,20 @@ func newInProcLegacyClient(server *legacyServer) (authzlib.AccessChecker, error) ) } -func newGrpcLegacyClient(address string) (authzlib.AccessChecker, error) { +func newGrpcLegacyClient(authCfg *Cfg) (authzlib.AccessChecker, error) { // This client interceptor is a noop, as we don't send an access token - grpcClientConfig := authnlib.GrpcClientConfig{} - clientInterceptor, err := authnlib.NewGrpcClientInterceptor(&grpcClientConfig, - authnlib.WithDisableAccessTokenOption(), - ) + clientConfig := authnlib.GrpcClientConfig{} + clientInterceptor, err := authnlib.NewGrpcClientInterceptor(&clientConfig, authnlib.WithDisableAccessTokenOption()) if err != nil { return nil, err } - cfg := authzlib.ClientConfig{RemoteAddress: address} + cfg := authzlib.ClientConfig{RemoteAddress: authCfg.remoteAddress} client, err := authzlib.NewClient(&cfg, - // TODO(drclau): make this configurable (e.g. allow to use insecure connections) authzlib.WithGrpcDialOptionsClientOption( - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor), - grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor), + getDialOpts(clientInterceptor, authCfg.allowInsecure)..., ), - // TODO(drclau): remove this once we have access token support on-prem + // TODO: remove this once access tokens are supported on-prem authzlib.WithDisableAccessTokenClientOption(), ) if err != nil { @@ -155,11 +152,8 @@ func newCloudLegacyClient(authCfg *Cfg) (authzlib.AccessChecker, error) { clientCfg := authzlib.ClientConfig{RemoteAddress: authCfg.remoteAddress} client, err := authzlib.NewClient(&clientCfg, - // TODO(drclau): make this configurable (e.g. allow to use insecure connections) authzlib.WithGrpcDialOptionsClientOption( - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor), - grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor), + getDialOpts(clientInterceptor, authCfg.allowInsecure)..., ), ) if err != nil { @@ -168,3 +162,16 @@ func newCloudLegacyClient(authCfg *Cfg) (authzlib.AccessChecker, error) { return client, nil } + +func getDialOpts(interceptor *authnlib.GrpcClientInterceptor, allowInsecure bool) []grpc.DialOption { + dialOpts := []grpc.DialOption{ + grpc.WithUnaryInterceptor(interceptor.UnaryClientInterceptor), + grpc.WithStreamInterceptor(interceptor.StreamClientInterceptor), + } + if allowInsecure { + // allow insecure connections in development mode to facilitate testing + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + return dialOpts +} diff --git a/pkg/services/authz/config.go b/pkg/services/authz/config.go index c8e8c00a99e..76edd381608 100644 --- a/pkg/services/authz/config.go +++ b/pkg/services/authz/config.go @@ -10,7 +10,7 @@ type Mode string func (s Mode) IsValid() bool { switch s { - case ModeGRPC, ModeInProc, ModeCloud: + case ModeGRPC, ModeInProc: return true } return false @@ -19,7 +19,6 @@ func (s Mode) IsValid() bool { const ( ModeGRPC Mode = "grpc" ModeInProc Mode = "inproc" - ModeCloud Mode = "cloud" ) type Cfg struct { @@ -30,30 +29,37 @@ type Cfg struct { token string tokenExchangeURL string tokenNamespace string + + allowInsecure bool } func ReadCfg(cfg *setting.Cfg) (*Cfg, error) { - section := cfg.SectionWithEnvOverrides("authorization") + authzSection := cfg.SectionWithEnvOverrides("authorization") + grpcClientAuthSection := cfg.SectionWithEnvOverrides("grpc_client_authentication") - mode := Mode(section.Key("mode").MustString(string(ModeInProc))) + mode := Mode(authzSection.Key("mode").MustString(string(ModeInProc))) if !mode.IsValid() { return nil, fmt.Errorf("authorization: invalid mode %q", mode) } - token := section.Key("token").MustString("") - tokenExchangeURL := section.Key("token_exchange_url").MustString("") - tokenNamespace := section.Key("token_namespace").MustString("stack-" + cfg.StackID) + token := grpcClientAuthSection.Key("token").MustString("") + tokenExchangeURL := grpcClientAuthSection.Key("token_exchange_url").MustString("") + tokenNamespace := grpcClientAuthSection.Key("token_namespace").MustString("stacks-" + cfg.StackID) - if mode == ModeCloud && token == "" && tokenExchangeURL == "" { - return nil, fmt.Errorf("authorization: missing token or tokenExchangeUrl") + // When running in cloud mode, the token and tokenExchangeURL are required. + if mode == ModeGRPC && cfg.StackID != "" { + if token == "" || tokenExchangeURL == "" { + return nil, fmt.Errorf("authorization: missing token or tokenExchangeUrl") + } } return &Cfg{ - remoteAddress: section.Key("remote_address").MustString(""), - listen: section.Key("listen").MustBool(false), + remoteAddress: authzSection.Key("remote_address").MustString(""), + listen: authzSection.Key("listen").MustBool(false), mode: mode, token: token, tokenExchangeURL: tokenExchangeURL, tokenNamespace: tokenNamespace, + allowInsecure: cfg.Env == setting.Dev, }, nil } diff --git a/pkg/services/authz/server.go b/pkg/services/authz/server.go index 1fc7fe51f08..d345021f34e 100644 --- a/pkg/services/authz/server.go +++ b/pkg/services/authz/server.go @@ -4,7 +4,9 @@ import ( "context" "errors" + "github.com/grafana/authlib/authz" authzv1 "github.com/grafana/authlib/authz/proto/v1" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" @@ -14,6 +16,8 @@ import ( ) var _ authzv1.AuthzServiceServer = (*legacyServer)(nil) +var _ grpc_auth.ServiceAuthFuncOverride = (*legacyServer)(nil) +var _ authz.ServiceAuthorizeFuncOverride = (*legacyServer)(nil) func newLegacyServer( acSvc accesscontrol.Service, features featuremgmt.FeatureToggles, @@ -23,17 +27,21 @@ func newLegacyServer( return nil, nil } - s := &legacyServer{ + l := &legacyServer{ acSvc: acSvc, logger: log.New("authz-grpc-server"), tracer: tracer, } if cfg.listen { - grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, s) + if !cfg.allowInsecure { + l.logger.Error("Not allowing the authz service to run in insecure mode as Auth is skipped") + } else { + grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, l) + } } - return s, nil + return l, nil } type legacyServer struct { @@ -44,6 +52,24 @@ type legacyServer struct { tracer tracing.Tracer } +// AuthFuncOverride is a function that allows to override the default auth function. +// This is ok for now since we don't have on-prem access token support. +func (l *legacyServer) AuthFuncOverride(ctx context.Context, _ string) (context.Context, error) { + ctx, span := l.tracer.Start(ctx, "authz.AuthFuncOverride") + defer span.End() + + return ctx, nil +} + +// AuthorizeFuncOverride is a function that allows to override the default authorize function that checks the namespace of the caller. +// This is ok for now since we don't have on-prem access token support. +func (l *legacyServer) AuthorizeFuncOverride(ctx context.Context) error { + _, span := l.tracer.Start(ctx, "authz.AuthorizeFuncOverride") + defer span.End() + + return nil +} + func (l *legacyServer) Check(context.Context, *authzv1.CheckRequest) (*authzv1.CheckResponse, error) { // FIXME: implement for legacy access control return nil, errors.New("unimplemented")