From 151b81361c0df63823c1622d349b0414dc874cca Mon Sep 17 00:00:00 2001 From: konsalex Date: Wed, 26 Nov 2025 11:42:53 +0100 Subject: [PATCH] rebasing --- conf/apiextensions.ini | 8 ++- pkg/apimachinery/identity/context.go | 1 + pkg/registry/apis/apiextensions/register.go | 80 ++++----------------- 3 files changed, 21 insertions(+), 68 deletions(-) diff --git a/conf/apiextensions.ini b/conf/apiextensions.ini index 747e034026f..b9d0c4d50ff 100644 --- a/conf/apiextensions.ini +++ b/conf/apiextensions.ini @@ -37,4 +37,10 @@ dualWriterMode = 5 ; SQLite database for testing type = sqlite3 path = grafana.db -high_availability = false \ No newline at end of file +high_availability = false + +; Will only be used for the MT grafana +; apiextensions service +; [auth.extended_jwt] +; enabled = true +; jwks_url = "http://localhost:6481/jwks" \ No newline at end of file diff --git a/pkg/apimachinery/identity/context.go b/pkg/apimachinery/identity/context.go index 6b39c1af046..53f48912fea 100644 --- a/pkg/apimachinery/identity/context.go +++ b/pkg/apimachinery/identity/context.go @@ -160,6 +160,7 @@ var serviceIdentityTokenPermissions = []string{ "iam.grafana.app:*", "preferences.grafana.app:*", // user, team, and org preferences "collections.grafana.app:*", // user stars + "apiextensions.grafana.app:*", // Secrets Manager uses a custom verb for secret decryption, and its authorizer does not allow wildcard permissions. "secret.grafana.app/securevalues:decrypt", diff --git a/pkg/registry/apis/apiextensions/register.go b/pkg/registry/apis/apiextensions/register.go index f046330559b..86438f460bc 100644 --- a/pkg/registry/apis/apiextensions/register.go +++ b/pkg/registry/apis/apiextensions/register.go @@ -12,6 +12,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/authorization/authorizer" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" "k8s.io/apiserver/pkg/registry/rest" genericapiserver "k8s.io/apiserver/pkg/server" @@ -22,6 +23,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" + grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/setting" @@ -93,6 +95,11 @@ func RegisterAPIService( return b, nil } +// The default authorizer is fine because authorization happens in storage where we know the parent folder +func (b *APIExtensionsBuilder) GetAuthorizer() authorizer.Authorizer { + return grafanaauthorizer.NewServiceAuthorizer() +} + // loadAndRegisterCRDsWithDynamicHandler loads CRDs from storage and registers their handlers // This is called during UpdateAPIGroupInfo when storage IS ready func (b *APIExtensionsBuilder) loadAndRegisterCRDsWithDynamicHandler( @@ -100,17 +107,12 @@ func (b *APIExtensionsBuilder) loadAndRegisterCRDsWithDynamicHandler( crdStore *genericregistry.Store, opts builder.APIGroupOptions, ) error { - // Create a system context with fallback auth - systemCtx := resource.WithFallback(ctx) - systemCtx = authlib.WithAuthInfo(systemCtx, &identity.StaticRequester{ - Type: authlib.TypeServiceAccount, - Login: "system:apiextensions", - UserID: 0, - UserUID: "system:apiextensions", - OrgID: 1, - OrgRole: identity.RoleAdmin, - IsGrafanaAdmin: true, - }) + // TODO(@konsalex): Have a conditional check here for MT + // For ST we can use the identity.WithServiceIdentityContext + // for MT we can use implicitly use a service token: + // https://github.com/grafana/kube-manifests/blob/9f5e409c72fef4f831b173480131121e9cb348a3/flux/dev-us-east-0/grafana-iam/Deployment-iam-grafana-app-main.yaml#L114C9-L114C59 + // SO we can skip systemCtx creation + systemCtx := identity.WithServiceIdentityContext(context.WithoutCancel(ctx), 1) // List all CRDs using the initialized storage listObj, err := crdStore.List(systemCtx, &metainternalversion.ListOptions{}) @@ -177,62 +179,6 @@ func (b *APIExtensionsBuilder) loadAndRegisterCRDsWithDynamicHandler( return nil } -// loadAndRegisterCRDs loads existing CRDs from storage and registers their API group builders -// This is called during UpdateAPIGroupInfo when storage is ready -func (b *APIExtensionsBuilder) loadAndRegisterCRDs(ctx context.Context, crdStore *genericregistry.Store, opts builder.APIGroupOptions) error { - if b.apiregistrar == nil { - return fmt.Errorf("apiregistrar is nil") - } - - // Create a system context with fallback auth for initialization - // This allows us to list CRDs without a user session during server startup - // TODO(@konsalex): Does this cause any security issue? Not 100% how to authenticate - // a service call like this - // Use well-know constants, and co-ord with IAM and SnStorage to ensure this is secure. - systemCtx := resource.WithFallback(ctx) - systemCtx = authlib.WithAuthInfo(systemCtx, &identity.StaticRequester{ - Type: authlib.TypeServiceAccount, - Login: "system:apiextensions", - UserID: 0, - UserUID: "system:apiextensions", - OrgID: 1, - OrgRole: identity.RoleAdmin, - IsGrafanaAdmin: true, - }) - - // List all CRDs using the initialized storage with system context - listObj, err := crdStore.List(systemCtx, &metainternalversion.ListOptions{}) - if err != nil { - return fmt.Errorf("failed to list CRDs: %w", err) - } - - crdList, ok := listObj.(*apiextensionsv1.CustomResourceDefinitionList) - if !ok { - return fmt.Errorf("unexpected list type: %T", listObj) - } - - if len(crdList.Items) == 0 { - return nil - } - - // Register a builder for each CRD - for i := range crdList.Items { - crd := &crdList.Items[i] - - // Register with the dynamic registry for API group installation - if err := b.dynamicReg.RegisterCRD(crd); err != nil { - // TODO(@konsalex): Add logger in the context and use it - fmt.Printf(" Warning: failed to register CRD %s in dynamic registry: %v\n", crd.Name, err) - continue - } - // DEBUG - fmt.Printf("Registered custom resource API group: %s/%s (resource: %s)\n", - crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural) - } - - return nil -} - // NewAPIService creates an APIExtensionsBuilder for multi-tenant mode // TODO(@konsalex): NOT YET IMPLEMENTED properly func NewAPIService(