← News
/PLAYBOOK · 2026-08-16

Grafana Security Hardening: Prevent Unauthorized Access and Dashboard Leaks

Anonymous access, default credentials, IP allowlists, HTTPS, and cookie hardening. Step-by-step Grafana dashboard lockdown to stop unauthorized access and metric leaks.

Scope

Ops teams running Grafana dashboards—especially when the UI is on the public internet or reachable across multiple internal segments. If metrics include business KPIs, host utilization, or database connection counts, an unhardened Grafana instance is a recon source. Applies to Grafana 8.x/9.x/10.x. Nginx reverse-proxy deployments included.

Prerequisites

Root cause

Grafana defaults favor usability over security. Anonymous access is off by default but often enabled by mistake. Admin defaults to `admin/admin` if left unchanged. The service listens on `0.0.0.0:3000`; any reachable host gets the login page. Grafana has shipped high-severity bugs: CVE-2021-43798 (unauthenticated arbitrary file read), CVE-2023-3128 (low-privilege directory traversal), and login-page CSRF (CVE-2022-39328). Restrict network exposure, disable anonymous access, and force HTTPS. That cuts exploitability.

Procedure

1. Disable anonymous access and change the default password

Edit `/etc/grafana/grafana.ini`. These settings must be in effect:

# disable anonymous access
[auth.anonymous]
enabled = false

# change default admin (also changeable in the UI after first start)
[security]
admin_user = admin
admin_password = <strong-password>
# 32+ random hex; signs sessions and encrypts datasource secrets
secret_key = <openssl rand -hex 32>

Restart: `systemctl restart grafana-server`. Changing `secret_key` makes saved datasource passwords undecryptable. Re-enter them. Backup first.

2. IP allowlist and bind address

Bind Grafana to an internal address. Terminate public access at the reverse proxy:

# bind internal only; example 10.0.0.10
[server]
http_addr = 10.0.0.10
http_port = 3000

Firewall: port 3000 only from the management subnet:

# firewalld
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.0.1.0/24 port port=3000 protocol=tcp accept'
firewall-cmd --reload

# or ufw
ufw allow from 10.0.1.0/24 to any port 3000 proto tcp

3. Nginx reverse proxy and HTTPS

Do not expose 3000 to the internet. Proxy through Nginx. Add TLS and access control:

server {
    listen 443 ssl;
    server_name grafana.example.com;
    ssl_certificate     /etc/letsencrypt/live/grafana.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grafana.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # required: pass X-Forwarded-Proto so Grafana does not emit http links
        proxy_set_header X-Forwarded-Proto $scheme;
        # source IP allowlist (optional)
        allow 10.0.1.0/24;
        deny all;
    }
}

In grafana.ini, set the public URL:

[server]
root_url = https://grafana.example.com
domain = grafana.example.com
enforce_domain = false

[auth]
# required: login cookie sent only over HTTPS
login_cookie_secure = true

4. Harden sessions and dashboard access

[security]
# block iframe embedding; stop clickjacking
allow_embedding = false
# Cookie SameSite
cookie_samesite = strict

[users]
# disable self-signup
allow_sign_up = false
# new users default to read-only
auto_assign_org_role = Viewer

Verify

# 1. verify listen address
ss -tlnp | grep 3000

# 2. unauthenticated request must 302 to login
curl -sI http://10.0.0.10:3000/ | head -3

# 3. anonymous must return 302/403 (200 means anonymous still on)
curl -sI http://10.0.0.10:3000/api/health

# 4. reverse-proxy HTTPS OK
curl -sI https://grafana.example.com/ | head -3

# 5. check version; match against Grafana security advisories
curl -s http://10.0.0.10:3000/api/health

FAQ

Q1: Datasources fail after changing secret_key. Fix?

Expected. `secret_key` encrypts datasource passwords. Roll back the old `secret_key` and restart. If the old key is gone, re-enter passwords on each datasource. Backup `grafana.db` before changing the key.

Q2: After disabling anonymous access, how do colleagues view the wallboard?

Do not turn anonymous back on. Create a read-only Viewer account and share the dashboard link. Or use Grafana Public Dashboards (enable explicitly, bound the time range). Keep `allow_embedding = false`.

Summary

Three moves: shrink exposure, kill default backdoors, encrypt transit. Disable anonymous access and change the default password. Bind to an internal address and allowlist source IPs. Front with Nginx+HTTPS. Harden session cookies. This cuts unauthenticated exploit risk for bugs such as CVE-2021-43798. Stay on the latest stable release. Audit regularly.