K8s/ManagedBy: Enforce who can CRUD provisioning resources (#103322)

This commit is contained in:
Ryan McKinley
2025-04-08 14:17:33 +03:00
committed by GitHub
parent 577ea8f6a9
commit d3e6e308a0
8 changed files with 290 additions and 8 deletions
+88
View File
@@ -0,0 +1,88 @@
package apistore
import (
"net/http"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
authtypes "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
func checkManagerPropertiesOnDelete(auth authtypes.AuthInfo, obj utils.GrafanaMetaAccessor) error {
return enforceManagerProperties(auth, obj)
}
func checkManagerPropertiesOnCreate(auth authtypes.AuthInfo, obj utils.GrafanaMetaAccessor) error {
return enforceManagerProperties(auth, obj)
}
func checkManagerPropertiesOnUpdateSpec(auth authtypes.AuthInfo, obj utils.GrafanaMetaAccessor, old utils.GrafanaMetaAccessor) error {
objKind := obj.GetAnnotation(utils.AnnoKeyManagerKind)
oldKind := old.GetAnnotation(utils.AnnoKeyManagerKind)
if objKind == "" && objKind == oldKind {
return nil // not managed
}
// Check the current settings
err := checkManagerPropertiesOnCreate(auth, obj)
if err != nil { // new settings failed
return err
}
managerNew, okNew := obj.GetManagerProperties()
managerOld, okOld := old.GetManagerProperties()
if managerNew == managerOld || (okNew && !okOld) { // added manager is OK
return nil
}
if !okNew && okOld {
// This allows removing the managedBy annotations if you were allowed to write them originally
if err := checkManagerPropertiesOnCreate(auth, old); err != nil {
return &apierrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusForbidden,
Reason: metav1.StatusReasonForbidden,
Message: "Can not remove resource manager from resource",
}}
}
}
return nil
}
func enforceManagerProperties(auth authtypes.AuthInfo, obj utils.GrafanaMetaAccessor) error {
kind := obj.GetAnnotation(utils.AnnoKeyManagerKind)
if kind == "" {
return nil
}
switch utils.ParseManagerKindString(kind) {
case utils.ManagerKindUnknown:
return nil // not managed
case utils.ManagerKindRepo:
if auth.GetUID() == "access-policy:provisioning" {
return nil // OK!
}
return &apierrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusForbidden,
Reason: metav1.StatusReasonForbidden,
Message: "Provisioned resources must be manaaged by the provisioning service account",
}}
case utils.ManagerKindPlugin, utils.ManagerKindClassicFP: // nolint:staticcheck
// ?? what identity do we use for legacy internal requests?
return nil // no error
case utils.ManagerKindTerraform, utils.ManagerKindKubectl:
manager, _ := obj.GetManagerProperties()
if manager.AllowsEdits {
return nil // no error anyone can do it
}
// TODO: check the kubectl+terraform resource
return nil
}
return nil
}