maxuru ~ % cat grafana
Hardening Grafana
Grafana is not primarily a dashboard. It is a credential store that happens to draw graphs.
To show you a panel it must hold working credentials for the system behind it — your Prometheus, your PostgreSQL, your Elasticsearch, your MySQL, your cloud provider’s metrics API. A single Grafana commonly holds the keys to a dozen backends, which makes it worth more to an attacker than any one of them.
That reframes the whole checklist. The question is not “who can see my dashboards” but “what can somebody do with what Grafana knows” — and the answer runs through data source credential theft.
// grafana checklist — 7 controls
Controls
Section titled “Controls”Grafana’s defaults are better than its reputation
Section titled “Grafana’s defaults are better than its reputation”Worth stating up front, because most Grafana hardening advice implies the
opposite and it changes what you should actually spend time on. Read from
Grafana’s own defaults.ini:
| Setting | Default | Verdict |
|---|---|---|
[auth.anonymous] enabled |
false |
Good — anonymous access is off |
[users] allow_sign_up |
false |
Good — no self-registration |
[users] allow_org_create |
false |
Good |
disable_brute_force_login_protection |
false |
Good — protection is on |
allow_embedding |
false |
Good — no framing |
admin_password |
admin |
The problem |
secret_key |
SW2YcwTIb9zpOOhoPsMm |
The bigger problem |
[server] protocol |
http |
Plaintext |
cookie_secure |
false |
Follows from the above |
[snapshots] external_enabled |
true |
Publishes to a third party |
“Disable anonymous access” leads a lot of Grafana guides. It is already disabled — so that page here is a verification, not a change, and the time saved belongs on the two rows marked as problems.
The secret key is the one people miss
Section titled “The secret key is the one people miss”admin/admin gets all the attention. The default secret_key deserves
more.
It is the same twenty characters in every Grafana installation on earth, it is
in the public source tree, and it is what encrypts data source passwords in
Grafana’s database. Anyone who obtains a copy of grafana.db — a backup, a
snapshot, a container volume, a file-read vulnerability — can decrypt every
stored credential with a key they already know.
Version
Section titled “Version”Grafana 13 is the current major line. Two security notes worth carrying:
- CVE-2026-27876 — RCE via SQL Expressions chained with a Grafana Enterprise
plugin, CVSS 9.1. Only instances with the
sqlExpressionsfeature toggle enabled are affected. Fixed in 11.6.14, 12.1.10, 12.2.8, 12.3.6 and 12.4.2; v13 was never affected. - CVE-2021-43798 — unauthenticated arbitrary file read in Grafana 8.x, in CISA’s Known Exploited Vulnerabilities catalogue. It is the historical example the threat page is built on, because it turned “Grafana is reachable” into “every data source credential is gone”.
grafana server -vLicence — check what you are entitled to
Section titled “Licence — check what you are entitled to”Grafana moved from Apache 2.0 to AGPLv3 on 20 April 2021, along with Loki and Tempo. Plugins, agents and some libraries stayed Apache-licensed.
This is not the distro-drops-it story that MongoDB has — Grafana OSS remains freely available and packaged. It matters for two narrower reasons: AGPL has obligations if you modify Grafana and expose it over a network, and the OSS/Enterprise split determines which features you have. Audit logging in particular is not an OSS feature, which is the same trap MongoDB and Elasticsearch set — a checklist item you cannot action on the build you are running.
Where the config lives
Section titled “Where the config lives”/etc/grafana/grafana.ini # yours — edit this/usr/share/grafana/conf/defaults.ini # reference only — never editGrafana reads defaults.ini first and overlays grafana.ini, so your file only
needs the lines you are changing. Environment variables override both, using
the pattern GF_<SECTION>_<KEY>:
GF_SECURITY_ADMIN_PASSWORD=...GF_SECURITY_SECRET_KEY=...That is how nearly all containerised Grafana is configured, and it is why
reading grafana.ini can be actively misleading in Docker and Kubernetes. Check
the environment as well:
sudo grafana server --config /etc/grafana/grafana.ini --homepath /usr/share/grafana cfg:default.log.level=debugdocker inspect grafana --format '{{range .Config.Env}}{{println .}}{{end}}' | grep ^GF_Restarting
Section titled “Restarting”sudo systemctl restart grafana-serverGrafana has no config reload — every change needs a restart. Dashboards and users live in the database, so a restart costs an interruption but loses nothing.