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:
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user