Skip to content

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’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.

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.

See rotate the secret key.

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 sqlExpressions feature 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”.
Terminal window
grafana server -v

Licence — 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.

Terminal window
/etc/grafana/grafana.ini # yours — edit this
/usr/share/grafana/conf/defaults.ini # reference only — never edit

Grafana 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>:

Terminal window
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:

Terminal window
sudo grafana server --config /etc/grafana/grafana.ini --homepath /usr/share/grafana cfg:default.log.level=debug
docker inspect grafana --format '{{range .Config.Env}}{{println .}}{{end}}' | grep ^GF_
Terminal window
sudo systemctl restart grafana-server

Grafana 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.