mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 02:02:33 +08:00
Use authlib repo. Use otel (#103178)
* Use authlib repo. Use otel * Use interceptors on the provider level * Create a new wire set with otel * Lint * Fix test * make update-workflow * make update-workspace * make update-workspace. Try to add authlib as enterprise imports * make update-workspace
This commit is contained in:
2
go.mod
2
go.mod
@ -85,7 +85,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6 // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa // @grafana/identity-access-team
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
|
||||
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics
|
||||
|
4
go.sum
4
go.sum
@ -1577,8 +1577,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6 h1:XJO4BNSwMFvJO5k0GaqQ5OBc8BEHqhBYCoVASrqB/GY=
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa h1:08Wh/svkv8WpDuOBBKAzSPa14gKjYLZvQJsHWXLjPuc=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d h1:TDVZemfYeJHPyXeYCnqL7BQqsa+mpaZYth/Qm3TKaT8=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA=
|
||||
|
@ -1089,8 +1089,10 @@ github.com/grafana/alerting v0.0.0-20250310104713-16b885f1c79e/go.mod h1:HfvjmU3
|
||||
github.com/grafana/alerting v0.0.0-20250403153742-418bc7118d05 h1:hMzOzI/S0nkZt0nUqpfAa4Rdb+YL8z8oG3pl4Jb31h8=
|
||||
github.com/grafana/alerting v0.0.0-20250403153742-418bc7118d05/go.mod h1:K3YAJumchx5EEZItGv4D3pCv/Ux796hmoOibP/p/eYk=
|
||||
github.com/grafana/authlib v0.0.0-20250123104008-e99947858901/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120144156-d6737a7dc8f5/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250314102521-a77865c746c0/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2 h1:qhugDMdQ4Vp68H0tp/0iN17DM2ehRo1rLEdOFe/gB8I=
|
||||
github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2/go.mod h1:w/aiO1POVIeXUQyl0VQSZjl5OAGDTL5aX+4v0RA1tcw=
|
||||
github.com/grafana/cog v0.0.23 h1:/0CCJ24Z8XXM2DnboSd2FzoIswUroqIZzVr8oJWmMQs=
|
||||
@ -1112,6 +1114,7 @@ github.com/grafana/grafana/pkg/build v0.0.0-20250227105625-8f465f124924/go.mod h
|
||||
github.com/grafana/grafana/pkg/build v0.0.0-20250227163402-d78c646f93bb/go.mod h1:Vw0LdoMma64VgIMVpRY3i0D156jddgUGjTQBOcyeF3k=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c=
|
||||
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg=
|
||||
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250317130411-3f270d1de043/go.mod h1:usON2sfgh4qjGs4GLhH6+PL7Q6g5ezOP6M/9vOeHpAM=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3 h1:6D2gGAwyQBElSrp3E+9lSr7k8gLuP3Aiy20rweLWeBw=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3/go.mod h1:YeND+6FDA7OuFgDzYODN8kfPhXLCehcpxe4T9mdnpCY=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250403152731-b0a305952f0c h1:KH22wMjV61ysp8igdHpuP3zJYsaaIL/GJj4y67Vr/7M=
|
||||
@ -1855,6 +1858,7 @@ golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/pkg/apimachinery
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa // @grafana/identity-access-team
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
|
||||
github.com/stretchr/testify v1.10.0
|
||||
k8s.io/apimachinery v0.32.3
|
||||
|
@ -32,8 +32,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa h1:08Wh/svkv8WpDuOBBKAzSPa14gKjYLZvQJsHWXLjPuc=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d h1:TDVZemfYeJHPyXeYCnqL7BQqsa+mpaZYth/Qm3TKaT8=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
|
@ -32,6 +32,11 @@ import (
|
||||
_ "golang.org/x/time/rate"
|
||||
_ "xorm.io/builder"
|
||||
|
||||
_ "github.com/grafana/authlib/authn"
|
||||
_ "github.com/grafana/authlib/authz"
|
||||
_ "github.com/grafana/authlib/cache"
|
||||
_ "github.com/grafana/authlib/grpcutils"
|
||||
_ "github.com/grafana/authlib/types"
|
||||
_ "github.com/grafana/dskit/backoff"
|
||||
_ "github.com/grafana/dskit/flagext"
|
||||
_ "github.com/grafana/e2e"
|
||||
|
@ -9,6 +9,8 @@ package server
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
|
||||
@ -185,6 +187,16 @@ import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/zipkin"
|
||||
)
|
||||
|
||||
func otelTracer() trace.Tracer {
|
||||
return otel.GetTracerProvider().Tracer("grafana")
|
||||
}
|
||||
|
||||
var withOTelSet = wire.NewSet(
|
||||
otelTracer,
|
||||
grpcserver.ProvideService,
|
||||
interceptors.ProvideAuthenticator,
|
||||
)
|
||||
|
||||
var wireBasicSet = wire.NewSet(
|
||||
annotationsimpl.ProvideService,
|
||||
wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)),
|
||||
@ -265,6 +277,7 @@ var wireBasicSet = wire.NewSet(
|
||||
tracing.ProvideService,
|
||||
tracing.ProvideTracingConfig,
|
||||
wire.Bind(new(tracing.Tracer), new(*tracing.TracingService)),
|
||||
withOTelSet,
|
||||
testdatasource.ProvideService,
|
||||
ldapapi.ProvideService,
|
||||
opentsdb.ProvideService,
|
||||
@ -355,10 +368,8 @@ var wireBasicSet = wire.NewSet(
|
||||
orgimpl.ProvideDeletionService,
|
||||
statsimpl.ProvideService,
|
||||
grpccontext.ProvideContextHandler,
|
||||
grpcserver.ProvideService,
|
||||
grpcserver.ProvideHealthService,
|
||||
grpcserver.ProvideReflectionService,
|
||||
interceptors.ProvideAuthenticator,
|
||||
resolver.ProvideEntityReferenceResolver,
|
||||
teamimpl.ProvideService,
|
||||
teamapi.ProvideTeamAPI,
|
||||
|
@ -1,33 +1,9 @@
|
||||
package grpcutils
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type GrpcServerConfig struct {
|
||||
SigningKeysURL string
|
||||
AllowedAudiences []string
|
||||
LegacyFallback bool
|
||||
AllowInsecure bool
|
||||
}
|
||||
|
||||
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 {
|
||||
section := cfg.SectionWithEnvOverrides("grpc_server_authentication")
|
||||
|
||||
return &GrpcServerConfig{
|
||||
SigningKeysURL: section.Key("signing_keys_url").MustString(""),
|
||||
AllowedAudiences: section.Key("allowed_audiences").Strings(","),
|
||||
LegacyFallback: section.Key("legacy_fallback").MustBool(true),
|
||||
AllowInsecure: cfg.Env == setting.Dev,
|
||||
}
|
||||
}
|
||||
|
||||
type GrpcClientConfig struct {
|
||||
Token string
|
||||
TokenExchangeURL string
|
||||
|
@ -1,164 +0,0 @@
|
||||
package grpcutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func NewInProcGrpcAuthenticator() interceptors.Authenticator {
|
||||
return NewAuthenticatorInterceptor(
|
||||
authn.NewDefaultAuthenticator(
|
||||
authn.NewUnsafeAccessTokenVerifier(authn.VerifierConfig{}),
|
||||
authn.NewUnsafeIDTokenVerifier(authn.VerifierConfig{}),
|
||||
),
|
||||
noop.NewTracerProvider().Tracer(""),
|
||||
)
|
||||
}
|
||||
|
||||
func NewAuthenticator(cfg *GrpcServerConfig, tracer trace.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.NewAccessTokenVerifier(authn.VerifierConfig{AllowedAudiences: cfg.AllowedAudiences}, kr),
|
||||
authn.NewIDTokenVerifier(authn.VerifierConfig{}, kr),
|
||||
)
|
||||
|
||||
return NewAuthenticatorInterceptor(auth, tracer)
|
||||
}
|
||||
|
||||
func NewAuthenticatorWithFallback(cfg *setting.Cfg, reg prometheus.Registerer, tracer trace.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 NewAuthenticatorInterceptor(auth authn.Authenticator, tracer trace.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")
|
||||
}
|
||||
|
||||
info, err := auth.Authenticate(ctx, authn.NewGRPCTokenProvider(md))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
if authn.IsUnauthenticatedErr(err) {
|
||||
return nil, status.Error(codes.Unauthenticated, err.Error())
|
||||
}
|
||||
|
||||
return ctx, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
||||
|
||||
type authenticatorWithFallback struct {
|
||||
authenticator interceptors.Authenticator
|
||||
fallback interceptors.Authenticator
|
||||
metrics *metrics
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
type contextFallbackKey struct{}
|
||||
|
||||
func FallbackUsed(ctx context.Context) bool {
|
||||
return ctx.Value(contextFallbackKey{}) != nil
|
||||
}
|
||||
|
||||
func WithFallback(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, contextFallbackKey{}, true)
|
||||
}
|
||||
|
||||
func (f *authenticatorWithFallback) Authenticate(ctx context.Context) (context.Context, error) {
|
||||
ctx, span := f.tracer.Start(ctx, "grpcutils.AuthenticatorWithFallback.Authenticate")
|
||||
defer span.End()
|
||||
|
||||
// Try to authenticate with the new authenticator first
|
||||
span.SetAttributes(attribute.Bool("fallback_used", false))
|
||||
newCtx, err := f.authenticator.Authenticate(ctx)
|
||||
if err == nil {
|
||||
// fallback not used, authentication successful
|
||||
f.metrics.requestsTotal.WithLabelValues("false", "true").Inc()
|
||||
return newCtx, nil
|
||||
}
|
||||
|
||||
// In case of error, fallback to the legacy authenticator
|
||||
span.SetAttributes(attribute.Bool("fallback_used", true))
|
||||
newCtx, err = f.fallback.Authenticate(ctx)
|
||||
if newCtx != nil {
|
||||
newCtx = WithFallback(newCtx)
|
||||
}
|
||||
f.metrics.requestsTotal.WithLabelValues("true", fmt.Sprintf("%t", err == nil)).Inc()
|
||||
return newCtx, err
|
||||
}
|
||||
|
||||
const (
|
||||
metricsNamespace = "grafana"
|
||||
metricsSubSystem = "grpc_authenticator_with_fallback"
|
||||
)
|
||||
|
||||
type metrics struct {
|
||||
requestsTotal *prometheus.CounterVec
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func newMetrics(reg prometheus.Registerer) *metrics {
|
||||
m := &metrics{
|
||||
requestsTotal: prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: metricsSubSystem,
|
||||
Name: "requests_total",
|
||||
Help: "Number requests using the authenticator with fallback",
|
||||
}, []string{"fallback_used", "result"}),
|
||||
}
|
||||
|
||||
if reg != nil {
|
||||
once.Do(func() {
|
||||
reg.MustRegister(m.requestsTotal)
|
||||
})
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package grpcutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func ProvideInProcExchanger() authn.StaticTokenExchanger {
|
||||
token, err := createInProcToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return authn.NewStaticTokenExchanger(token)
|
||||
}
|
||||
|
||||
func createInProcToken() (string, error) {
|
||||
claims := authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: "grafana",
|
||||
Subject: types.NewTypeID(types.TypeAccessPolicy, "grafana"),
|
||||
Audience: []string{"resourceStore"},
|
||||
},
|
||||
Rest: authn.AccessTokenClaims{
|
||||
Namespace: "*",
|
||||
Permissions: identity.ServiceIdentityClaims.Rest.Permissions,
|
||||
DelegatedPermissions: identity.ServiceIdentityClaims.Rest.DelegatedPermissions,
|
||||
},
|
||||
}
|
||||
|
||||
header, err := json.Marshal(map[string]string{
|
||||
"alg": "none",
|
||||
"typ": authn.TokenTypeAccess,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(header) + "." + base64.RawURLEncoding.EncodeToString(payload) + ".", nil
|
||||
}
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@ -98,7 +99,7 @@ func ProvideAuthZClient(
|
||||
// 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,
|
||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer trace.Tracer,
|
||||
) (authlib.AccessClient, error) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
|
||||
return nil, nil
|
||||
@ -112,7 +113,7 @@ func ProvideStandaloneAuthZClient(
|
||||
return newRemoteRBACClient(authCfg, tracer)
|
||||
}
|
||||
|
||||
func newRemoteRBACClient(clientCfg *authzClientSettings, tracer tracing.Tracer) (authlib.AccessClient, error) {
|
||||
func newRemoteRBACClient(clientCfg *authzClientSettings, tracer trace.Tracer) (authlib.AccessClient, error) {
|
||||
tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
|
||||
Token: clientCfg.token,
|
||||
TokenExchangeURL: clientCfg.tokenExchangeURL,
|
||||
@ -143,7 +144,7 @@ func newRemoteRBACClient(clientCfg *authzClientSettings, tracer tracing.Tracer)
|
||||
return newRBACClient(conn, tracer), nil
|
||||
}
|
||||
|
||||
func newRBACClient(conn grpc.ClientConnInterface, tracer tracing.Tracer) authlib.AccessClient {
|
||||
func newRBACClient(conn grpc.ClientConnInterface, tracer trace.Tracer) authlib.AccessClient {
|
||||
return authzlib.NewClient(
|
||||
conn,
|
||||
authzlib.WithCacheClientOption(cache.NewLocalCache(cache.Config{
|
||||
|
@ -20,14 +20,15 @@ import (
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/services"
|
||||
|
||||
"github.com/grafana/authlib/grpcutils"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -190,7 +191,7 @@ func (z *Zanzana) start(ctx context.Context) error {
|
||||
z.handle, err = grpcserver.ProvideService(
|
||||
z.cfg,
|
||||
z.features,
|
||||
grpcutils.NewAuthenticatorInterceptor(authenticator, tracer),
|
||||
interceptors.AuthenticatorFunc(grpcutils.NewAuthenticatorInterceptor(authenticator, tracer)),
|
||||
tracer,
|
||||
prometheus.DefaultRegisterer,
|
||||
)
|
||||
|
@ -3,16 +3,16 @@ package interceptors
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const tracingPrefix = "gRPC Server "
|
||||
|
||||
func TracingStreamInterceptor(tracer tracing.Tracer) grpc.StreamServerInterceptor {
|
||||
func TracingStreamInterceptor(tracer trace.Tracer) grpc.StreamServerInterceptor {
|
||||
return func(srv any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
ctx := stream.Context()
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
|
@ -11,11 +11,11 @@ import (
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
@ -42,7 +42,7 @@ type gPRCServerService struct {
|
||||
startedChan chan struct{}
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, authenticator interceptors.Authenticator, tracer tracing.Tracer, registerer prometheus.Registerer) (Provider, error) {
|
||||
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, authenticator interceptors.Authenticator, tracer trace.Tracer, registerer prometheus.Registerer) (Provider, error) {
|
||||
s := &gPRCServerService{
|
||||
cfg: cfg.GRPCServer,
|
||||
logger: log.New("grpc-server"),
|
||||
|
@ -203,7 +203,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6 // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d // indirect
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // indirect
|
||||
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect
|
||||
github.com/grafana/grafana-app-sdk v0.35.1 // indirect
|
||||
|
@ -1251,8 +1251,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6 h1:XJO4BNSwMFvJO5k0GaqQ5OBc8BEHqhBYCoVASrqB/GY=
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa h1:08Wh/svkv8WpDuOBBKAzSPa14gKjYLZvQJsHWXLjPuc=
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d h1:TDVZemfYeJHPyXeYCnqL7BQqsa+mpaZYth/Qm3TKaT8=
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA=
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
)
|
||||
|
||||
type groupResource map[string]map[string]interface{}
|
||||
@ -117,11 +116,11 @@ func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req c
|
||||
attribute.String("name", req.Name),
|
||||
attribute.String("verb", req.Verb),
|
||||
attribute.String("folder", req.Folder),
|
||||
attribute.Bool("fallback_used", grpcutils.FallbackUsed(ctx)),
|
||||
attribute.Bool("fallback_used", FallbackUsed(ctx)),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
if grpcutils.FallbackUsed(ctx) {
|
||||
if FallbackUsed(ctx) {
|
||||
if req.Namespace == "" {
|
||||
// cross namespace queries are not allowed when fallback is used
|
||||
span.SetAttributes(attribute.Bool("allowed", false))
|
||||
@ -162,7 +161,7 @@ func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req c
|
||||
// Compile implements claims.AccessClient.
|
||||
func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req claims.ListRequest) (claims.ItemChecker, error) {
|
||||
t := time.Now()
|
||||
fallbackUsed := grpcutils.FallbackUsed(ctx)
|
||||
fallbackUsed := FallbackUsed(ctx)
|
||||
ctx, span := c.tracer.Start(ctx, "authzLimitedClient.Compile", trace.WithAttributes(
|
||||
attribute.String("group", req.Group),
|
||||
attribute.String("resource", req.Resource),
|
||||
@ -218,3 +217,13 @@ func (c authzLimitedClient) IsCompatibleWithRBAC(group, resource string) bool {
|
||||
}
|
||||
|
||||
var _ claims.AccessClient = &authzLimitedClient{}
|
||||
|
||||
type contextFallbackKey struct{}
|
||||
|
||||
func WithFallback(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, contextFallbackKey{}, true)
|
||||
}
|
||||
|
||||
func FallbackUsed(ctx context.Context) bool {
|
||||
return ctx.Value(contextFallbackKey{}) != nil
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
)
|
||||
|
||||
func TestAuthzLimitedClient_Check(t *testing.T) {
|
||||
@ -195,7 +194,7 @@ func TestNamespaceMatchingFallback(t *testing.T) {
|
||||
Verb: utils.VerbGet,
|
||||
Namespace: tt.reqNamespace,
|
||||
}
|
||||
ctx = grpcutils.WithFallback(ctx)
|
||||
ctx = WithFallback(ctx)
|
||||
// Create a mock auth info with the specified namespace
|
||||
// Test Check method
|
||||
user := &identity.StaticRequester{Namespace: tt.authNamespace}
|
||||
|
@ -3,22 +3,25 @@ package resource
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/grpcutils"
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
)
|
||||
|
||||
@ -56,8 +59,9 @@ func NewLegacyResourceClient(channel grpc.ClientConnInterface) ResourceClient {
|
||||
func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
// scenario: local in-proc
|
||||
channel := &inprocgrpc.Channel{}
|
||||
tracer := otel.Tracer("github.com/grafana/grafana/pkg/storage/unified/resource")
|
||||
|
||||
grpcAuthInt := grpcutils.NewInProcGrpcAuthenticator()
|
||||
grpcAuthInt := grpcutils.NewUnsafeAuthenticator(tracer)
|
||||
for _, desc := range []*grpc.ServiceDesc{
|
||||
&ResourceStore_ServiceDesc,
|
||||
&ResourceIndex_ServiceDesc,
|
||||
@ -69,15 +73,15 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
channel.RegisterService(
|
||||
grpchan.InterceptServer(
|
||||
desc,
|
||||
grpcAuth.UnaryServerInterceptor(grpcAuthInt.Authenticate),
|
||||
grpcAuth.StreamServerInterceptor(grpcAuthInt.Authenticate),
|
||||
grpcAuth.UnaryServerInterceptor(grpcAuthInt),
|
||||
grpcAuth.StreamServerInterceptor(grpcAuthInt),
|
||||
),
|
||||
server,
|
||||
)
|
||||
}
|
||||
|
||||
clientInt := authnlib.NewGrpcClientInterceptor(
|
||||
grpcutils.ProvideInProcExchanger(),
|
||||
ProvideInProcExchanger(),
|
||||
authnlib.WithClientInterceptorIDTokenExtractor(idTokenExtractor),
|
||||
)
|
||||
|
||||
@ -100,7 +104,7 @@ type RemoteResourceClientConfig struct {
|
||||
AllowInsecure bool
|
||||
}
|
||||
|
||||
func NewRemoteResourceClient(tracer tracing.Tracer, conn grpc.ClientConnInterface, cfg RemoteResourceClientConfig) (ResourceClient, error) {
|
||||
func NewRemoteResourceClient(tracer trace.Tracer, conn grpc.ClientConnInterface, cfg RemoteResourceClientConfig) (ResourceClient, error) {
|
||||
exchangeOpts := []authnlib.ExchangeClientOpts{}
|
||||
|
||||
if cfg.AllowInsecure {
|
||||
@ -160,3 +164,42 @@ func idTokenExtractor(ctx context.Context) (string, error) {
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func ProvideInProcExchanger() authnlib.StaticTokenExchanger {
|
||||
token, err := createInProcToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return authnlib.NewStaticTokenExchanger(token)
|
||||
}
|
||||
|
||||
func createInProcToken() (string, error) {
|
||||
claims := authnlib.Claims[authnlib.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: "grafana",
|
||||
Subject: types.NewTypeID(types.TypeAccessPolicy, "grafana"),
|
||||
Audience: []string{"resourceStore"},
|
||||
},
|
||||
Rest: authnlib.AccessTokenClaims{
|
||||
Namespace: "*",
|
||||
Permissions: identity.ServiceIdentityClaims.Rest.Permissions,
|
||||
DelegatedPermissions: identity.ServiceIdentityClaims.Rest.DelegatedPermissions,
|
||||
},
|
||||
}
|
||||
|
||||
header, err := json.Marshal(map[string]string{
|
||||
"alg": "none",
|
||||
"typ": authnlib.TokenTypeAccess,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(header) + "." + base64.RawURLEncoding.EncodeToString(payload) + ".", nil
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ replace (
|
||||
require (
|
||||
github.com/fullstorydev/grpchan v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa
|
||||
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d
|
||||
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040
|
||||
github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible
|
||||
@ -19,7 +19,6 @@ require (
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250317130411-3f270d1de043
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250401081501-6af5fbf3fff0
|
||||
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250325075903-77fa2271be7a // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
@ -34,6 +33,8 @@ require (
|
||||
k8s.io/apimachinery v0.32.3
|
||||
)
|
||||
|
||||
require github.com/go-jose/go-jose/v3 v3.0.4
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.118.2 // indirect
|
||||
@ -41,11 +42,8 @@ require (
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.3.1 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.4 // indirect
|
||||
cloud.google.com/go/monitoring v1.23.0 // indirect
|
||||
cloud.google.com/go/spanner v1.75.0 // indirect
|
||||
cloud.google.com/go/storage v1.50.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
@ -54,13 +52,10 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect
|
||||
github.com/apache/arrow-go/v18 v18.2.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
|
||||
@ -82,8 +77,6 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/bluele/gcache v0.0.2 // indirect
|
||||
github.com/bufbuild/protocompile v0.4.0 // indirect
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@ -91,7 +84,6 @@ require (
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/elazarl/goproxy v1.7.2 // indirect
|
||||
@ -102,7 +94,6 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.131.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
@ -110,18 +101,14 @@ require (
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.0 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gogo/status v1.1.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.7.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
@ -130,70 +117,38 @@ require (
|
||||
github.com/google/wire v0.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/googleapis/go-sql-spanner v1.11.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250331100049-71df46678ea6 // indirect
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // indirect
|
||||
github.com/grafana/grafana-app-sdk v0.35.1 // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.35.0 // indirect
|
||||
github.com/grafana/grafana-aws-sdk v0.36.0 // indirect
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect
|
||||
github.com/grafana/otel-profiling-go v0.5.1 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
|
||||
github.com/grafana/sqlds/v4 v4.2.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-msgpack v1.1.5 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.3 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/memberlist v0.5.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jhump/protoreflect v1.15.1 // indirect
|
||||
github.com/jmespath-community/go-jmespath v1.1.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/magefile/mage v1.15.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattetti/filebuffer v1.0.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mithrandie/csvq v1.18.1 // indirect
|
||||
github.com/mithrandie/csvq-driver v1.7.0 // indirect
|
||||
github.com/mithrandie/go-file/v2 v2.1.0 // indirect
|
||||
github.com/mithrandie/go-text v1.6.0 // indirect
|
||||
github.com/mithrandie/ternary v1.1.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/open-feature/go-sdk v1.14.1 // indirect
|
||||
github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.3 // indirect
|
||||
github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
@ -202,17 +157,11 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/alertmanager v0.27.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/exporter-toolkit v0.13.2 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tjhop/slog-gokit v0.1.3 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
@ -230,7 +179,6 @@ require (
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
@ -244,7 +192,6 @@ require (
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
@ -256,11 +203,8 @@ require (
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.32.3 // indirect
|
||||
k8s.io/apiserver v0.32.3 // indirect
|
||||
k8s.io/component-base v0.32.3 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
@ -268,7 +212,4 @@ require (
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
xorm.io/builder v0.3.6 // indirect
|
||||
xorm.io/core v0.7.3 // indirect
|
||||
xorm.io/xorm v0.8.2 // indirect
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,13 +4,13 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func NewSearchOptions(features featuremgmt.FeatureToggles, cfg *setting.Cfg, tracer tracing.Tracer, docs resource.DocumentBuilderSupplier, indexMetrics *resource.BleveIndexMetrics) (resource.SearchOptions, error) {
|
||||
func NewSearchOptions(features featuremgmt.FeatureToggles, cfg *setting.Cfg, tracer trace.Tracer, docs resource.DocumentBuilderSupplier, indexMetrics *resource.BleveIndexMetrics) (resource.SearchOptions, error) {
|
||||
// Setup the search server
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagUnifiedStorageSearch) ||
|
||||
features.IsEnabledGlobally(featuremgmt.FlagProvisioning) {
|
||||
|
@ -5,11 +5,11 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -19,7 +19,7 @@ import (
|
||||
|
||||
// Creates a new ResourceServer
|
||||
func NewResourceServer(db infraDB.DB, cfg *setting.Cfg,
|
||||
tracer tracing.Tracer, reg prometheus.Registerer, ac types.AccessClient,
|
||||
tracer trace.Tracer, reg prometheus.Registerer, ac types.AccessClient,
|
||||
searchOptions resource.SearchOptions, storageMetrics *resource.StorageMetrics,
|
||||
indexMetrics *resource.BleveIndexMetrics, features featuremgmt.FeatureToggles) (resource.ResourceServer, error) {
|
||||
apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver")
|
||||
|
@ -2,17 +2,21 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
"github.com/grafana/authlib/grpcutils"
|
||||
"github.com/grafana/dskit/services"
|
||||
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/modules"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
"github.com/grafana/grafana/pkg/services/authz"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
@ -45,9 +49,9 @@ type service struct {
|
||||
|
||||
handler grpcserver.Provider
|
||||
|
||||
tracing *tracing.TracingService
|
||||
tracing trace.Tracer
|
||||
|
||||
authenticator interceptors.Authenticator
|
||||
authenticator func(ctx context.Context) (context.Context, error)
|
||||
|
||||
log log.Logger
|
||||
reg prometheus.Registerer
|
||||
@ -67,16 +71,7 @@ func ProvideUnifiedStorageGrpcService(
|
||||
storageMetrics *resource.StorageMetrics,
|
||||
indexMetrics *resource.BleveIndexMetrics,
|
||||
) (UnifiedStorageGrpcService, error) {
|
||||
tracingCfg, err := tracing.ProvideTracingConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tracingCfg.ServiceName = "unified-storage"
|
||||
|
||||
tracing, err := tracing.ProvideService(tracingCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tracer := otel.Tracer("unified-storage")
|
||||
|
||||
// reg can be nil when running unified storage in standalone mode
|
||||
if reg == nil {
|
||||
@ -85,14 +80,17 @@ func ProvideUnifiedStorageGrpcService(
|
||||
|
||||
// FIXME: This is a temporary solution while we are migrating to the new authn interceptor
|
||||
// grpcutils.NewGrpcAuthenticator should be used instead.
|
||||
authn := grpcutils.NewAuthenticatorWithFallback(cfg, reg, tracing, &grpc.Authenticator{Tracer: tracing})
|
||||
authn := NewAuthenticatorWithFallback(cfg, reg, tracer, func(ctx context.Context) (context.Context, error) {
|
||||
auth := grpc.Authenticator{Tracer: tracer}
|
||||
return auth.Authenticate(ctx)
|
||||
})
|
||||
|
||||
s := &service{
|
||||
cfg: cfg,
|
||||
features: features,
|
||||
stopCh: make(chan struct{}),
|
||||
authenticator: authn,
|
||||
tracing: tracing,
|
||||
tracing: tracer,
|
||||
db: db,
|
||||
log: log,
|
||||
reg: reg,
|
||||
@ -122,7 +120,7 @@ func (s *service) start(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.handler, err = grpcserver.ProvideService(s.cfg, s.features, s.authenticator, s.tracing, prometheus.DefaultRegisterer)
|
||||
s.handler, err = grpcserver.ProvideService(s.cfg, s.features, interceptors.AuthenticatorFunc(s.authenticator), s.tracing, prometheus.DefaultRegisterer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -175,3 +173,88 @@ func (s *service) running(ctx context.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type authenticatorWithFallback struct {
|
||||
authenticator func(ctx context.Context) (context.Context, error)
|
||||
fallback func(ctx context.Context) (context.Context, error)
|
||||
metrics *metrics
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
type metrics struct {
|
||||
requestsTotal *prometheus.CounterVec
|
||||
}
|
||||
|
||||
func (f *authenticatorWithFallback) Authenticate(ctx context.Context) (context.Context, error) {
|
||||
ctx, span := f.tracer.Start(ctx, "grpcutils.AuthenticatorWithFallback.Authenticate")
|
||||
defer span.End()
|
||||
|
||||
// Try to authenticate with the new authenticator first
|
||||
span.SetAttributes(attribute.Bool("fallback_used", false))
|
||||
newCtx, err := f.authenticator(ctx)
|
||||
if err == nil {
|
||||
// fallback not used, authentication successful
|
||||
f.metrics.requestsTotal.WithLabelValues("false", "true").Inc()
|
||||
return newCtx, nil
|
||||
}
|
||||
|
||||
// In case of error, fallback to the legacy authenticator
|
||||
span.SetAttributes(attribute.Bool("fallback_used", true))
|
||||
newCtx, err = f.fallback(ctx)
|
||||
if newCtx != nil {
|
||||
newCtx = resource.WithFallback(newCtx)
|
||||
}
|
||||
f.metrics.requestsTotal.WithLabelValues("true", fmt.Sprintf("%t", err == nil)).Inc()
|
||||
return newCtx, err
|
||||
}
|
||||
|
||||
const (
|
||||
metricsNamespace = "grafana"
|
||||
metricsSubSystem = "grpc_authenticator_with_fallback"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func newMetrics(reg prometheus.Registerer) *metrics {
|
||||
m := &metrics{
|
||||
requestsTotal: prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: metricsSubSystem,
|
||||
Name: "requests_total",
|
||||
Help: "Number requests using the authenticator with fallback",
|
||||
}, []string{"fallback_used", "result"}),
|
||||
}
|
||||
|
||||
if reg != nil {
|
||||
once.Do(func() {
|
||||
reg.MustRegister(m.requestsTotal)
|
||||
})
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func ReadGrpcServerConfig(cfg *setting.Cfg) *grpcutils.AuthenticatorConfig {
|
||||
section := cfg.SectionWithEnvOverrides("grpc_server_authentication")
|
||||
|
||||
return &grpcutils.AuthenticatorConfig{
|
||||
SigningKeysURL: section.Key("signing_keys_url").MustString(""),
|
||||
AllowedAudiences: section.Key("allowed_audiences").Strings(","),
|
||||
AllowInsecure: cfg.Env == setting.Dev,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuthenticatorWithFallback(cfg *setting.Cfg, reg prometheus.Registerer, tracer trace.Tracer, fallback func(context.Context) (context.Context, error)) func(context.Context) (context.Context, error) {
|
||||
authCfg := ReadGrpcServerConfig(cfg)
|
||||
authenticator := grpcutils.NewAuthenticator(authCfg, tracer)
|
||||
return func(ctx context.Context) (context.Context, error) {
|
||||
a := &authenticatorWithFallback{
|
||||
authenticator: authenticator,
|
||||
fallback: fallback,
|
||||
tracer: tracer,
|
||||
metrics: newMetrics(reg),
|
||||
}
|
||||
return a.Authenticate(ctx)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user