Secure Grafana with cnspec
Scan Grafana against security and compliance best practices with cnspec.
Scan your Grafana organization to find security risks before they become incidents. cnspec evaluates service account roles and token hygiene, organization admin sprawl, datasource credential and TLS settings, alerting and notification configuration, and other Grafana controls.
Prerequisites
To test your Grafana environment with cnspec, you must have:
- cnspec installed on your workstation
- A Grafana organization (Grafana Cloud or a self-hosted Grafana instance reachable from your workstation)
- A Grafana service account token with permission to read service accounts, users, datasources, and alerting configuration
Give cnspec access using a Grafana service account token
To scan your Grafana environment, cnspec needs access through the Grafana HTTP API. You create a service account token in Grafana and then provide it when running cnspec commands.
To create a service account token:
- Sign in to Grafana as an organization admin.
- Open Administration > Users and access > Service accounts.
- Select Add service account.
- Give the service account a display name (for example,
cnspec-scan) and assign the Viewer role. The Viewer role is enough for cnspec to read the resources it needs to evaluate. - Select Create.
- On the service account details page, select Add service account token.
- Give the token a display name and set an expiration date. Mondoo recommends 90 days or less.
- Select Generate token and copy the value. You will not be able to see it again.
Configure a GRAFANA_TOKEN environment variable
You can supply your service account token to cnspec using the GRAFANA_TOKEN environment variable. This avoids passing the token on the command line with every command.
On Linux / macOS:
export GRAFANA_TOKEN=YOUR_SERVICE_ACCOUNT_TOKENOn Windows, using PowerShell:
$Env:GRAFANA_TOKEN = "YOUR_SERVICE_ACCOUNT_TOKEN"When GRAFANA_TOKEN is set, you can omit the --token flag from all the commands below.
Test your connection
Before running a full scan, verify that your token works by opening a cnspec shell:
cnspec shell grafana --token YOUR_SERVICE_ACCOUNT_TOKEN --url https://myorg.grafana.netcnspec> grafana.organization { name }
grafana.organization: {
name: "Main Org."
}If you see your organization listed, cnspec is connected and ready to scan.
Scan Grafana
To scan your Grafana organization:
cnspec scan grafana --token YOUR_SERVICE_ACCOUNT_TOKEN --url https://myorg.grafana.netUnderstand scan output
When a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy. Each check shows a pass or fail result. For example:
✓ Pass: Ensure service accounts do not use the Admin role
✕ Fail: Ensure all service account tokens have an expiration date
✓ Pass: Ensure datasources use proxy modeAt the end of the output, cnspec shows a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues.
Scan with the Mondoo Grafana Security policy
Mondoo maintains an out of the box Grafana Security policy that checks service account roles and token expiration, organization admin assignments, datasource proxy and TLS configuration, alert receivers, and more.
Mondoo Platform users: Enable the policy in your space. In the Mondoo App, go to Findings > Policies, search for "Grafana", and add the policy. All future scans of your Grafana organization automatically evaluate against it. To learn more, read Manage Policies.
Open source users: Pass the policy bundle URL directly to cnspec:
cnspec scan grafana --token YOUR_SERVICE_ACCOUNT_TOKEN --url https://myorg.grafana.net \
--policy-bundle https://raw.githubusercontent.com/mondoohq/cnspec/refs/heads/main/content/mondoo-grafana-security.mql.yamlYou can also create your own policies to meet your specific requirements.
Explore your Grafana environment
Run cnspec shell grafana --token YOUR_SERVICE_ACCOUNT_TOKEN --url https://myorg.grafana.net to open the interactive shell.
List service accounts
cnspec> grafana.serviceAccounts { name role isDisabled }
grafana.serviceAccounts: [
0: { name: "cnspec-scan" role: "Viewer" isDisabled: false }
...
]List service account tokens
cnspec> grafana.serviceAccounts[0].tokens { name hasExpiration isExpired }
grafana.serviceAccounts[0].tokens: [
0: { name: "rotated-token" hasExpiration: true isExpired: false }
...
]List users in the organization
cnspec> grafana.users { login email role isAdmin }List datasources
cnspec> grafana.datasources { name type access basicAuth jsonData }List notification contact points
cnspec> grafana.alerting.contactPoints { name type settings }Example security checks
From the cnspec interactive shell, you can make checks like the examples below.
Ensure service accounts do not use the Admin role
This check ensures that no service account is assigned the Admin role:
cnspec> grafana.serviceAccounts.none(role == "Admin")
[ok] value: trueEnsure service account tokens have an expiration date
Tokens without an expiration persist indefinitely and remain valid until manually revoked. This check requires every token to have one set:
cnspec> grafana.serviceAccounts.all(tokens.all(hasExpiration == true))
[ok] value: trueEnsure no expired service account tokens exist
Expired tokens cannot authenticate, but their presence indicates token rotation is being neglected:
cnspec> grafana.serviceAccounts.all(tokens.none(isExpired == true))
[ok] value: trueEnsure disabled service accounts have no tokens
A disabled service account should not have active tokens that could be re-enabled and reused:
cnspec> grafana.serviceAccounts.where(isDisabled == true).all(tokens.length == 0)
[ok] value: trueEnsure datasources use proxy mode
Proxy-mode datasources route requests through Grafana, so credentials never leave the server:
cnspec> grafana.datasources.all(access == "proxy")
[ok] value: trueEnsure datasources do not use basic auth
Basic auth sends credentials with every request and is weaker than token-based authentication:
cnspec> grafana.datasources.all(basicAuth == false)
[ok] value: trueEnsure datasources use TLS
This check verifies that datasources do not skip TLS verification:
cnspec> grafana.datasources.all(jsonData["tlsSkipVerify"] != true)
[ok] value: trueLearn more
-
To learn more about how the MQL query language works, read Write Effective MQL.
-
To learn about all the Grafana resources and properties you can query, read the Mondoo Grafana Resource Pack Reference.