AuthZ: Refactor authentication modes for the Authz package (#95120)

* AuthZ: Fix authentication modes for the Authz package

Co-Authored-By: Claudiu Dragalina-Paraipan <drclau@users.noreply.github.com>
This commit is contained in:
Gabriel MABILLE 2024-10-22 13:38:59 +02:00 committed by GitHub
parent 229658ad83
commit 0704ae734f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 97 additions and 39 deletions

View File

@ -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"
```

View File

@ -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
}

View File

@ -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
}

View File

@ -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")