AuthZ: GRPC client init and config options (#89161)
This commit is contained in:
@@ -1,3 +1,51 @@
|
|||||||
# Authorization
|
# Authorization
|
||||||
|
|
||||||
This package contains the authorization server implementation.
|
This package contains the authorization server implementation.
|
||||||
|
|
||||||
|
## Feature toggles
|
||||||
|
|
||||||
|
The following feature toggles need to be activated:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[feature_toggles]
|
||||||
|
authZGRPCServer = true
|
||||||
|
grpcServer = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
To configure the authorization server and client, use the "authorization" section of the configuration ini file.
|
||||||
|
|
||||||
|
The `remote_address` setting, specifies the address where the authorization server is located (ex: `server.example.org:10000`).
|
||||||
|
|
||||||
|
The `mode` setting can be set to either `grpc` or `inproc`. When set to `grpc`, the client will connect to the specified address. When set to `inproc` the client will use inprocgrpc (relying on go channels) to wrap a local instantiation of the server.
|
||||||
|
|
||||||
|
The `listen` setting determines whether the authorization server should listen for incoming requests. When set to `true`, the authorization service will be registered to the Grafana GRPC server.
|
||||||
|
|
||||||
|
The default configuration does not register the authorization service on the Grafana GRPC server and binds the client to it `inproc`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[authorization]
|
||||||
|
remote_address = ""
|
||||||
|
listen = false
|
||||||
|
mode = "inproc"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Here is an example to connect the authorization client to a remote grpc server.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[authorization]
|
||||||
|
remote_address = "server.example.org:10000"
|
||||||
|
mode = "grpc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example to register the authorization service on the Grafana GRPC server and connect the client to it through grpc
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[authorization]
|
||||||
|
remote_address = "localhost:10000"
|
||||||
|
listen = true
|
||||||
|
mode = "grpc"
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
package authz
|
package authz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/fullstorydev/grpchan"
|
||||||
|
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||||
|
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||||
|
grpcUtils "github.com/grafana/grafana/pkg/services/store/entity/grpc"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,8 +24,10 @@ type Client interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LegacyClient struct {
|
type LegacyClient struct {
|
||||||
|
clientV1 authzv1.AuthzServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProvideAuthZClient provides an AuthZ client and creates the AuthZ service.
|
||||||
func ProvideAuthZClient(
|
func ProvideAuthZClient(
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, acSvc accesscontrol.Service,
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, acSvc accesscontrol.Service,
|
||||||
grpcServer grpcserver.Provider, tracer tracing.Tracer,
|
grpcServer grpcserver.Provider, tracer tracing.Tracer,
|
||||||
@@ -23,11 +36,90 @@ func ProvideAuthZClient(
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := newLegacyServer(acSvc, features, grpcServer, tracer)
|
authCfg, err := ReadCfg(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO differentiate run local from run remote grpc
|
var client *LegacyClient
|
||||||
return &LegacyClient{}, nil
|
|
||||||
|
// Register the server
|
||||||
|
server, err := newLegacyServer(acSvc, features, grpcServer, tracer, authCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch authCfg.mode {
|
||||||
|
case ModeInProc:
|
||||||
|
client = newInProcLegacyClient(server)
|
||||||
|
case ModeGRPC:
|
||||||
|
client, err = newGrpcLegacyClient(authCfg.remoteAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideStandaloneAuthZClient provides a standalone AuthZ client, without registering the AuthZ service.
|
||||||
|
// You need to provide a remote address in the configuration
|
||||||
|
func ProvideStandaloneAuthZClient(
|
||||||
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer,
|
||||||
|
) (Client, error) {
|
||||||
|
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
authCfg, err := ReadCfg(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGrpcLegacyClient(authCfg.remoteAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInProcLegacyClient(server *legacyServer) *LegacyClient {
|
||||||
|
channel := &inprocgrpc.Channel{}
|
||||||
|
|
||||||
|
// TODO (gamab): change this once it's clear how to authenticate the client
|
||||||
|
// Choices are:
|
||||||
|
// - noAuth given it's in proc and we don't need the user
|
||||||
|
// - access_token verif only as it's consistent with when it's remote (we check the service is allowed to call the authz service)
|
||||||
|
// - access_token and id_token ? the id_token being only necessary when the user is trying to access the service straight away
|
||||||
|
// auth := grpcUtils.ProvideAuthenticator(cfg)
|
||||||
|
noAuth := func(ctx context.Context) (context.Context, error) {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.RegisterService(
|
||||||
|
grpchan.InterceptServer(
|
||||||
|
&authzv1.AuthzService_ServiceDesc,
|
||||||
|
grpcAuth.UnaryServerInterceptor(noAuth),
|
||||||
|
grpcAuth.StreamServerInterceptor(noAuth),
|
||||||
|
),
|
||||||
|
server,
|
||||||
|
)
|
||||||
|
|
||||||
|
conn := grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)
|
||||||
|
|
||||||
|
client := authzv1.NewAuthzServiceClient(conn)
|
||||||
|
|
||||||
|
return &LegacyClient{
|
||||||
|
clientV1: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGrpcLegacyClient(address string) (*LegacyClient, error) {
|
||||||
|
// Create a connection to the gRPC server
|
||||||
|
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := authzv1.NewAuthzServiceClient(conn)
|
||||||
|
|
||||||
|
return &LegacyClient{
|
||||||
|
clientV1: client,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package authz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mode string
|
||||||
|
|
||||||
|
func (s Mode) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case ModeGRPC, ModeInProc:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModeGRPC Mode = "grpc"
|
||||||
|
ModeInProc Mode = "inproc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cfg struct {
|
||||||
|
remoteAddress string
|
||||||
|
listen bool
|
||||||
|
mode Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadCfg(cfg *setting.Cfg) (*Cfg, error) {
|
||||||
|
section := cfg.SectionWithEnvOverrides("authorization")
|
||||||
|
|
||||||
|
mode := Mode(section.Key("mode").MustString(string(ModeInProc)))
|
||||||
|
if !mode.IsValid() {
|
||||||
|
return nil, fmt.Errorf("authorization: invalid mode %q", mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Cfg{
|
||||||
|
remoteAddress: section.Key("remote_address").MustString(""),
|
||||||
|
listen: section.Key("listen").MustBool(false),
|
||||||
|
mode: mode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ type legacyServer struct {
|
|||||||
|
|
||||||
func newLegacyServer(
|
func newLegacyServer(
|
||||||
acSvc accesscontrol.Service, features featuremgmt.FeatureToggles,
|
acSvc accesscontrol.Service, features featuremgmt.FeatureToggles,
|
||||||
grpcServer grpcserver.Provider, tracer tracing.Tracer,
|
grpcServer grpcserver.Provider, tracer tracing.Tracer, cfg *Cfg,
|
||||||
) (*legacyServer, error) {
|
) (*legacyServer, error) {
|
||||||
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
|
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -36,7 +36,9 @@ func newLegacyServer(
|
|||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, s)
|
if cfg.listen {
|
||||||
|
grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, s)
|
||||||
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user