File 0002-Drop-zanzana.patch of Package grafana
commit c93b7a31c795c24c9ecde78a3d31c9479f913766
Author: Witek Bedyk <wbedyk@suse.com>
Date: Mon Nov 24 12:23:48 2025 +0100
Drop Zanzana
Drop experimental authorization server/client implementation which is
affected by CVE-2025-64751.
diff --git a/pkg/server/module_server.go b/pkg/server/module_server.go
index f20225cba5b..f65ead4ea88 100644
--- a/pkg/server/module_server.go
+++ b/pkg/server/module_server.go
@@ -13,7 +13,6 @@ import (
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/modules"
- "github.com/grafana/grafana/pkg/services/authz"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/sql"
@@ -138,10 +137,6 @@ func (s *ModuleServer) Run() error {
return sql.ProvideUnifiedStorageGrpcService(s.cfg, s.features, nil, s.log, nil, docBuilders)
})
- m.RegisterModule(modules.ZanzanaServer, func() (services.Service, error) {
- return authz.ProvideZanzanaService(s.cfg, s.features)
- })
-
m.RegisterModule(modules.All, nil)
return m.Run(s.context)
diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go
index 1cff4d1b04f..8d4817d6c4d 100644
--- a/pkg/server/wire_gen.go
+++ b/pkg/server/wire_gen.go
@@ -364,10 +364,6 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
return nil, err
}
actionSetService := resourcepermissions.NewActionSetService(featureToggles)
- client, err := authz.ProvideZanzana(cfg, sqlStore, featureToggles)
- if err != nil {
- return nil, err
- }
permissionRegistry := permreg.ProvidePermissionRegistry()
serverLockService := serverlock.ProvideService(sqlStore, tracingService)
folderStoreImpl := folderimpl.ProvideStore(sqlStore)
@@ -828,10 +824,6 @@ func InitializeForTest(t sqlutil.ITestDB, cfg *setting.Cfg, opts Options, apiOpt
return nil, err
}
actionSetService := resourcepermissions.NewActionSetService(featureToggles)
- client, err := authz.ProvideZanzana(cfg, sqlStore, featureToggles)
- if err != nil {
- return nil, err
- }
permissionRegistry := permreg.ProvidePermissionRegistry()
serverLockService := serverlock.ProvideService(sqlStore, tracingService)
folderStoreImpl := folderimpl.ProvideStore(sqlStore)
diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go
index 3a13f94af69..50452912c37 100644
--- a/pkg/services/accesscontrol/acimpl/service.go
+++ b/pkg/services/accesscontrol/acimpl/service.go
@@ -25,11 +25,9 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/api"
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
- "github.com/grafana/grafana/pkg/services/accesscontrol/dualwrite"
"github.com/grafana/grafana/pkg/services/accesscontrol/migrator"
"github.com/grafana/grafana/pkg/services/accesscontrol/permreg"
"github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils"
- "github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
@@ -54,7 +52,7 @@ var OSSRolesPrefixes = []string{accesscontrol.ManagedRolePrefix, accesscontrol.E
func ProvideService(
cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegister, cache *localcache.CacheService,
accessControl accesscontrol.AccessControl, userService user.Service, actionResolver accesscontrol.ActionResolver,
- features featuremgmt.FeatureToggles, tracer tracing.Tracer, zclient zanzana.Client, permRegistry permreg.PermissionRegistry,
+ features featuremgmt.FeatureToggles, tracer tracing.Tracer, permRegistry permreg.PermissionRegistry,
lock *serverlock.ServerLockService, folderService folder.Service,
) (*Service, error) {
service := ProvideOSSService(
@@ -64,7 +62,6 @@ func ProvideService(
cache,
features,
tracer,
- zclient,
db,
permRegistry,
lock,
@@ -90,7 +87,7 @@ func ProvideService(
func ProvideOSSService(
cfg *setting.Cfg, store accesscontrol.Store, actionResolver accesscontrol.ActionResolver,
cache *localcache.CacheService, features featuremgmt.FeatureToggles, tracer tracing.Tracer,
- zclient zanzana.Client, db db.DB, permRegistry permreg.PermissionRegistry,
+ db db.DB, permRegistry permreg.PermissionRegistry,
lock *serverlock.ServerLockService, folderService folder.Service,
) *Service {
s := &Service{
@@ -101,7 +98,6 @@ func ProvideOSSService(
log: log.New("accesscontrol.service"),
roles: accesscontrol.BuildBasicRoleDefinitions(),
store: store,
- reconciler: dualwrite.NewZanzanaReconciler(cfg, zclient, db, lock, folderService),
permRegistry: permRegistry,
}
@@ -118,15 +114,11 @@ type Service struct {
registrations accesscontrol.RegistrationList
roles map[string]*accesscontrol.RoleDTO
store accesscontrol.Store
- reconciler *dualwrite.ZanzanaReconciler
permRegistry permreg.PermissionRegistry
}
// Run implements accesscontrol.Service.
func (s *Service) Run(ctx context.Context) error {
- if s.features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
- return s.reconciler.Reconcile(ctx)
- }
return nil
}
diff --git a/pkg/services/authz/wireset.go b/pkg/services/authz/wireset.go
index 4a46f15ea3b..f16235fa4b4 100644
--- a/pkg/services/authz/wireset.go
+++ b/pkg/services/authz/wireset.go
@@ -6,5 +6,4 @@ import (
var WireSet = wire.NewSet(
ProvideAuthZClient,
- ProvideZanzana,
)
diff --git a/pkg/services/authz/zanzana.go b/pkg/services/authz/zanzana.go
deleted file mode 100644
index 6c1567ef469..00000000000
--- a/pkg/services/authz/zanzana.go
+++ /dev/null
@@ -1,264 +0,0 @@
-package authz
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/fullstorydev/grpchan/inprocgrpc"
- authnlib "github.com/grafana/authlib/authn"
- authzv1 "github.com/grafana/authlib/authz/proto/v1"
- "github.com/grafana/authlib/claims"
- "github.com/grafana/dskit/services"
- grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
- openfgav1 "github.com/openfga/api/proto/openfga/v1"
- "github.com/prometheus/client_golang/prometheus"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
- "google.golang.org/grpc/metadata"
-
- "github.com/grafana/grafana/pkg/infra/db"
- "github.com/grafana/grafana/pkg/infra/log"
- "github.com/grafana/grafana/pkg/infra/tracing"
- authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
- "github.com/grafana/grafana/pkg/services/authz/zanzana"
- zclient "github.com/grafana/grafana/pkg/services/authz/zanzana/client"
- zserver "github.com/grafana/grafana/pkg/services/authz/zanzana/server"
- "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"
-)
-
-const zanzanaAudience = "zanzana"
-
-// ProvideZanzana used to register ZanzanaClient.
-// It will also start an embedded ZanzanaSever if mode is set to "embedded".
-func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureToggles) (zanzana.Client, error) {
- if !features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
- return zclient.NewNoop(), nil
- }
-
- logger := log.New("zanzana")
-
- var client zanzana.Client
- switch cfg.Zanzana.Mode {
- case setting.ZanzanaModeClient:
- tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
- Token: cfg.Zanzana.Token,
- TokenExchangeURL: cfg.Zanzana.TokenExchangeURL,
- })
- if err != nil {
- return nil, fmt.Errorf("failed to initialize token exchange client: %w", err)
- }
-
- if cfg.StackID == "" {
- return nil, fmt.Errorf("missing stack ID")
- }
- namespace := fmt.Sprintf("stacks-%s", cfg.StackID)
-
- tokenAuthCred := &tokenAuth{
- cfg: cfg,
- namespace: namespace,
- tokenClient: tokenClient,
- }
-
- dialOptions := []grpc.DialOption{
- // TODO: add TLS support
- grpc.WithTransportCredentials(insecure.NewCredentials()),
- grpc.WithPerRPCCredentials(tokenAuthCred),
- }
-
- conn, err := grpc.NewClient(cfg.Zanzana.Addr, dialOptions...)
- if err != nil {
- return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
- }
-
- client, err = zclient.NewClient(context.Background(), conn, cfg)
- if err != nil {
- return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
- }
- case setting.ZanzanaModeEmbedded:
- store, err := zanzana.NewEmbeddedStore(cfg, db, logger)
- if err != nil {
- return nil, fmt.Errorf("failed to start zanzana: %w", err)
- }
-
- openfga, err := zserver.NewOpenFGA(&cfg.Zanzana, store, logger)
- if err != nil {
- return nil, fmt.Errorf("failed to start zanzana: %w", err)
- }
-
- srv, err := zserver.NewAuthzServer(cfg, openfga)
- if err != nil {
- return nil, fmt.Errorf("failed to start zanzana: %w", err)
- }
-
- channel := &inprocgrpc.Channel{}
- // Put * as a namespace so we can properly authorize request with in-proc mode
- channel.WithServerUnaryInterceptor(grpcAuth.UnaryServerInterceptor(func(ctx context.Context) (context.Context, error) {
- ctx = claims.WithClaims(ctx, authnlib.NewAccessTokenAuthInfo(authnlib.Claims[authnlib.AccessTokenClaims]{
- Rest: authnlib.AccessTokenClaims{
- Namespace: "*",
- },
- }))
- return ctx, nil
- }))
-
- openfgav1.RegisterOpenFGAServiceServer(channel, openfga)
- authzv1.RegisterAuthzServiceServer(channel, srv)
- authzextv1.RegisterAuthzExtentionServiceServer(channel, srv)
-
- client, err = zclient.NewClient(context.Background(), channel, cfg)
- if err != nil {
- return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
- }
-
- default:
- return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.Zanzana.Mode)
- }
-
- return client, nil
-}
-
-type ZanzanaService interface {
- services.NamedService
-}
-
-var _ ZanzanaService = (*Zanzana)(nil)
-
-// ProvideZanzanaService is used to register zanzana as a module so we can run it seperatly from grafana.
-func ProvideZanzanaService(cfg *setting.Cfg, features featuremgmt.FeatureToggles) (*Zanzana, error) {
- s := &Zanzana{
- cfg: cfg,
- features: features,
- logger: log.New("zanzana"),
- }
-
- s.BasicService = services.NewBasicService(s.start, s.running, s.stopping).WithName("zanzana")
-
- return s, nil
-}
-
-type Zanzana struct {
- *services.BasicService
-
- cfg *setting.Cfg
-
- logger log.Logger
- handle grpcserver.Provider
- features featuremgmt.FeatureToggles
-}
-
-func (z *Zanzana) start(ctx context.Context) error {
- store, err := zanzana.NewStore(z.cfg, z.logger)
- if err != nil {
- return fmt.Errorf("failed to initilize zanana store: %w", err)
- }
-
- openfga, err := zserver.NewOpenFGA(&z.cfg.Zanzana, store, z.logger)
- if err != nil {
- return fmt.Errorf("failed to start zanzana: %w", err)
- }
-
- srv, err := zserver.NewAuthzServer(z.cfg, openfga)
- if err != nil {
- return fmt.Errorf("failed to start zanzana: %w", err)
- }
-
- tracingCfg, err := tracing.ProvideTracingConfig(z.cfg)
- if err != nil {
- return err
- }
- tracingCfg.ServiceName = "zanzana"
-
- tracer, err := tracing.ProvideService(tracingCfg)
- if err != nil {
- return err
- }
-
- authenticator := authnlib.NewAccessTokenAuthenticator(
- authnlib.NewAccessTokenVerifier(
- authnlib.VerifierConfig{
- AllowedAudiences: []string{zanzanaAudience},
- },
- authnlib.NewKeyRetriever(authnlib.KeyRetrieverConfig{
- SigningKeysURL: z.cfg.Zanzana.SigningKeysURL,
- }),
- ),
- )
-
- authfn := interceptors.AuthenticatorFunc(func(ctx context.Context) (context.Context, error) {
- md, ok := metadata.FromIncomingContext(ctx)
- if !ok {
- return nil, fmt.Errorf("missing metadata")
- }
- c, err := authenticator.Authenticate(ctx, authnlib.NewGRPCTokenProvider(md))
- if err != nil {
- return nil, err
- }
- return claims.WithClaims(ctx, c), nil
- })
-
- z.handle, err = grpcserver.ProvideService(z.cfg, z.features, authfn, tracer, prometheus.DefaultRegisterer)
- if err != nil {
- return fmt.Errorf("failed to create zanzana grpc server: %w", err)
- }
-
- s := z.handle.GetServer()
- openfgav1.RegisterOpenFGAServiceServer(s, openfga)
- authzv1.RegisterAuthzServiceServer(s, srv)
- authzextv1.RegisterAuthzExtentionServiceServer(s, srv)
-
- if _, err := grpcserver.ProvideReflectionService(z.cfg, z.handle); err != nil {
- return fmt.Errorf("failed to register reflection for zanzana: %w", err)
- }
-
- return nil
-}
-
-func (z *Zanzana) running(ctx context.Context) error {
- if z.cfg.Env == setting.Dev && z.cfg.Zanzana.ListenHTTP {
- go func() {
- z.logger.Info("Starting OpenFGA HTTP server")
- err := zserver.StartOpenFGAHttpSever(z.cfg, z.handle, z.logger)
- if err != nil {
- z.logger.Error("failed to start OpenFGA HTTP server", "error", err)
- }
- }()
- }
-
- // Run is blocking so we can just run it here
- return z.handle.Run(ctx)
-}
-
-func (z *Zanzana) stopping(err error) error {
- if err != nil && !errors.Is(err, context.Canceled) {
- z.logger.Error("Stopping zanzana due to unexpected error", "err", err)
- }
- return nil
-}
-
-type tokenAuth struct {
- cfg *setting.Cfg
- namespace string
- tokenClient *authnlib.TokenExchangeClient
-}
-
-func (t *tokenAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
- token, err := t.tokenClient.Exchange(ctx, authnlib.TokenExchangeRequest{
- Namespace: t.namespace,
- Audiences: []string{zanzanaAudience},
- })
- if err != nil {
- return nil, err
- }
-
- return map[string]string{
- authnlib.DefaultAccessTokenMetadataKey: token.Token,
- }, nil
-}
-
-func (t *tokenAuth) RequireTransportSecurity() bool {
- return false
-}