maxuru ~ % cat grafana/restrict-data-source-permissions
Restrict what Grafana's data source credentials can do
-- PostgreSQL: a data source account that can only read what it mustCREATE ROLE grafana_ro LOGIN PASSWORD 'generated-value';GRANT CONNECT ON DATABASE appdb TO grafana_ro;GRANT USAGE ON SCHEMA metrics TO grafana_ro;GRANT SELECT ON ALL TABLES IN SCHEMA metrics TO grafana_ro;ALTER DEFAULT PRIVILEGES IN SCHEMA metrics GRANT SELECT ON TABLES TO grafana_ro;The control is not really configured in Grafana. It is configured in each system Grafana connects to.
The credential is the asset
Section titled “The credential is the asset”Everything else on this checklist protects Grafana. This one limits what Grafana’s compromise is worth — and it is the only control here that keeps working after every other one has failed.
Grafana stores working credentials for each data source and uses them to run queries. Those credentials are frequently over-privileged, because the person adding the data source used one that was already available and the dashboard only had to work:
- the application’s own database user, which can write
- a cloud key with broad read access across services, not just metrics
- an Elasticsearch
elasticsuperuser rather than a scoped API key - a database superuser, because it was the one in the password manager
A Grafana breach with those stored is a breach of every one of those systems, at that privilege level. With read-only scoped accounts it is a disclosure of whatever the dashboards already showed — bad, bounded, and recoverable.
Per backend
Section titled “Per backend”The site has a page for most of these; the Grafana-specific instruction is to create a dedicated account per data source rather than reusing anything.
- PostgreSQL — a
LOGINrole withSELECTonly, as above. See least-privilege roles. Grafana can enforce read-only at its end too, but that is a client-side guard, not a permission. - MySQL / MariaDB —
GRANT SELECT ON metrics.* TO 'grafana_ro'@'10.0.1.5', scoped by host as well as schema. See least-privilege grants. - Elasticsearch — an API key with read privileges on the specific
indices, never the
elasticsuperuser. See API keys and least privilege. - Prometheus — has no built-in auth; the control is network placement plus a proxy in front. Note that Prometheus’s own admin API can delete series if it is enabled, so do not let Grafana’s path reach it.
- Cloud providers — a role scoped to the metrics API only. This is where over-privilege is most common and most expensive, because the same key often carries far more than monitoring.
Grafana-side settings that help
Section titled “Grafana-side settings that help”Two are worth knowing about:
[security]data_source_proxy_whitelist =Empty by default, meaning no restriction. Populating it with the
host:port pairs your data sources actually use limits where Grafana’s proxy
will send requests — a useful guard against a data source being edited to point
somewhere internal, which is a server-side request forgery in everything but
name. Anyone who can add or edit a data source in Grafana can otherwise aim it
at any address Grafana can reach, including
cloud metadata at 169.254.169.254.
The second is who can edit data sources at all. In Grafana that is Admin role within an organisation. Keep that list short — data source edit rights are effectively “make Grafana connect to arbitrary hosts with stored credentials”.
Related
Section titled “Related”- Data source credential theft — the scenario this control is sized against.
- Rotate the Grafana secret key — protecting the same credentials at rest.