Authn: use authenticator for grpc (#99573)

* Remove usage of grpc-authenticator

* Cleanup client construction code
This commit is contained in:
Karl Persson
2025-02-17 10:58:59 +01:00
committed by GitHub
parent ea788975e0
commit 6eeb28e312
8 changed files with 69 additions and 140 deletions
+1 -1
View File
@@ -133,7 +133,7 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi
Namespace: o.GrpcClientAuthenticationTokenNamespace,
},
}
unified, err := resource.NewCloudResourceClient(tracer, conn, authCfg, o.GrpcClientAuthenticationAllowInsecure)
unified, err := resource.NewRemoteResourceClient(tracer, conn, authCfg, o.GrpcClientAuthenticationAllowInsecure)
if err != nil {
return err
}
+2 -26
View File
@@ -1,32 +1,14 @@
package grpcutils
import (
"fmt"
"github.com/spf13/pflag"
"github.com/grafana/grafana/pkg/setting"
)
type Mode string
func (s Mode) IsValid() bool {
switch s {
case ModeOnPrem, ModeCloud:
return true
}
return false
}
const (
ModeOnPrem Mode = "on-prem"
ModeCloud Mode = "cloud"
)
type GrpcServerConfig struct {
SigningKeysURL string
AllowedAudiences []string
Mode Mode
LegacyFallback bool
AllowInsecure bool
}
@@ -35,21 +17,15 @@ func (c *GrpcServerConfig) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.SigningKeysURL, "grpc-server-authentication.signing-keys-url", "", "gRPC server authentication signing keys URL")
}
func ReadGrpcServerConfig(cfg *setting.Cfg) (*GrpcServerConfig, error) {
func ReadGrpcServerConfig(cfg *setting.Cfg) *GrpcServerConfig {
section := cfg.SectionWithEnvOverrides("grpc_server_authentication")
mode := Mode(section.Key("mode").MustString(string(ModeOnPrem)))
if !mode.IsValid() {
return nil, fmt.Errorf("grpc_server_authentication: invalid mode %q", mode)
}
return &GrpcServerConfig{
SigningKeysURL: section.Key("signing_keys_url").MustString(""),
AllowedAudiences: section.Key("allowed_audiences").Strings(","),
Mode: mode,
LegacyFallback: section.Key("legacy_fallback").MustBool(true),
AllowInsecure: cfg.Env == setting.Dev,
}, nil
}
}
type GrpcClientConfig struct {
@@ -19,15 +19,54 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
var once sync.Once
func NewInProcGrpcAuthenticator() interceptors.Authenticator {
return newAuthenticator(
authn.NewDefaultAuthenticator(
authn.NewUnsafeAccessTokenVerifier(authn.VerifierConfig{}),
authn.NewUnsafeIDTokenVerifier(authn.VerifierConfig{}),
),
tracing.NewNoopTracerService(),
)
}
func NewAuthenticator(cfg *GrpcServerConfig, tracer tracing.Tracer) interceptors.Authenticator {
client := http.DefaultClient
if cfg.AllowInsecure {
client = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
}
kr := authn.NewKeyRetriever(authn.KeyRetrieverConfig{
SigningKeysURL: cfg.SigningKeysURL,
}, authn.WithHTTPClientKeyRetrieverOpt(client))
auth := authn.NewDefaultAuthenticator(
authn.NewUnsafeAccessTokenVerifier(authn.VerifierConfig{}),
authn.NewUnsafeIDTokenVerifier(authn.VerifierConfig{}),
authn.NewAccessTokenVerifier(authn.VerifierConfig{AllowedAudiences: cfg.AllowedAudiences}, kr),
authn.NewIDTokenVerifier(authn.VerifierConfig{}, kr),
)
return newAuthenticator(auth, tracer)
}
func NewAuthenticatorWithFallback(cfg *setting.Cfg, reg prometheus.Registerer, tracer tracing.Tracer, fallback interceptors.Authenticator) interceptors.Authenticator {
authCfg := ReadGrpcServerConfig(cfg)
authenticator := NewAuthenticator(authCfg, tracer)
if !authCfg.LegacyFallback {
return authenticator
}
return &authenticatorWithFallback{
authenticator: authenticator,
fallback: fallback,
tracer: tracer,
metrics: newMetrics(reg),
}
}
func newAuthenticator(auth authn.Authenticator, tracer tracing.Tracer) interceptors.Authenticator {
return interceptors.AuthenticatorFunc(func(ctx context.Context) (context.Context, error) {
ctx, span := tracer.Start(ctx, "grpcutils.Authenticate")
defer span.End()
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, errors.New("missing metedata in context")
@@ -35,85 +74,31 @@ func NewInProcGrpcAuthenticator() interceptors.Authenticator {
info, err := auth.Authenticate(ctx, authn.NewGRPCTokenProvider(md))
if err != nil {
span.RecordError(err)
return ctx, err
}
// FIXME: Add attribute with service subject once https://github.com/grafana/authlib/issues/139 is closed.
span.SetAttributes(attribute.String("subject", info.GetUID()))
span.SetAttributes(attribute.Bool("service", types.IsIdentityType(info.GetIdentityType(), types.TypeAccessPolicy)))
return types.WithAuthInfo(ctx, info), nil
})
}
func NewGrpcAuthenticator(authCfg *GrpcServerConfig, tracer tracing.Tracer) (*authn.GrpcAuthenticator, error) {
grpcAuthCfg := authn.GrpcAuthenticatorConfig{
KeyRetrieverConfig: authn.KeyRetrieverConfig{
SigningKeysURL: authCfg.SigningKeysURL,
},
VerifierConfig: authn.VerifierConfig{
AllowedAudiences: authCfg.AllowedAudiences,
},
}
client := http.DefaultClient
if authCfg.AllowInsecure {
// allow insecure connections in development mode to facilitate testing
client = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
}
keyRetriever := authn.NewKeyRetriever(grpcAuthCfg.KeyRetrieverConfig, authn.WithHTTPClientKeyRetrieverOpt(client))
grpcOpts := []authn.GrpcAuthenticatorOption{
authn.WithKeyRetrieverOption(keyRetriever),
authn.WithTracerAuthOption(tracer),
authn.WithIDTokenAuthOption(false),
}
if authCfg.Mode == ModeOnPrem {
grpcOpts = append(grpcOpts,
// Access token are not yet available on-prem
authn.WithDisableAccessTokenAuthOption(),
)
}
return authn.NewGrpcAuthenticator(
&grpcAuthCfg,
grpcOpts...,
)
}
type contextFallbackKey struct{}
type AuthenticatorWithFallback struct {
authenticator *authn.GrpcAuthenticator
type authenticatorWithFallback struct {
authenticator interceptors.Authenticator
fallback interceptors.Authenticator
metrics *metrics
tracer tracing.Tracer
}
func NewGrpcAuthenticatorWithFallback(cfg *setting.Cfg, reg prometheus.Registerer, tracer tracing.Tracer, fallback interceptors.Authenticator) (interceptors.Authenticator, error) {
authCfg, err := ReadGrpcServerConfig(cfg)
if err != nil {
return nil, err
}
authenticator, err := NewGrpcAuthenticator(authCfg, tracer)
if err != nil {
return nil, err
}
if !authCfg.LegacyFallback {
return authenticator, nil
}
return &AuthenticatorWithFallback{
authenticator: authenticator,
fallback: fallback,
metrics: newMetrics(reg),
tracer: tracer,
}, nil
}
type contextFallbackKey struct{}
func FallbackUsed(ctx context.Context) bool {
return ctx.Value(contextFallbackKey{}) != nil
}
func (f *AuthenticatorWithFallback) Authenticate(ctx context.Context) (context.Context, error) {
func (f *authenticatorWithFallback) Authenticate(ctx context.Context) (context.Context, error) {
ctx, span := f.tracer.Start(ctx, "grpcutils.AuthenticatorWithFallback.Authenticate")
defer span.End()
@@ -145,6 +130,8 @@ type metrics struct {
requestsTotal *prometheus.CounterVec
}
var once sync.Once
func newMetrics(reg prometheus.Registerer) *metrics {
m := &metrics{
requestsTotal: prometheus.NewCounterVec(