authn: client side updates

Co-Authored-By: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
Claudiu Dragalina-Paraipan 2024-08-01 18:32:19 +03:00
parent 2e2ddc5c42
commit 92aba937a9
4 changed files with 132 additions and 4 deletions

View File

@ -288,7 +288,10 @@ func (s *service) start(ctx context.Context) error {
}
// 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)
case grafanaapiserveroptions.StorageTypeLegacy:

View 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),
}
}

View File

@ -1,18 +1,29 @@
package resource
import (
"context"
"fmt"
"github.com/fullstorydev/grpchan"
"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"
"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"
)
// TODO: decide on the audience for the resource store
const resourceStoreAudience = "resourceStore"
func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient {
// scenario: local in-proc
channel := &inprocgrpc.Channel{}
auth := &grpcUtils.Authenticator{}
auth := &grpcUtils.InProcAuthenticator{}
channel.RegisterService(
grpchan.InterceptServer(
@ -25,6 +36,72 @@ func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor))
}
func NewResourceStoreClientGRPC(channel *grpc.ClientConn) ResourceStoreClient {
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor))
func NewResourceStoreClientGRPC(conn *grpc.ClientConn) (ResourceStoreClient, error) {
// 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
}

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