SaaS

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:

  1. Sign in to Grafana as an organization admin.
  2. Open Administration > Users and access > Service accounts.
  3. Select Add service account.
  4. 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.
  5. Select Create.
  6. On the service account details page, select Add service account token.
  7. Give the token a display name and set an expiration date. Mondoo recommends 90 days or less.
  8. 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_TOKEN

On 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.net
cnspec> 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.net

Understand 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 mode

At 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.yaml

You 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: true

Ensure 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: true

Ensure 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: true

Ensure 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: true

Ensure 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: true

Ensure 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: true

Ensure datasources use TLS

This check verifies that datasources do not skip TLS verification:

cnspec> grafana.datasources.all(jsonData["tlsSkipVerify"] != true)
[ok] value: true

Learn more

On this page