mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
authn: client side updates
Co-Authored-By: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
@@ -288,7 +288,10 @@ func (s *service) start(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a client instance
|
// Create a client instance
|
||||||
client := resource.NewResourceStoreClientGRPC(conn)
|
client, err := resource.NewResourceStoreClientGRPC(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(client, o.RecommendedOptions.Etcd.StorageConfig)
|
serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(client, o.RecommendedOptions.Etcd.StorageConfig)
|
||||||
|
|
||||||
case grafanaapiserveroptions.StorageTypeLegacy:
|
case grafanaapiserveroptions.StorageTypeLegacy:
|
||||||
|
|||||||
21
pkg/services/authn/grpcutils/config.go
Normal file
21
pkg/services/authn/grpcutils/config.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package grpcutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GrpcClientConfig struct {
|
||||||
|
Token string
|
||||||
|
TokenExchangeURL string
|
||||||
|
TokenNamespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadCfg(cfg *setting.Cfg) *GrpcClientConfig {
|
||||||
|
section := cfg.SectionWithEnvOverrides("grpc_client_authentication")
|
||||||
|
|
||||||
|
return &GrpcClientConfig{
|
||||||
|
Token: section.Key("token").MustString(""),
|
||||||
|
TokenExchangeURL: section.Key("token_exchange_url").MustString(""),
|
||||||
|
TokenNamespace: section.Key("token_namespace").MustString("stack-" + cfg.StackID),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,29 @@
|
|||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/fullstorydev/grpchan"
|
"github.com/fullstorydev/grpchan"
|
||||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||||
|
authnlib "github.com/grafana/authlib/authn"
|
||||||
|
authzlib "github.com/grafana/authlib/authz"
|
||||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: decide on the audience for the resource store
|
||||||
|
const resourceStoreAudience = "resourceStore"
|
||||||
|
|
||||||
func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient {
|
func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient {
|
||||||
|
// scenario: local in-proc
|
||||||
channel := &inprocgrpc.Channel{}
|
channel := &inprocgrpc.Channel{}
|
||||||
|
|
||||||
auth := &grpcUtils.Authenticator{}
|
auth := &grpcUtils.InProcAuthenticator{}
|
||||||
|
|
||||||
channel.RegisterService(
|
channel.RegisterService(
|
||||||
grpchan.InterceptServer(
|
grpchan.InterceptServer(
|
||||||
@@ -25,6 +36,72 @@ func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient
|
|||||||
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor))
|
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResourceStoreClientGRPC(channel *grpc.ClientConn) ResourceStoreClient {
|
func NewResourceStoreClientGRPC(conn *grpc.ClientConn) (ResourceStoreClient, error) {
|
||||||
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor))
|
// scenario: remote on-prem
|
||||||
|
clientInterceptor, err := authnlib.NewGrpcClientInterceptor(
|
||||||
|
&authnlib.GrpcClientConfig{},
|
||||||
|
authnlib.WithDisableAccessTokenOption(),
|
||||||
|
authnlib.WithIDTokenExtractorOption(idTokenExtractor),
|
||||||
|
authnlib.WithMetadataExtractorOption(orgIdExtractor),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResourceStoreClient(
|
||||||
|
grpchan.InterceptClientConn(
|
||||||
|
conn,
|
||||||
|
clientInterceptor.UnaryClientInterceptor,
|
||||||
|
clientInterceptor.StreamClientInterceptor,
|
||||||
|
)),
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResourceStoreClientCloud(conn *grpc.ClientConn, cfg *grpcutils.GrpcClientConfig) (ResourceStoreClient, error) {
|
||||||
|
// scenario: remote cloud
|
||||||
|
grpcClientConfig := authnlib.GrpcClientConfig{
|
||||||
|
TokenClientConfig: &authnlib.TokenExchangeConfig{
|
||||||
|
Token: cfg.Token,
|
||||||
|
TokenExchangeURL: cfg.TokenExchangeURL,
|
||||||
|
},
|
||||||
|
TokenRequest: &authnlib.TokenExchangeRequest{
|
||||||
|
Namespace: cfg.TokenNamespace,
|
||||||
|
Audiences: []string{resourceStoreAudience},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientInterceptor, err := authnlib.NewGrpcClientInterceptor(
|
||||||
|
&grpcClientConfig,
|
||||||
|
authnlib.WithIDTokenExtractorOption(idTokenExtractor),
|
||||||
|
authnlib.WithMetadataExtractorOption(orgIdExtractor),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResourceStoreClient(
|
||||||
|
grpchan.InterceptClientConn(
|
||||||
|
conn,
|
||||||
|
clientInterceptor.UnaryClientInterceptor,
|
||||||
|
clientInterceptor.StreamClientInterceptor,
|
||||||
|
)),
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func idTokenExtractor(ctx context.Context) (string, error) {
|
||||||
|
requester, err := identity.GetRequester(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return requester.GetIDToken(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func orgIdExtractor(ctx context.Context) (key string, values []string, err error) {
|
||||||
|
requester, err := identity.GetRequester(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return authzlib.DefaultStackIDMetadataKey, []string{fmt.Sprintf("%d", requester.GetOrgID())}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
27
pkg/storage/unified/resource/grpc/inproc-authenticator.go
Normal file
27
pkg/storage/unified/resource/grpc/inproc-authenticator.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
authnlib "github.com/grafana/authlib/authn"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InProcAuthenticator struct{}
|
||||||
|
|
||||||
|
func (f *InProcAuthenticator) Authenticate(ctx context.Context) (context.Context, error) {
|
||||||
|
r, err := identity.GetRequester(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idClaims := r.GetIDClaims()
|
||||||
|
if idClaims == nil {
|
||||||
|
return nil, fmt.Errorf("no id claims found")
|
||||||
|
}
|
||||||
|
|
||||||
|
callerAuthInfo := authnlib.CallerAuthInfo{IDTokenClaims: idClaims}
|
||||||
|
return authnlib.AddCallerAuthInfoToContext(ctx, callerAuthInfo), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user