ObservabilityXXX · Grafana SecurityGrafanaSecurity
RBAC and Teams
What you'll learn
- Map Grafana roles (Viewer, Editor, Admin) to the actions they permit on dashboards, data sources, alerting, and folders
- Distinguish org-admin from server-admin and know which operations each can and cannot do
- Apply folder-level permissions to grant team-scoped dashboard editing without granting org-wide Editor
- Configure team-sync from an external IdP group claim so team membership does not drift from HR
- Diagnose a permission-denied symptom to the missing role / team / folder combination that caused it
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
A Grafana install with two hundred engineers, twelve teams, and one Viewer role for the whole organisation. The platform team owns the data sources and provisioning; the application teams own their dashboards and alerting. A junior engineer on the checkout team opens a Loki datasource and sees every label, every derived field, every query that any other team has run in the last ninety days. An SRE on the platform team deletes a folder owned by the search team because the folder was empty and the SRE was granted Editor. A query lands in the alerting audit log under the wrong team. Every one of these incidents is a role assignment that did not match the team’s actual responsibilities.
This lesson is about closing that gap. Grafana’s role-based access control model is small and well-defined; the production failure shape is “the role was right for the team, but the team was wrong for the resource.”
What it is
Grafana RBAC is the set of permissions that decide what a user can see, edit, configure, and delete. There are four levels.
- Server Admin — the install-wide superuser. Can create organisations, manage users across organisations, install plugins, and read all data sources. Set via
[users] default_themeand theGrafanaAdminflag in theusertable. There is at most a handful of these in any production install. - Org Admin — the administrator of a single Grafana organisation. Can create users in that org, assign roles, create teams, configure data sources, and read the org audit log.
- Editor — can create, edit, and delete dashboards, alerting rules, and folders they have permission on. Cannot manage users or data sources.
- Viewer — read-only across the dashboards and panels they have permission on. Cannot save edits, cannot edit alerting rules.
Every role can be granted on three scopes:
- Organisation-wide — the role applies to every resource in the organisation. The default for the
adminuser after install. - Team — the role applies to resources owned by that team. The team itself is just a label and a member list; the resources the team can touch are governed by folder permissions.
- Folder — the role applies to a specific folder and every dashboard, alert rule, and panel beneath it. Folder permissions can grant Viewer or Editor; Admin is organisation-wide only.
A team is not a role. A team is a group of users. A role is what the group is allowed to do. The two interact through folder permissions: “the payments team is Editor on the folder /Payments.”
Server Admin (install-wide)
|
+-- Org 1
| |
| +-- Org Admin (full org)
| +-- Team: platform
| | +-- Editor on /Infrastructure
| | +-- Viewer on /Payments
| +-- Team: payments
| +-- Editor on /Payments
| +-- Viewer on /Infrastructure
+-- Org 2
+-- ...
Why a sysadmin cares
RBAC mistakes in Grafana are not loud. The wrong role produces dashboards that show the wrong data, alerts that fire under the wrong team, audit log entries attributed to the wrong person, and a security posture that is materially different from what the org chart says it is.
The four production failure shapes:
- The everyone-is-Editor drift. A team ships Grafana with the default admin account having Org Admin, and every new SSO user gets the Editor role by default. Within a year, every engineer can edit every dashboard. The “we have RBAC” claim is technically true; the boundary is effectively zero.
- The folder-permission tax. Folders are the right granularity but they require an explicit grant per resource. A team that does not maintain its folder-permission matrix falls back to org-wide Editor because it is the path of least resistance.
- The team-vs-role confusion. Operators write RBAC rules that grant Editor to a team when they meant to grant Editor to a folder. The team becomes Editor on every folder in the org, including folders that should be read-only to them.
- The HR-drift problem. Team membership is set in the Grafana UI by hand. The IdP is the system of record. When the two diverge — usually after a re-org — Grafana still grants access to people who have left the team.
How it works
Every Grafana request carries a userId resolved from the session cookie or the service-account token. Every Grafana resource (dashboard, folder, data source, alert rule) carries an owner ID and a folder ID. The access check answers three questions in order:
- Is the user a Server Admin? If yes, every operation is permitted.
- Is the user an Org Admin of the org that owns the resource? If yes, every operation within that org is permitted.
- Does the user have a folder-level permission or team-based role that grants the operation on this resource? If yes, the operation is permitted.
The third question is where the operational complexity lives. A team grants its members access to a folder; the folder grants its members access to its children; the children inherit. There is no per-dashboard permission in Grafana 11; permissions live on folders, and dashboards inside a folder inherit from it.
User logs in
|
v
Resolve userId and orgId
|
v
Resource request (e.g. GET /api/dashboards/uid/payments-overview)
|
v
Lookup folder_uid for the dashboard
|
v
Check: org_admin? -- yes -> allow
|
v (no)
Check: server_admin? -- yes -> allow
|
v (no)
Check: folder ACL grants? -- yes -> allow
|
v (no)
Check: team membership + team role on folder?
|
v (no)
403 Forbidden
Team sync is the bridge between the IdP and Grafana teams. With team_sync = true and a team_sync_groups_claim configured under [auth.generic_oauth], every login re-reads the group claim from the IdP token and reconciles the Grafana team membership. A user removed from the IdP group is removed from the Grafana team on next login; a user added is added.
How to configure it
Default roles for new SSO users
# /etc/grafana/grafana.ini
[users]
allow_sign_up = false
# A new SSO user with no matching team lands as Viewer.
auto_assign_org_role = Viewer
auto_assign_org_id = 1
[auth.generic_oauth]
# Reconcile Grafana teams from the IdP `groups` claim.
team_sync = true
team_sync_groups_claim = groups
# Match an IdP group to a Grafana team by name (case-sensitive).
team_sync_group_matchers = "^payments-(.+)$:payments-$1"
Folder provisioning and permissions
Folders are provisioned in YAML under conf/provisioning/access-control/.
# /etc/grafana/provisioning/access-control/folders.yaml
apiVersion: 1
folders:
- uid: payments
title: Payments
permissions:
- team: payments
permission: 1 # Editor
- team: platform
permission: 0 # Viewer
- role: Editor # org-wide Editor does not get this folder
permission: 0
- uid: infrastructure
title: Infrastructure
permissions:
- team: platform
permission: 1
- team: payments
permission: 0
Team provisioning
# /etc/grafana/provisioning/access-control/teams.yaml
apiVersion: 1
teams:
- name: payments
uid: payments
email: payments@example.com
members:
# User identifiers come from the email or login claim configured
# under [auth.generic_oauth] login_attribute_path.
- user: alice@example.com
- user: bob@example.com
preferences:
theme: dark
timezone: browser
- name: platform
uid: platform
email: platform@example.com
members:
- user: ops@example.com
- user: sre-lead@example.com
Org- and team-scoped dashboard provisioning
# /etc/grafana/provisioning/dashboards/payments.yaml
apiVersion: 1
providers:
- name: payments
orgId: 1
folder: payments
folderUid: payments
type: file
disableDeletion: true
updateIntervalSeconds: 30
options:
path: /etc/grafana/dashboards/payments
foldersFromFilesStructure: true
How to validate it
# READ-ONLY: enumerate teams and member counts.
curl -fsS -H "Authorization: Bearer ${GF_SA_TOKEN}" \
https://grafana.example.com/api/teams/search | jq '.teams[] | {name, memberCount}'
# {"name":"platform","memberCount":3}
# {"name":"payments","memberCount":7}
# READ-ONLY: list folder permissions on /Payments.
curl -fsS -H "Authorization: Bearer ${GF_SA_TOKEN}" \
https://grafana.example.com/api/folders/payments/permissions | jq
# [
# {"id":1,"role":"Viewer","teamId":2,"userId":0},
# {"id":2,"role":"Editor","teamId":1,"userId":0}
# ]
# READ-ONLY: an Editor on the payments folder can edit dashboards in it.
SA_TOKEN_FOR_ALICE=$(curl -fsS -X POST .../api/serviceaccounts/.../tokens ...)
curl -fsS -o /dev/null -w "%{http_code}\n" \
-X POST -H "Authorization: Bearer ${SA_TOKEN_FOR_ALICE}" \
-H "Content-Type: application/json" \
-d '{"dashboard":{"title":"Test","uid":"test"},"folderUid":"payments"}' \
https://grafana.example.com/api/dashboards/db
# 200
# READ-ONLY: the same Editor gets 403 on /Infrastructure.
curl -fsS -o /dev/null -w "%{http_code}\n" \
-X POST -H "Authorization: Bearer ${SA_TOKEN_FOR_ALICE}" \
-H "Content-Type: application/json" \
-d '{"dashboard":{"title":"Test","uid":"test"},"folderUid":"infrastructure"}' \
https://grafana.example.com/api/dashboards/db
# 403
# READ-ONLY: confirm team membership matches the IdP group claim.
# Decode the JWT from a recent login and list the `groups` claim.
IDP_JWT=$(...)
echo "${IDP_JWT}" | cut -d. -f2 | base64 -d | jq '.groups'
# ["payments-engineering","oncall-rotation"]
# Compare to /api/teams/search above; team membership must match.
How it can fail
The high-frequency RBAC failure modes from real Grafana installs.
auto_assign_org_role = Editoras a default. New SSO users land with the Editor role for the entire organisation. The symptom is a steady drift of dashboards being edited by people who never owned them, and an audit log full of edits from engineers who are not in the team that owns the folder.- Folder permission granted by user ID, not team ID. A row in
dashboard_aclwithuser_id = 17, team_id = 0means “user 17 specifically gets Editor.” That user leaving the team does not remove the permission; only deleting the row does. The symptom is access that survives off-boarding. - Team sync claim name mismatch. Grafana’s
team_sync_groups_claimdoes not match what the IdP actually sends (case-sensitive, claim path mismatch). The symptom is “the teams exist but no members were ever added.” - Org Admin deleted a folder by accident. Org Admin permission includes folder deletion. The symptom is a 404 on every dashboard under the deleted folder; recovery requires a database restore from backup.
role: Viewerprovisioned with nofolderUid. A permissions row withteamId = 1, permission = 0and no folder scope applies org-wide. The provisioner intended “Viewer on folder X” and got “Viewer org-wide.” The symptom is a team that can browse every dashboard in the org.- Server Admin left the company. The Grafana Server Admin credential is a single account; losing it is losing the install. The break-glass is the database. The symptom is “no one can install plugins or create orgs.”
How to troubleshoot it
The diagnostic order matters: a 403 can come from five different places in the chain.
- Reproduce as the affected user. Use a service-account token scoped to the user or
impersonatevia the Grafana admin API to confirm the user actually sees a 403 and not a UI bug. - Check the user’s org role.
GET /api/org/userslists every user and their org role. If the role is Editor org-wide, the user has access to every folder regardless of folder ACLs. - Inspect the folder ACL.
GET /api/folders/<uid>/permissionsreturns the rows. A missing row means “no permission granted beyond org role.” A wrongteamIdmeans “the right team was not matched.” - Reconcile team membership.
GET /api/teams/<id>/membersagainst the IdP’sgroupsclaim. A mismatch is the source of most off-boarding incidents. - For server-admin issues:
grafana cli adminand direct database access (sqlite3 /var/lib/grafana/grafana.dborpsqlagainst the configured database) are the last-resort paths. - Check the provisioning log.
tail -F /var/log/grafana/grafana.log | grep provisioningshows which file was last loaded and any YAML parse errors. A permissions row in the file that does not parse is silently ignored on the next sync.
Security implications
- Org Admin is the most powerful non-Server role. Anyone with Org Admin can create service-account tokens, install plugins, and read the org audit log. Treat it as the production break-glass and limit it to two named people.
- Team membership is a credential. A user with Editor on a folder can read the secrets stored against the data sources the dashboards in the folder reference. Treat team membership like database grants.
- Folder permissions apply at read time, not at write time. A deleted folder permission does not retroactively revoke already-issued service-account tokens. Rotate service-account tokens when the team membership changes.
- Provisioning YAML is the source of truth. The UI is a read-only view. Operators who edit RBAC by hand and then provision will lose their hand edits at the next sync.
Performance implications
- Folder ACL lookups are indexed. A Grafana with thousands of dashboards and tens of folders pays O(1) per request. With tens of thousands of dashboards, the join cost is still small but appears in Postgres slow-query logs.
- Team sync runs on every login. A user with a large group list produces a
team_memberwrite per group. With a slow database, login latency grows. - Org Admin audit-log queries are unbounded.
GET /api/auditwith no time filter scans the wholeaudittable. Add afromandtoparameter; Grafana enforces a maximum but the default is generous.
Production guidance
- Two named Server Admins; both reachable through break-glass console access.
- One named Org Admin per organisation; a second as on-call backup.
- Folder permissions in YAML; UI is read-only.
- Team sync enabled; the IdP is the source of truth for team membership.
- A service-account token per machine identity; rotate on the same cadence as human credentials.
auto_assign_org_role = Viewer; promotion to Editor happens via team membership.- An audit-log review on a fixed cadence: every team has the folders it expects, every folder has the permissions it expects, every service-account token has an owner and an expiration.
Verification
You should now be able to answer:
- What is the difference between a team and a role in Grafana 11.x?
- Why is granting Editor org-wide via
auto_assign_org_rolea failure shape, and what is the correct alternative? - How does team sync from an external IdP reconcile Grafana team membership, and what does it not cover?
- Why is folder-level permission a better RBAC granularity than org-wide role, and what is the operational cost of using it?
- Where in the request path does a 403 originate, and what are the three checks Grafana runs before denying?
Quiz
Knowledge check · 8 questions
Q1. Which Grafana role grants access to manage users, install plugins, and create organisations install-wide?
Q2. Setting auto_assign_org_role = Editor as the default for new SSO users is a reasonable Grafana production default.
Q3. Which of these are required for team sync to reconcile Grafana team membership from an external IdP?
Q4. A folder ACL row with teamId = 1 and permission = 0 (Viewer) but no folderUid grants the Viewer role at which scope?
Q5. Name the two Grafana tables that store role assignments and folder permissions.
Q6. A junior engineer on the payments team can edit dashboards under /Infrastructure. Which misconfiguration is the most likely cause?
Q7. Removing a user from an IdP group removes their Grafana team membership at the next login.
Q8. Which of these are true about folder permissions in Grafana 11.x?
Passing score: 75%. Answers are checked in this browser.