Alerting: Reject receiver update in config API when FlagAlertingApiServer enabled (#93300)

* Reject receiver update in config API when FlagAlertingApiServer enabled
This commit is contained in:
Matthew Jacobson
2024-09-17 16:49:17 +03:00
committed by GitHub
parent cbf2aa993e
commit 1ea873950b
7 changed files with 149 additions and 32 deletions
+5 -4
View File
@@ -96,10 +96,11 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
api.DatasourceCache,
NewLotexAM(proxy, logger),
&AlertmanagerSrv{
crypto: api.MultiOrgAlertmanager.Crypto,
log: logger,
ac: api.AccessControl,
mam: api.MultiOrgAlertmanager,
crypto: api.MultiOrgAlertmanager.Crypto,
log: logger,
ac: api.AccessControl,
mam: api.MultiOrgAlertmanager,
featureManager: api.FeatureManager,
silenceSvc: notifier.NewSilenceService(
accesscontrol.NewSilenceService(api.AccessControl, api.RuleStore),
api.TransactionManager,
+19 -5
View File
@@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@@ -28,11 +29,12 @@ const (
)
type AlertmanagerSrv struct {
log log.Logger
ac accesscontrol.AccessControl
mam *notifier.MultiOrgAlertmanager
crypto notifier.Crypto
silenceSvc SilenceService
log log.Logger
ac accesscontrol.AccessControl
mam *notifier.MultiOrgAlertmanager
crypto notifier.Crypto
silenceSvc SilenceService
featureManager featuremgmt.FeatureToggles
}
type UnknownReceiverError struct {
@@ -195,6 +197,18 @@ func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *contextmodel.ReqContext, b
return ErrResp(http.StatusBadRequest, err, "")
}
}
if srv.featureManager.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingApiServer) {
if err != nil {
// Unclear if returning an error here is the right thing to do, preventing the user from posting a new config
// when the current one is legitimately invalid is not optimal, but we need to ensure receiver
// permissions are maintained and prevent potential access control bypasses. The workaround is to use the
// various new k8s API endpoints to fix the configuration.
return ErrResp(http.StatusInternalServerError, err, "")
}
if err := srv.k8sApiServiceGuard(currentConfig, body); err != nil {
return ErrResp(http.StatusBadRequest, err, "")
}
}
err = srv.mam.SaveAndApplyAlertmanagerConfiguration(c.Req.Context(), c.SignedInUser.GetOrgID(), body)
if err == nil {
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"})
@@ -32,6 +32,27 @@ func (srv AlertmanagerSrv) provenanceGuard(currentConfig apimodels.GettableUserC
return nil
}
func (srv AlertmanagerSrv) k8sApiServiceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
// Modifications to receivers via this API is tricky with new per-receiver RBAC. Assuming we restrict the API to only
// those users with global edit permissions, we would still need to consider the following:
// - Since the UIDs stored in the database for the purposes of per-receiver RBAC are generated based on the receiver
// name, we would need to ensure continuity of permissions when a receiver is renamed. This would, preferably,
// require detecting renames and updating the permissions UID in the database.
// - It would need to determine newly created and deleted receivers so it can populate per-receiver access control defaults.
// Neither of these are insurmountable, but considering this endpoint will be removed once FlagAlertingApiServer
// becomes GA, the complexity may not be worthwhile. To that end, for now we reject any request that attempts to
// modify receivers.
delta, err := calculateReceiversDelta(currentConfig.AlertmanagerConfig.Receivers, newConfig.AlertmanagerConfig.Receivers)
if err != nil {
return err
}
if !delta.IsEmpty() {
return fmt.Errorf("cannot modify receivers using this API while per-receiver RBAC is enabled; either disable the `alertingApiServer` feature flag or use an API that supports per-receiver RBAC (e.g. provisioning or receivers API)")
}
return nil
}
func checkRoutes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
reporter := cmputil.DiffReporter{}
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{})}
@@ -569,11 +569,12 @@ func createSut(t *testing.T) AlertmanagerSrv {
ruleStore := ngfakes.NewRuleStore(t)
ruleAuthzService := accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()))
return AlertmanagerSrv{
mam: mam,
crypto: mam.Crypto,
ac: ac,
log: log,
silenceSvc: notifier.NewSilenceService(accesscontrol.NewSilenceService(ac, ruleStore), ruleStore, log, mam, ruleStore, ruleAuthzService),
mam: mam,
crypto: mam.Crypto,
ac: ac,
log: log,
featureManager: featuremgmt.WithFeatures(),
silenceSvc: notifier.NewSilenceService(accesscontrol.NewSilenceService(ac, ruleStore), ruleStore, log, mam, ruleStore, ruleAuthzService),
}
}