mirror of https://github.com/grafana/grafana
commit
777e0c6a98
@ -1,200 +0,0 @@ |
||||
--- |
||||
aliases: |
||||
- ../data-sources/prometheus/ |
||||
- ../features/datasources/prometheus/ |
||||
description: Guide for configuring Prometheus in Grafana |
||||
keywords: |
||||
- grafana |
||||
- prometheus |
||||
- guide |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
menuTitle: Configure Prometheus |
||||
title: Configure the Prometheus data source |
||||
weight: 200 |
||||
refs: |
||||
intro-to-prometheus: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/ |
||||
exemplars: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
configure-data-links-value-variables: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables |
||||
alerting-alert-rules: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/ |
||||
--- |
||||
|
||||
# Configure Prometheus |
||||
|
||||
Grafana ships with built-in support for Prometheus. If you are new to Prometheus the following documentation will help you get started working with Prometheus and Grafana: |
||||
|
||||
- [What is Prometheus?](ref:intro-to-prometheus) |
||||
- [Prometheus data model](https://prometheus.io/docs/concepts/data_model/) |
||||
- [Getting started](https://prometheus.io/docs/prometheus/latest/getting_started/) |
||||
|
||||
## Configure the data source |
||||
|
||||
{{< shared id="add-prom-data-source" >}} |
||||
|
||||
To add the Prometheus data source, complete the following steps: |
||||
|
||||
1. Click **Connections** in the left-side menu. |
||||
1. Under **Connections**, click **Add new connection**. |
||||
1. Enter `Prometheus` in the search bar. |
||||
1. Click **Prometheus data source**. |
||||
1. Click **Add new data source** in the upper right. |
||||
1. Enter a name for the data source. |
||||
|
||||
{{< /shared >}} |
||||
|
||||
You will be taken to the **Settings** tab where you will set up your Prometheus configuration. |
||||
|
||||
## Configuration options |
||||
|
||||
The following is a list of configuration options for Prometheus. |
||||
|
||||
The first option to configure is the name of your connection: |
||||
|
||||
- **Name** - The data source name. This is how you refer to the data source in panels and queries. Examples: prometheus-1, prom-metrics. |
||||
|
||||
- **Default** - Toggle to select as the default name in dashboard panels. When you go to a dashboard panel this will be the default selected data source. |
||||
|
||||
### Connection section |
||||
|
||||
- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}} |
||||
If your Prometheus server is local, use `http://localhost:9090`. If it's on a server within a network, this is the URL with the port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`. |
||||
|
||||
{{< admonition type="note" >}} |
||||
|
||||
If you're running Grafana and Prometheus together in different container environments, each localhost refers to its own container - if the server URL is localhost:9090, that means port 9090 inside the Grafana container, not port 9090 on the host machine. |
||||
|
||||
You should use the IP address of the Prometheus container, or the hostname if you are using Docker Compose. Alternatively, you can consider `http://host.docker.internal:9090`. |
||||
|
||||
{{< /admonition >}} |
||||
|
||||
{{< /shared >}} |
||||
|
||||
### Authentication section |
||||
|
||||
There are several authentication methods you can choose in the Authentication section. |
||||
|
||||
{{% admonition type="note" %}} |
||||
|
||||
Use TLS (Transport Layer Security) for an additional layer of security when working with Prometheus. For information on setting up TLS encryption with Prometheus see [Securing Prometheus API and UI Endpoints Using TLS Encryption](https://prometheus.io/docs/guides/tls-encryption/). You must add TLS settings to your Prometheus configuration file **prior** to setting these options in Grafana. |
||||
|
||||
{{% /admonition %}} |
||||
|
||||
- **Basic authentication** - The most common authentication method. Use your `data source` user name and `data source` password to connect. |
||||
|
||||
- **With credentials** - Toggle on to enable credentials such as cookies or auth headers to be sent with cross-site requests. |
||||
|
||||
- **TLS client authentication** - Toggle on to use client authentication. When enabled, add the `Server name`, `Client cert` and `Client key`. The client provides a certificate that is validated by the server to establish the client's trusted identity. The client key encrypts the data between client and server. |
||||
|
||||
- **With CA cert** - Authenticate with a CA certificate. Follow the instructions of the CA (Certificate Authority) to download the certificate file. |
||||
|
||||
- **Skip TLS verify** - Toggle on to bypass TLS certificate validation. |
||||
|
||||
- **Forward OAuth identity** - Forward the OAuth access token (and also the OIDC ID token if available) of the user querying the data source. |
||||
|
||||
### Custom HTTP headers |
||||
|
||||
- **Header** - Add a custom header. This allows custom headers to be passed based on the needs of your Prometheus instance. |
||||
|
||||
- **Value** - The value of the header. |
||||
|
||||
## Advanced settings |
||||
|
||||
Following are additional configuration options. |
||||
|
||||
### Advanced HTTP settings |
||||
|
||||
- **Allowed cookies** - Specify cookies by name that should be forwarded to the data source. The Grafana proxy deletes all forwarded cookies by default. |
||||
|
||||
- **Timeout** - The HTTP request timeout. This must be in seconds. The default is 30 seconds. |
||||
|
||||
### Alerting |
||||
|
||||
- **Manage alerts via Alerting UI** - Toggle to enable [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed. |
||||
|
||||
{{% admonition type="note" %}} |
||||
|
||||
The **Manage alerts via Alerting UI** toggle is enabled by default. You can change this behavior by setting the [default_manage_alerts_ui_toggle](../../../setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle) option in the Grafana configuration file. |
||||
|
||||
{{% /admonition %}} |
||||
|
||||
### Interval behavior |
||||
|
||||
- **Scrape interval** - Set to the typical scrape and evaluation interval configured in Prometheus. The default is `15s`. |
||||
|
||||
- **Query timeout** - The default is `60s`. |
||||
|
||||
### Query editor |
||||
|
||||
- **Default editor** - Sets a default editor. Options are `Builder` or `Code`. For information on query editor types see [Prometheus query editor](../query-editor/). |
||||
|
||||
- **Disable metrics lookup** - Toggle on to disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with large Prometheus instances. |
||||
|
||||
### Performance |
||||
|
||||
- **Prometheus type** - The type of your Prometheus server. There are four options: `Prometheus`, `Cortex`, `Mimir`, and `Thanos`. |
||||
|
||||
- **Cache level** - The browser caching level for editor queries. There are four options: `Low`, `Medium`, `High`, or `None`. |
||||
|
||||
- **Incremental querying (beta)** - Changes the default behavior of relative queries to always request fresh data from the Prometheus instance. Enable this option to decrease database and network load. |
||||
|
||||
- **Disable recording rules (beta)** - Toggle on to disable the recording rules. Enable this option to improve dashboard performance. |
||||
|
||||
### Other |
||||
|
||||
- **Custom query parameters** - Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. |
||||
|
||||
- **HTTP method** - Use either `POST` or `GET` HTTP method to query your data source. `POST` is the recommended and pre-selected method as it allows bigger queries. Change to `GET` if you have a Prometheus version older than 2.1 or if `POST` requests are restricted in your network. |
||||
|
||||
### Exemplars |
||||
|
||||
Support for exemplars is available only for the Prometheus data source. If this is your first time working with exemplars see [Introduction to exemplars](ref:exemplars). An exemplar is a specific trace representative of measurement taken in a given time interval. |
||||
|
||||
- **Internal link** - Toggle on to enable an internal link. When enabled, reveals the data source selector. Select the backend tracing data store for your exemplar data. |
||||
|
||||
- **URL** - _(Visible if you **disable** `Internal link`)_ Defines the external link's URL trace backend. You can interpolate the value from the field by using the [`${__value.raw}` macro](ref:configure-data-links-value-variables). |
||||
|
||||
- **Data source** - _(Visible if you **enable** `Internal link`)_ The data source the exemplar will navigate to. |
||||
|
||||
- **URL label** - Adds a custom display label to override the value of the `Label name` field. |
||||
|
||||
- **Label name** - The name of the field in the `labels` object used to obtain the traceID property. |
||||
|
||||
- **Remove exemplar link** - Click to remove existing links. |
||||
|
||||
### Troubleshooting |
||||
|
||||
Refer to the following troubleshooting information, as required. |
||||
|
||||
#### Data doesn't appear in Explore metrics |
||||
|
||||
<!-- vale Grafana.Spelling = NO --> |
||||
|
||||
If metric data doesn't appear in Explore after you've successfully tested a connection to a Prometheus data source or sent |
||||
metrics to Grafana Cloud, ensure that you've selected the correct data source in the **Data source** drop-down menu. If |
||||
you've used remote_write to send metrics to Grafana Cloud, the data source name follows the convention |
||||
`grafanacloud-stackname-prom`. |
||||
|
||||
<!-- vale Grafana.Spelling = YES --> |
||||
|
||||
The following image shows the **Data source** field in Explore metrics. |
||||
|
||||
 |
@ -0,0 +1,300 @@ |
||||
--- |
||||
aliases: |
||||
- ../data-sources/prometheus/ |
||||
- ../features/datasources/prometheus/ |
||||
description: Guide for configuring Prometheus in Grafana |
||||
keywords: |
||||
- grafana |
||||
- prometheus |
||||
- guide |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
menuTitle: Configure the Prometheus data source |
||||
title: Configure the Prometheus data source |
||||
weight: 200 |
||||
refs: |
||||
intro-to-prometheus: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/ |
||||
exemplars: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
configure-data-links-value-variables: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables |
||||
alerting-alert-rules: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/ |
||||
add-a-data-source: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source |
||||
prom-query-editor: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor |
||||
default-manage-alerts-ui-toggle: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle |
||||
provision-grafana: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/ |
||||
manage-alerts-toggle: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle |
||||
private-data-source-connect: |
||||
- pattern: /docs/grafana/ |
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ |
||||
configure-pdc: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc |
||||
azure-active-directory: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication |
||||
configure-grafana-configuration-file-location: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location |
||||
--- |
||||
|
||||
# Configure the Prometheus data source |
||||
|
||||
This document provides instructions for configuring the Prometheus data source and explains the available configuration options. Grafana includes built-in support for Prometheus, so you don't need to install a plugin. For general information on adding a data source to Grafana, refer to [Add a data source](ref:add-a-data-source). |
||||
|
||||
## Before you begin |
||||
|
||||
- You must have the `Organization administrator` role to add a data source. Administrators can also configure a data source via YAML files. |
||||
|
||||
- Know which Prometheus-compatible database you are using. |
||||
|
||||
- Familiarize yourself with your Prometheus security configuration and gather any necessary security certificates and client keys. |
||||
|
||||
- Verify that data from Prometheus is being written to your Grafana instance. |
||||
|
||||
## Configure the data source using the UI |
||||
|
||||
{{< shared id="add-prom-data-source" >}} |
||||
|
||||
To add the Prometheus data source, complete the following steps: |
||||
|
||||
1. Click **Connections** in the left-side menu. |
||||
1. Under **Connections**, click **Add new connection**. |
||||
1. Enter `Prometheus` in the search bar. |
||||
1. Click **Prometheus data source**. |
||||
1. Click **Add new data source** in the upper right. |
||||
|
||||
{{< /shared >}} |
||||
|
||||
Grafana takes you to the **Settings** tab where you will set up your Prometheus configuration. |
||||
|
||||
## Configuration options |
||||
|
||||
Following is a list of configuration options for Prometheus. |
||||
|
||||
- **Name** - The data source name. Sets the name you use to refer to the data source in panels and queries. Examples: prometheus-1, prom-metrics. |
||||
- **Default** - Toggle to select as the default name in dashboard panels. When you go to a dashboard panel this will be the default selected data source. |
||||
|
||||
**Connection:** |
||||
|
||||
- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}} |
||||
If Prometheus is running locally, use `http://localhost:9090`. If it's hosted on a networked server, provide the server’s URL along with the port where Prometheus is running. Example: `http://prometheus.example.orgname:9090`. |
||||
|
||||
{{< admonition type="note" >}} |
||||
When running Grafana and Prometheus in separate containers, localhost refers to each container’s own network namespace. This means that `localhost:9090` points to port 9090 inside the Grafana container, not on the host machine. |
||||
|
||||
Use the IP address of the Prometheus container, or the hostname if you are using Docker Compose. Alternatively, you can use `http://host.docker.internal:9090` to reference the host machine. |
||||
{{< /admonition >}} |
||||
|
||||
{{< /shared >}} |
||||
|
||||
**Authentication:** |
||||
|
||||
There are three authentication options for the Prometheus data source. |
||||
|
||||
- **Basic authentication** - The most common authentication method. |
||||
|
||||
- **User** - The username you use to connect to the data source. |
||||
- **Password** - The password you use to connect to the data source. |
||||
|
||||
- **Forward OAuth identity** - Forward the OAuth access token (and also the OIDC ID token if available) of the user querying the data source. |
||||
|
||||
- **No authentication** - Allows access to the data source without any authentication. |
||||
|
||||
**TLS settings:** |
||||
|
||||
{{< admonition type="note" >}} |
||||
Use TLS (Transport Layer Security) for an additional layer of security when working with Prometheus. For information on setting up TLS encryption with Prometheus refer to [Securing Prometheus API and UI Endpoints Using TLS Encryption](https://prometheus.io/docs/guides/tls-encryption/). You must add TLS settings to your Prometheus configuration file **prior** to setting these options in Grafana. |
||||
{{< /admonition >}} |
||||
|
||||
- **Add self-signed certificate** - Check the box to authenticate with a CA certificate. Follow the instructions of the CA (Certificate Authority) to download the certificate file. Required for verifying self-signed TLS certificates. |
||||
- **CA certificate** - Add your certificate. |
||||
- **TLS client authentication** - Check the box to enable TLS client authentication. |
||||
- **Server name** - Add the server name, which is used to verify the hostname on the returned certificate. |
||||
- **Client certificate** - The client certificate is generated from a Certificate Authority or its self-signed. Follow the instructions of the CA (Certificate Authority) to download the certificate file. |
||||
- **Client key** - Add your client key, which can also be generated from a Certificate Authority (CA) or be self-signed. The client key encrypts data between the client and server. |
||||
- **Skip TLS verify** - Toggle on to bypass TLS certificate validation. Skipping TLS certificate validation is not recommended unless absolutely necessary or for testing purposes. |
||||
|
||||
**HTTP headers:** |
||||
|
||||
Pass along additional information and metadata about the request or response. |
||||
|
||||
- **Header** - Add a custom header. This allows custom headers to be passed based on the needs of your Prometheus instance. |
||||
- **Value** - The value of the header. |
||||
|
||||
**Advanced settings:** |
||||
|
||||
Following are optional configuration settings you can configure for more control over your data source. |
||||
|
||||
- **Advanced HTTP settings:** |
||||
- **Allowed cookies** - Specify cookies by name that should be forwarded to the data source. The Grafana proxy deletes all forwarded cookies by default. |
||||
- **Timeout** - The HTTP request timeout, must be in seconds. |
||||
|
||||
**Alerting:** |
||||
|
||||
- **Manage alerts via Alerting UI** -Toggled on by default. This enables [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed. Change this by setting the [`default_manage_alerts_ui_toggle`](ref:manage-alerts-toggle) option in the `grafana.ini` configuration file. |
||||
|
||||
**Interval behavior:** |
||||
|
||||
- **Scrape interval** - Sets the standard scrape and evaluation interval in Prometheus. The default is `15s`. This interval determines how often Prometheus scrapes targets. Set it to match the typical scrape and evaluation interval in your Prometheus configuration file. If you set a higher value than your Prometheus configuration, Grafana will evaluate data at this interval, resulting in fewer data points. |
||||
- **Query timeout** - Sets the Prometheus query timeout. The default is `60s`. Without a timeout, complex or inefficient queries can run indefinitely, consuming CPU and memory resources. |
||||
|
||||
**Query editor:** |
||||
|
||||
- **Default editor** - Sets the default query editor. Options are `Builder` or `Code`. `Builder` mode helps you build queries using a visual interface. `Code` mode is geared for the experienced Prometheus user with prior expertise in PromQL. For more details on editor types refer to [Prometheus query editor](ref:prom-query-editor). You can switch easily code editors in the Query editor UI. |
||||
- **Disable metrics lookup** - Toggle on to disable the metrics chooser and metric and label support in the query field's autocomplete. This can improve performance for large Prometheus instances. |
||||
|
||||
**Performance:** |
||||
|
||||
- **Prometheus type** - Select the type of your Prometheus-compatible database, such as Prometheus, Cortex, Mimir, or Thanos. Changing this setting will save your current configuration. Different database types support different APIs. For example, some allow `regex` matching for label queries to improve performance, while others provide a metadata API. Setting this incorrectly may cause unexpected behavior when querying metrics and labels. Refer to your Prometheus documentation to ensure you select the correct type. |
||||
- **Cache level** - Sets the browser caching level for editor queries. There are four options: `Low`, `Medium`, `High`, or `None`. Higher cache settings are recommended for high cardinality data sources. |
||||
- **Incremental querying (beta)** - Toggle on to enable incremental querying. Enabling this feature changes the default behavior of relative queries. Instead of always requesting fresh data from the Prometheus instance, Grafana will cache query results and only fetch new records. This helps reduce database and network load. |
||||
- **Query overlap window** - If you are using incremental querying, specify a duration (e.g., 10m, 120s, or 0s). The default is `10m`. This is a buffer of time added to incremental queries and this value is added to the duration of each incremental request. |
||||
- **Disable recording rules (beta)** - Toggle on to disable the recording rules. When recording rules are disabled, Grafana won't fetch and parse recording rules from Prometheus, improving dashboard performance by reducing processing overhead.. |
||||
|
||||
**Other settings:** |
||||
|
||||
- **Custom query parameters** - Add custom parameters to the Prometheus query URL, which allow for more control over how queries are executed. Examples: `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be joined using `&`. |
||||
- **HTTP method** - Select either the `POST` or `GET` HTTP method to query your data source. `POST`is recommended and selected by default, as it supports larger queries. Select `GET` if you're using Prometheus version 2.1 or older, or if your network restricts `POST` requests. |
||||
Toggle on |
||||
- **Use series endpoint** - Enabling this option makes Grafana use the series endpoint (/api/v1/series) with the match[] parameter instead of the label values endpoint (/api/v1/label/<label_name>/values). While the label values endpoint is generally more performant, some users may prefer the series endpoint because it supports the `POST` method, whereas the label values endpoint only allows `GET` requests. |
||||
|
||||
**Exemplars:** |
||||
|
||||
Support for exemplars is available only for the Prometheus data source. For more information on exemplars refer to [Introduction to exemplars](ref:exemplars). An exemplar is a trace that represents a specific measurement taken within a given time interval. |
||||
|
||||
Click the **+ sign** to add exemplars. |
||||
|
||||
- **Internal link** - Toggle on to enable an internal link. This will display the data source selector, where you can choose the backend tracing data store for your exemplar data. |
||||
- **URL** - _(Visible if you **disable** `Internal link`)_ Defines the external link's URL trace backend. You can interpolate the value from the field by using the [`${__value.raw}` macro](ref:configure-data-links-value-variables). |
||||
- **Data source** - _(Visible when`Internal link` is enabled.)_ Select the data source that the exemplar will link to from the drop-down. |
||||
- **URL label** - Adds a custom display label to override the value of the `Label name` field. |
||||
- **Label name** - The name of the field in the `labels` object used to obtain the traceID property. |
||||
- **Remove exemplar link** - Click the **X** to remove existing links. |
||||
|
||||
You can add multiple exemplars. |
||||
|
||||
- **Private data source connect** - _Only for Grafana Cloud users._ Private data source connect, or PDC, allows you to establish a private, secured connection between a Grafana Cloud instance, or stack, and data sources secured within a private network. Click the drop-down to locate the URL for PDC. For more information regarding Grafana PDC refer to [Private data source connect (PDC)](ref:private-data-source-connect) and [Configure Grafana private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc) for steps on setting up a PDC connection. |
||||
|
||||
Click **Manage private data source connect** to be taken to your PDC connection page, where you’ll find your PDC configuration details. |
||||
|
||||
After you have configured your Prometheus data source options, click **Save & test** at the bottom to test out your data source connection. |
||||
|
||||
You should see a confirmation dialog box that says: |
||||
|
||||
**Successfully queried the Prometheus API.** |
||||
|
||||
**Next, you can start to visualize data by building a dashboard, or by querying data in the Explore view.** |
||||
|
||||
You can also remove a connection by clicking **Delete**. |
||||
|
||||
## Provision the Prometheus data source |
||||
|
||||
You can define and configure the data source in YAML files as part of the Grafana provisioning system. For more information about provisioning, and for available configuration options, refer to [Provision Grafana](ref:provision-grafana). |
||||
|
||||
{{< admonition type="note" >}} |
||||
After you have provisioned a data source you cannot edit it. |
||||
{{< /admonition >}} |
||||
|
||||
**Example of a Prometheus data source configuration:** |
||||
|
||||
```yaml |
||||
apiVersion: 1 |
||||
|
||||
datasources: |
||||
- name: Prometheus |
||||
type: prometheus |
||||
access: proxy |
||||
url: http://localhost:9090 |
||||
jsonData: |
||||
httpMethod: POST |
||||
manageAlerts: true |
||||
prometheusType: Prometheus |
||||
prometheusVersion: 3.3.0 |
||||
cacheLevel: 'High' |
||||
disableRecordingRules: false |
||||
incrementalQueryOverlapWindow: 10m |
||||
exemplarTraceIdDestinations: |
||||
# Field with internal link pointing to data source in Grafana. |
||||
# datasourceUid value can be anything, but it should be unique across all defined data source uids. |
||||
- datasourceUid: my_jaeger_uid |
||||
name: traceID |
||||
|
||||
# Field with external link. |
||||
- name: traceID |
||||
url: 'http://localhost:3000/explore?orgId=1&left=%5B%22now-1h%22,%22now%22,%22Jaeger%22,%7B%22query%22:%22$${__value.raw}%22%7D%5D' |
||||
``` |
||||
|
||||
## Azure authentication settings |
||||
|
||||
The Prometheus data source works with Azure authentication. To configure Azure authentication refer to [Configure Azure Active Directory (AD) authentication](ref:azure-active-directory). |
||||
|
||||
In Grafana Enterprise, you need to update the .ini configuration file. Refer to [Configuration file location](ref:configure-grafana-configuration-file-location) to locate your .ini file. |
||||
|
||||
Add the following setting in the **[auth]** section of the .ini configuration file: |
||||
|
||||
```bash |
||||
[auth] |
||||
azure_auth_enabled = true |
||||
``` |
||||
|
||||
{{% admonition type="note" %}} |
||||
If you are using Azure authentication, don't enable `Forward OAuth identity`. Both methods use the same HTTP authorization headers, and the OAuth token will override your Azure credentials. |
||||
{{% /admonition %}} |
||||
|
||||
## Recording rules (beta) |
||||
|
||||
You can configure the Prometheus data source to disable recording rules in the data source configuration or provisioning file under `disableRecordingRules` in jsonData. |
||||
|
||||
## Troubleshooting configuration issues |
||||
|
||||
Refer to the following troubleshooting information as needed. |
||||
|
||||
**Data doesn't appear in Metrics Drilldown:** |
||||
|
||||
If you have successfully tested the connection to a Prometheus data source or are sending metrics to Grafana Cloud and there is no metric data appearing in Explore, make sure you've selected the correct data source from the data source drop-down menu. When using `remote_write` to send metrics to Grafana Cloud, the data source name follows the convention `grafanacloud-stackname-prom`. |
@ -0,0 +1,238 @@ |
||||
--- |
||||
aliases: |
||||
- ../../data-sources/prometheus/query-editor/ |
||||
description: Guide for using the Prometheus data source's query editor |
||||
keywords: |
||||
- grafana |
||||
- prometheus |
||||
- logs |
||||
- queries |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
menuTitle: Prometheus query editor |
||||
title: Prometheus query editor |
||||
weight: 300 |
||||
refs: |
||||
query-transform-data: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/ |
||||
table: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/table/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/table/ |
||||
exemplars: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
heatmap: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/heatmap/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/heatmap/ |
||||
time-series-transform: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/time-series/#transform |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/time-series/#transform |
||||
explore: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/explore/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/explore/ |
||||
--- |
||||
|
||||
# Prometheus query editor |
||||
|
||||
Grafana provides a query editor for the Prometheus data source to create queries in PromQL. For more information about PromQL, see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). The Prometheus query editor is located on the [Explore page](ref:explore). You can also access the PostgreSQL query editor from a dashboard panel. Click the ellipsis in the upper right of the panel and select **Edit**. |
||||
|
||||
For general documentation on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data). For options and functions common to all query editors, refer to [Query editors](ref:query-transform-data). |
||||
|
||||
The Prometheus query editor has two modes: |
||||
|
||||
- [Builder mode](#builder-mode) |
||||
- [Code mode](#code-mode) |
||||
|
||||
 |
||||
|
||||
Grafana synchronizes both modes, allowing you to switch between them. Grafana also displays a warning message if it detects an issue with the query while switching modes. |
||||
|
||||
You can configure Prometheus-specific options in the query editor by setting several options regardless of mode. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/options.png" max-width="500px" class="docs-image--no-shadow" caption="Options" >}} |
||||
|
||||
## Builder mode |
||||
|
||||
**Builder mode** helps you build queries using a visual interface. This option is best for users who have limited experience working with Prometheus and PromQL. |
||||
|
||||
The following video demonstrates how to use the visual Prometheus query builder: |
||||
|
||||
{{< vimeo 720004179 >}} |
||||
|
||||
Builder mode contains the following components: |
||||
|
||||
- **Kick start your query** - Click to view a list of predefined operation patterns that help you quickly build queries with multiple operations. These include: |
||||
|
||||
- Rate query starters |
||||
- Histogram query starters |
||||
- Binary query starters |
||||
|
||||
Click the arrow next to each to see the available options to add to your query. |
||||
|
||||
- **Explain** - Toggle on to display a step-by-step explanation of all query components and operations. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/explain-results.png" max-width="500px" class="docs-image--no-shadow" caption="Explain results" >}} |
||||
|
||||
- **Builder/Code** - Click the corresponding **Builder** or **Code** tab on the toolbar to select an editor mode. |
||||
|
||||
If you select Builder mode you will see the following options: |
||||
|
||||
- **Metric** - Select a metric from the drop-down. Click the icon to open Metrics explorer, where you can search for metrics by name and filter by type if your instance has a large number of metrics. Refer to [Metrics explorer](#metrics-explorer) for more detail on using this feature. |
||||
- **Label filters** - Select label filters from the drop-down. Select an operator and a value. |
||||
Select desired labels and their values from the drop-down list. |
||||
When a metric is selected, the data source requests available labels and their values from the server. |
||||
Use the `+` button to add a label, and the `x` button to remove a label. |
||||
|
||||
Click **+ Operations** to select from a list of operations including Aggregations, Range functions, Functions, Binary operations, Trigonometric and Time functions. You can select multiple operations. Refer to [Operations](#operations) for more detail. |
||||
|
||||
**Options:** |
||||
|
||||
- **Legend**- Lets you customize the name for the time series. You can use a predefined or custom format. |
||||
|
||||
- **Auto** - Displays unique labels. Also displays all overlapping labels if a series has multiple labels. |
||||
- **Verbose** - Displays all label names. |
||||
- **Custom** - Lets you customize the legend using label templates. For example, `{{hostname}}` is replaced with the value of the `hostname` label. To switch to a different legend mode, clear the input and click outside the field. |
||||
|
||||
- **Min step** - Sets the minimum interval between data points returned by the query. For example, setting this to `1h` suggests that data is collected or displayed at hourly intervals. This setting supports the `$__interval` and `$__rate_interval` macros. Note that the time range of the query is aligned to this step size, which may adjust the actual start and end times of the returned data. |
||||
|
||||
- **Format** - Determines how the data from your Prometheus query is interpreted and visualized in a panel. Choose from the following format options: |
||||
|
||||
- **Time series** - The default format. Refer to [Time series kind formats](https://grafana.com/developers/dataplane/timeseries/) for information on time series data frames and how time and value fields are structured. |
||||
- **Table** - Displays data in table format. This format works only in a [Table panel](ref:table). |
||||
- **Heatmap** - Displays Histogram-type metrics in a [Heatmap panel](ref:heatmap) by converting cumulative histograms to regular ones and sorting the series by the bucket bound. Converts cumulative histogram data into regular histogram format and sorts the series by bucket boundaries for proper display. |
||||
|
||||
- **Type** - This setting determines the query type. These include: |
||||
- **Both** - The default option. Returns results for both a **Range** query and an **Instant** query. |
||||
- **Range** - Returns a range vector - a set of time series |
||||
a set of time series where each series includes multiple data points over a period of time. You can choose to visualize the data as lines, bars, points, stacked lines, or stacked bars. |
||||
- **Instant** - Returns a single data point per series — the most recent value within the selected time range. The results can be displayed in a table or as raw data. To visualize instant query results in a time series panel, start by adding field override, then add a property to the override called `Transform`, and set the Transform value to `Constant` in the drop-down. For more information, refer to the [Time Series Transform option documentation](ref:time-series-transform). |
||||
|
||||
{{% admonition type="note" %}} |
||||
Grafana adjusts the query time range to align with the dynamically calculated step interval. This alignment ensures consistent metric visualization and supports Prometheus's result caching requirements. However, this alignment can cause minor visual differences, such as a slight gap at the graph's right edge or a shifted start time. For example, a `15s` step aligns timestamps to Unix times divisible by 15 seconds. A `1w` `minstep` aligns the range to the start of the week, which for Prometheus is Thursday at 00:00 UTC. |
||||
{{% /admonition %}} |
||||
|
||||
- **Exemplars** - Toggle on to run a query that includes exemplars in the graph. Exemplars are unique to Prometheus. For more information see [Introduction to exemplars](ref:exemplars). |
||||
|
||||
{{% admonition type="note" %}} |
||||
There is no option to add exemplars with an **Instant** query type. |
||||
{{% /admonition %}} |
||||
|
||||
### Filter metrics |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/metrics-and-labels.png" max-width="500px" class="docs-image--no-shadow" caption="Metric and label filters" >}} |
||||
|
||||
When you are ready to create a query, you can choose the specific metric name from the drop-down list under **Metric**. |
||||
The data source provides the list of available metrics based on the selected time range. |
||||
You can also enter text into the selector when the drop-down is open to search and filter the list. |
||||
|
||||
#### Metrics explorer in Builder mode |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/screenshot-grafana-prometheus-metrics-explorer-2.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics explorer" >}} |
||||
|
||||
Click the **Open book icon** to open the Metrics explorer, where you can search for and filter all the metrics in your instance. |
||||
|
||||
If you would like to explore your metrics in the query builder further, you can open the **Metrics explorer** by clicking the first option in the metric select component of the query builder. |
||||
|
||||
The Metrics explorer displays all metrics in a paginated table list. The list shows the total number of metrics, as well as the name, type, and description for each metric. You can enter text into the search input to filter results. |
||||
You can also filter by type. |
||||
|
||||
The following options are included under the **Additional Settings** drop-down: |
||||
|
||||
- **Include description in search**: Toggle on to search by both name and description. |
||||
- **Include results with no metadata**: Toggle on to include metrics that lack type or description metadata. |
||||
- **Disable text wrap**: Toggle on to disable text wrapping. |
||||
- **Enable regex search**: Toggle on to filter metric names by regex search, which uses an additional call on the Prometheus API. |
||||
|
||||
{{% admonition type="note" %}} |
||||
The Metrics explorer (Builder mode) and [Metrics browser (Code mode)](#metrics-browser-in-code-mode) are separate elements. The Metrics explorer does not have the ability to browse labels yet, but the Metrics browser can display all labels on a metric name. |
||||
{{% /admonition %}} |
||||
|
||||
### Operations |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/operations.png" max-width="500px" class="docs-image--no-shadow" caption="Operations" >}} |
||||
|
||||
Select the **+ Operations** button to add operations to your query. |
||||
|
||||
The query editor groups operations into the following sections: |
||||
|
||||
- Aggregations - for additional information see [Aggregation operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators). |
||||
- Range functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
- Functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
- Binary operations - for additional information see [Binary operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operators). |
||||
- Trigonometric - for additional information see [Trigonometric functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#trigonometric-functions). |
||||
- Time functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
|
||||
All operations have function parameters under the operation header. Click the `operator` to see a full list of supported functions. Some operations allow you to apply specific labels to functions. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/use-function-by-label-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Functions and labels" >}} |
||||
|
||||
Some operations are only valid when used in a specific order. If you add an operation in a way that would create an invalid or illogical query, the query editor automatically places it in the correct position to maintain a valid query structure. |
||||
|
||||
### Hints |
||||
|
||||
The query editor can detect which operations are most appropriate for certain selected metrics. |
||||
When it does, it displays a hint next to the **+ Operations** button. |
||||
|
||||
To add the operation to your query, click the **Hint**. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/hint-example.png" max-width="500px" class="docs-image--no-shadow" caption="Hint" >}} |
||||
|
||||
When you are satisfied with your query, click **Run query**. |
||||
|
||||
## Code mode |
||||
|
||||
**Code mode** is for the experienced Prometheus user with prior expertise in PromQL, Prometheus' query language. The Code mode editor allows you to create queries just as you would in Prometheus. For more information about PromQL see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/code-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Code mode" >}} |
||||
|
||||
The user interface (UI) also lets you select metrics, labels, filters, and operations. |
||||
|
||||
You can write complex queries using the text editor with autocompletion features and syntax highlighting. Code mode's autocomplete feature works automatically while typing. The query editor can autocomplete static functions, aggregations, keywords, and also dynamic items like metrics and labels. The autocompletion drop-down includes documentation for the suggested items where available. |
||||
|
||||
It also contains a [Metrics browser](#metrics-browser-in-code-mode) to further help you write queries. To open the Metrics browser, click the arrow next to **Metrics browser**. |
||||
|
||||
### Metrics browser in Code mode |
||||
|
||||
The Metrics browser locates metrics and selects relevant labels to help you build basic queries. |
||||
When you click **Metrics browser** in `Code` mode, it displays all available metrics and labels. |
||||
If supported by your Prometheus instance, each metric also displays its `HELP` and `TYPE` as a tooltip. |
||||
|
||||
{{< figure alt="Prometheus query editor metrics browser" src="/media/docs/prometheus/Metrics-browser-V10-prom-query-editor.png" caption="Metrics browser" >}} |
||||
|
||||
When you select a metric under **Step 1**, the browser narrows down the available labels to show only the ones applicable to the metric. |
||||
You can then select one or more labels shown in **Step 2**. |
||||
Select one or more values in **Step 3** for each label to tighten your query scope. |
||||
In **Step 4**, you can select **Use query** to run the query, **Use as rate query** to add the rate operation to your query (`$__rate_interval`), **Validate selector** to verify the selector is valid and show the number of series found, or **Clear** to clear your selections and start over. |
||||
|
||||
{{% admonition type="note" %}} |
||||
If you don't remember the exact metric name, you can start by selecting a few labels to filter the list. This helps you find relevant label values and narrow down your options. |
||||
{{% /admonition %}} |
||||
|
||||
All lists in the Metrics browser include a search field to quickly filter metrics or labels by keyword. |
||||
In the **Values** section, there's a single search field that filters across all selected labels, making it easier to find matching values. For example, if you have labels like `app`, `job`, and `job_name`, only one of them might contain the value you're looking for. |
||||
|
||||
When you are satisfied with your query, click **Run query**. |
||||
|
||||
## Incremental dashboard queries (beta) |
||||
|
||||
Starting with Grafana v10, the Prometheus data source supports incremental querying for live dashboards. Instead of re-querying the entire time range on each refresh, Grafana can fetch only new data since the last query. |
||||
|
||||
You can enable or disable this feature in the data source configuration or provisioning file using the `incrementalQuerying` field in `jsonData`. |
||||
|
||||
You can also control the overlap between consecutive incremental queries using the `incrementalQueryOverlapWindow` field in `jsonData`. By default, this is set to `10m` (10 minutes). Increasing the `incrementalQueryOverlapWindow` value increases the time range covered by each incremental query. This can help in environments where the most recent data may be delayed or incomplete. |
@ -1,255 +0,0 @@ |
||||
--- |
||||
aliases: |
||||
- ../../data-sources/prometheus/query-editor/ |
||||
description: Guide for using the Prometheus data source's query editor |
||||
keywords: |
||||
- grafana |
||||
- prometheus |
||||
- logs |
||||
- queries |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
menuTitle: Query editor |
||||
title: Prometheus query editor |
||||
weight: 300 |
||||
refs: |
||||
query-transform-data: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/ |
||||
table: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/table/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/table/ |
||||
exemplars: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/ |
||||
heatmap: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/heatmap/ |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/heatmap/ |
||||
time-series-transform: |
||||
- pattern: /docs/grafana/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/time-series/#transform |
||||
- pattern: /docs/grafana-cloud/ |
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/time-series/#transform |
||||
--- |
||||
|
||||
# Prometheus query editor |
||||
|
||||
Grafana provides a query editor for the Prometheus data source to create queries in PromQL. For more information about PromQL, see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). |
||||
|
||||
For general documentation on querying data sources in Grafana, see [Query and transform data](ref:query-transform-data). |
||||
|
||||
For options and functions common to all query editors, see [Query editors](ref:query-transform-data). |
||||
|
||||
## Choose a query editing mode |
||||
|
||||
The Prometheus query editor has two modes: |
||||
|
||||
- [Builder mode](#builder-mode) |
||||
- [Code mode](#code-mode) |
||||
|
||||
Each mode is explained in greater detail below. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/editing-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Query editor mode" >}} |
||||
|
||||
Both modes are synchronized, so you can switch between them. However, if there is an issue with the query while switching modes, a warning message will appear. |
||||
|
||||
## Toolbar elements |
||||
|
||||
The query editor toolbar contains the following elements: |
||||
|
||||
- **Kick start your query** - Click to see a list of operation patterns that help you quickly get started adding multiple operations to your query. These include: |
||||
|
||||
- Rate query starters |
||||
- Histogram query starters |
||||
- Binary query starters |
||||
|
||||
Click the arrow next to each to see available options to add to your query. |
||||
|
||||
- **Explain** - Toggle to display a step-by-step explanation of all query components and operations. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/explain-results.png" max-width="500px" class="docs-image--no-shadow" caption="Explain results" >}} |
||||
|
||||
- **Builder/Code** - Click the corresponding **Builder** or **Code** tab on the toolbar to select a editor mode. |
||||
|
||||
## Configure common options |
||||
|
||||
You can configure Prometheus-specific options in the query editor by setting several options regardless of mode. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/options.png" max-width="500px" class="docs-image--no-shadow" caption="Options" >}} |
||||
|
||||
### Legend |
||||
|
||||
The **Legend** setting defines the time series's name. You can use a predefined or custom format. |
||||
|
||||
- **Auto** - Displays unique labels. Also displays all overlapping labels if a series has multiple labels. |
||||
- **Verbose** - Displays all label names. |
||||
- **Custom** - Uses templating to select which labels will be included. For example, `{{hostname}}` is replaced by the label value for the label `hostname`. Clear the input and click outside of it to select another mode. |
||||
|
||||
### Min step |
||||
|
||||
The **Min step** setting defines the lower bounds on the interval between data points. |
||||
For example, set this to `1h` to hint that measurements are taken hourly. |
||||
This setting supports the `$__interval` and `$__rate_interval` macros. Be aware that the query range dates are aligned to the step and this can change the start and end of the range. |
||||
|
||||
### Format |
||||
|
||||
Switch between the following format options: |
||||
|
||||
- **Time series** - The default time series format. See [Time series kind formats](https://grafana.com/developers/dataplane/timeseries/) for information on time series data frames and how time and value fields are structured. |
||||
- **Table** - This works only in a [Table panel](ref:table). |
||||
- **Heatmap** - Displays metrics of the Histogram type on a [Heatmap panel](ref:heatmap) by converting cumulative histograms to regular ones and sorting the series by the bucket bound. |
||||
|
||||
### Type |
||||
|
||||
The **Type** setting sets the query type. These include: |
||||
|
||||
- **Both** - The default option. Returns results for both a **Range** query and an **Instant** query. |
||||
- **Range** - Returns a range vector consisting of a set of time series data containing a range of data points over time for each time series. You can choose lines, bars, points, stacked lines or stacked bars |
||||
- **Instant** - Returns one data point per query and only the most recent point in the time range provided. The results can be shown in table format or as raw data. To depict instant query results in the time series panel, first add a field override, next add a property to the override named `Transform`, and finally select `Constant` from the **Transform** dropdown. |
||||
|
||||
For more information, refer to the [Time Series Transform option documentation](ref:time-series-transform). |
||||
|
||||
{{% admonition type="note" %}} |
||||
Grafana modifies the request dates for queries to align them with the dynamically calculated step. |
||||
This ensures a consistent display of metrics data and Prometheus requires this for caching results. But, aligning the range with the step can result in a small gap of data at the right edge of a graph or change the start date of the range. For example, a 15s step aligns the range to Unix time divisible by 15s and a 1w minstep aligns the range to the start of the week on a Thursday. |
||||
{{% /admonition %}} |
||||
|
||||
### Exemplars |
||||
|
||||
Toggle **Exemplars** to run a query that includes exemplars in the graph. Exemplars are unique to Prometheus. For more information see [Introduction to exemplars](ref:exemplars). |
||||
|
||||
{{% admonition type="note" %}} |
||||
There is no option to add exemplars with an **Instant** query type. |
||||
{{% /admonition %}} |
||||
|
||||
### Inspector |
||||
|
||||
Click **Inspector** to get detailed statistics regarding your query. Inspector functions as a kind of debugging tool that "inspects" your query. It provides query statistics under **Stats**, request response time under **Query**, data frame details under **{} JSON**, and the shape of your data under **Data**. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/insepctor-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Inspector" >}} |
||||
|
||||
## Builder mode |
||||
|
||||
**Builder mode** helps you build queries using a visual interface. This option is best for users who have limited or no previous experience working with Prometheus and PromQL. |
||||
|
||||
This video demonstrates how to use the visual Prometheus query builder: |
||||
|
||||
{{< vimeo 720004179 >}} |
||||
|
||||
</br> |
||||
|
||||
### Metrics |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/metrics-and-labels.png" max-width="500px" class="docs-image--no-shadow" caption="Metric and label filters" >}} |
||||
|
||||
When you are ready to create a query, you can choose the specific metric name from the dropdown list under **Metric**. |
||||
The data source requests the list of available metrics from the Prometheus server based on the selected time rage. |
||||
You can also enter text into the selector when the dropdown is open to search and filter the list. |
||||
|
||||
#### Metrics explorer |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/screenshot-grafana-prometheus-metrics-explorer-2.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics explorer" >}} |
||||
|
||||
If you would like to explore your metrics in the query builder further, you can open the **Metrics Explorer** by clicking the first option in the metric select component of the query builder. |
||||
|
||||
The metrics explorer is different than the metrics browser. The metrics explorer is only found in the query builder section. The metrics browser is only found in the code editor. The metrics explorer does not have the ability to browse labels yet, but the metrics browser can display all labels on a metric name. |
||||
|
||||
The metrics explorer displays all metrics in a paginated table list. The list shows the total number of metrics, as well as the name, type and description for each metric. You can enter text into the search input to filter results. |
||||
You can also filter by type. |
||||
|
||||
There are also additional settings for the following items: |
||||
|
||||
- Include description in search. Search by name **and** description |
||||
- Include results with no metadata. Many Prometheus metrics have no metadata. This allows users to include metrics with undefined type and description. |
||||
- Disable text wrap. |
||||
- Enable regex search. This uses the Prometheus API to enable regex search for the metric name. |
||||
|
||||
### Label filters |
||||
|
||||
Select desired labels and their values from the dropdown list. |
||||
When a metric is selected, the data source requests available labels and their values from the server. |
||||
Use the `+` button to add a label, and the `x` button to remove a label. |
||||
|
||||
### Operations |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/operations.png" max-width="500px" class="docs-image--no-shadow" caption="Operations" >}} |
||||
|
||||
Select the **+ Operations** button to add operations to your query. |
||||
|
||||
The query editor groups operations into the following sections: |
||||
|
||||
- Aggregations - for additional information see [Aggregation operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators). |
||||
- Range functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
- Functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
- Binary operations - for additional information see [Binary operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operators). |
||||
- Trigonometric - for additional information see [Trigonometric functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#trigonometric-functions). |
||||
- Time functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). |
||||
|
||||
All operations have function parameters under the operation header. Click the `operator` to see a full list of supported functions. Some operations allow you to apply specific labels to functions. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/use-function-by-label-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Functions and labels" >}} |
||||
|
||||
Some operations make sense only when used in a specific order. |
||||
If adding an operation would result in nonsensical query, the query editor adds the operation to the correct place. |
||||
|
||||
#### Hints |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/hint-example.png" max-width="500px" class="docs-image--no-shadow" caption="Hint" >}} |
||||
|
||||
The query editor can detect which operations are most appropriate for some selected metrics. |
||||
If it does, it displays a hint next to the **+ Operations** button. |
||||
|
||||
To add the operation to your query, click the **Hint**. |
||||
|
||||
Once you are satisfied with your query, click **Run query**. |
||||
|
||||
## Code mode |
||||
|
||||
**Code mode** is for the experienced Prometheus user with prior expertise in PromQL, Prometheus' query language. The Code mode editor allows you to create queries just as you would in Prometheus. For more information about PromQL see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/code-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Code mode" >}} |
||||
|
||||
The user interface (UI) also lets you select metrics, labels, filters and operations. |
||||
|
||||
You can write complex queries using the text editor with autocompletion features and syntax highlighting. |
||||
It also contains a [Metrics browser](#metrics-browser) to further help you write queries. |
||||
|
||||
### Use autocomplete |
||||
|
||||
Code mode's autocomplete feature works automatically while typing. The query editor can autocomplete static functions, aggregations, keywords, and also dynamic items like metrics and labels. |
||||
The autocompletion dropdown includes documentation for the suggested items where available. |
||||
|
||||
### Metrics browser |
||||
|
||||
The metrics browser locates metrics and selects relevant labels to help you build basic queries. |
||||
When you click **Metrics browser** in `Code` mode, it displays all available metrics and labels. |
||||
If supported by your Prometheus instance, each metric also displays its `HELP` and `TYPE` as a tooltip. |
||||
|
||||
{{< figure src="/static/img/docs/prometheus/metric-browser.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics browser" >}} |
||||
|
||||
When you select a metric under Step 1, the browser narrows down the available labels to show only the ones applicable to the metric. |
||||
You can then select one or more labels shown in Step 2. |
||||
Select one or more values in Step 3 for each label to tighten your query scope. |
||||
In Step 4, you can select **Use query** to run the query, **Use as rate query** to add the rate operation to your query (`$__rate_interval`), **Validate selector** to verify the selector is valid and show the number of series found, or **Clear** to clear your selections and start over. |
||||
|
||||
{{% admonition type="note" %}} |
||||
If you do not remember a metric name, you can also select a few labels to narrow down the list, then find relevant label values. |
||||
{{% /admonition %}} |
||||
|
||||
All lists in the metrics browser have a search field above them to quickly filter for metrics or labels that match a certain string. |
||||
The values section has only one search field, and its filtering applies to all labels to help you find values across labels once selected. |
||||
|
||||
For example, among your labels `app`, `job`, `job_name` only one might have the value you are looking for. |
||||
|
||||
Once you are satisfied with your query, click **Run query**. |
@ -1,73 +0,0 @@ |
||||
--- |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
title: Metrics Drilldown |
||||
aliases: |
||||
- ../explore-metrics/ # /docs/grafana/latest/explore/explore-metrics/ |
||||
canonical: https://grafana.com/docs/grafana/latest/explore/simplified-exploration/metrics/ |
||||
description: Grafana Metrics Drilldown lets you browse Prometheus-compatible metrics using an intuitive, queryless experience. |
||||
weight: 200 |
||||
--- |
||||
|
||||
# Grafana Metrics Drilldown |
||||
|
||||
Grafana Metrics Drilldown is a query-less experience for browsing **Prometheus-compatible** metrics. Quickly find related metrics with just a few simple clicks, without needing to write PromQL queries to retrieve metrics. |
||||
|
||||
{{< docs/shared source="grafana" lookup="plugins/rename-note.md" version="<GRAFANA_VERSION>" >}} |
||||
|
||||
With Metrics Drilldown, you can: |
||||
|
||||
- Easily segment metrics based on their labels, so you can immediately spot anomalies and identify issues. |
||||
- Automatically display the optimal visualization for each metric type (gauge vs. counter, for example) without manual setup. |
||||
- Uncover related metrics relevant to the one you're viewing. |
||||
- “Explore in a drawer” - overlay additional content on your dashboard without losing your current view. |
||||
- View a history of user steps when navigating through metrics and their filters. |
||||
- Seamlessly pivot to related telemetry, including log data. |
||||
|
||||
{{< docs/play title="Metrics Drilldown" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}} |
||||
|
||||
You can access Metrics Drilldown either as a standalone experience or as part of Grafana dashboards. |
||||
|
||||
## Standalone experience |
||||
|
||||
To access Metrics Drilldown as a standalone experience: |
||||
|
||||
1. Click the arrow next to **Drilldown** in the Grafana left-side menu and click **Metrics**. You are taken to an overview page that shows recent metrics, bookmarks, and the option to select a new metric exploration. |
||||
1. To get started with a new exploration, click **Let's start!**. |
||||
1. Select **Prometheus** or any Prometheus-compatible data source available in the drop-down menu under **Data source**. |
||||
1. Click **+ Add label** to select a label-value pair from the drop-down menu. You can add multiple label-value pairs. A label type appears above the selected label with a drop-down list of options from which to choose. For example, if you select the label `container` a drop-down list of available containers appears. |
||||
1. You can also search for metrics using keywords under **Search metrics** in the search bar. |
||||
1. Use the time picker to select a date and time range from the drop-down menu or use an absolute time range. |
||||
1. Click the down arrow next to the **Refresh** icon to set a refresh rate from the drop-down menu. The default is `Off`. |
||||
|
||||
The **History** button in the upper left corner tracks every step navigating through metric exploration. |
||||
|
||||
 |
||||
|
||||
### Metrics exploration |
||||
|
||||
To further explore a metric, click **Select** in the upper right corner of the metric visualization. |
||||
|
||||
 |
||||
|
||||
- The **Overview** tab provides a description for each metric, as well as the metric `type` and `unit` associated with the metric. It also provides a list of labels associated with the metric. Click on any label to view drill-down visualizations. |
||||
- The **Breakdown** tab depicts time series visualizations for each of the label-value pairs for the selected metric. You can further drill down on each label and click **Add to filter** to add the label/value pair into your filters. You can also change the **View** from grid to rows. |
||||
- The **Related metrics** tab depicts related metrics with relevant key words. You can repeat the drill down process for any related metric. Toggle **Show previews** to preview visualizations. |
||||
|
||||
After you have gathered your metrics exploration data you can: |
||||
|
||||
- Click the **Open in Explore** icon on the right side to open the graph in Explore, where you can modify the query or add the graph to a dashboard or incident. |
||||
- Click the **Copy URL** icon on the right side to copy the metric drill down URL to the clipboard so it can be shared. |
||||
- Click the **Star** icon on the right side to bookmark and save the metrics exploration. |
||||
|
||||
## Dashboard experience |
||||
|
||||
To access Metrics Drilldown via a dashboard: |
||||
|
||||
1. Navigate to your dashboard. |
||||
1. Select a time series panel. |
||||
1. Click the panel menu in the upper right and select **Metrics Drilldown**. If there are multiple metrics, click on the one you want to explore. |
||||
1. You see a slide out drawer with the Metrics Experience, starting with the drill down. You can access the standalone experience by clicking **Open** in the upper right. |
@ -0,0 +1,232 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../auth/saml/ |
||||
- ../../../enterprise/configure-saml/ |
||||
- ../../../enterprise/saml/ |
||||
- ../../../enterprise/saml/about-saml/ |
||||
- ../../../enterprise/saml/configure-saml/ |
||||
- ../../../enterprise/saml/enable-saml/ |
||||
- ../../../enterprise/saml/set-up-saml-with-okta/ |
||||
- ../../../enterprise/saml/troubleshoot-saml/ |
||||
- ../../../saml/ |
||||
description: Learn how to configure SAML authentication in Grafana's configuration |
||||
file. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: SAML |
||||
title: Configure SAML authentication in Grafana |
||||
weight: 500 |
||||
--- |
||||
|
||||
# SAML authentication in Grafana |
||||
|
||||
{{< admonition type="note" >}} |
||||
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). |
||||
{{< /admonition >}} |
||||
|
||||
SAML authentication integration allows your Grafana users to log in by using an external SAML 2.0 Identity Provider (IdP). To enable this, Grafana becomes a Service Provider (SP) in the authentication flow, interacting with the IdP to exchange user information. |
||||
|
||||
You can configure SAML authentication in Grafana through one of the following methods: |
||||
|
||||
- [Configure SAML using Grafana configuration file](#configure-saml-using-the-grafana-config-file) |
||||
- Configure SAML using the [SSO Settings API](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/developers/http_api/sso-settings/) |
||||
- Configure SAML using the [SAML user interface](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/saml-ui/) |
||||
- Configure SAML using the [Grafana Terraform provider](https://registry.terraform.io/providers/grafana/grafana/<GRAFANA_VERSION>/docs/resources/sso_settings) |
||||
|
||||
If you are using Okta or Azure AD as Identity Provider, see the following documentation for configuration: |
||||
|
||||
- [Configure SAML with Azure AD](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-azuread/) |
||||
- [Configure SAML with Okta](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-okta/) |
||||
|
||||
{{< admonition type="note" >}} |
||||
The API and Terraform support are available in Public Preview in Grafana v11.1 behind the `ssoSettingsSAML` feature toggle. You must also enable the `ssoSettingsApi` flag. |
||||
{{< /admonition >}} |
||||
|
||||
All methods offer the same configuration options. However, if you want to keep all of Grafana authentication settings in one place, use the Grafana configuration file or the Terraform provider. If you are a Grafana Cloud user, you do not have access to Grafana configuration file. Instead, configure SAML through the other methods. |
||||
|
||||
{{< admonition type="note" >}} |
||||
Configuration in the API takes precedence over the configuration in the Grafana configuration file. SAML settings from the API will override any SAML configuration set in the Grafana configuration file. |
||||
{{< /admonition >}} |
||||
|
||||
## SAML Bindings |
||||
|
||||
Grafana supports the following SAML 2.0 bindings: |
||||
|
||||
- From the Service Provider (SP) to the Identity Provider (IdP): |
||||
|
||||
- `HTTP-POST` binding |
||||
- `HTTP-Redirect` binding |
||||
|
||||
- From the Identity Provider (IdP) to the Service Provider (SP): |
||||
- `HTTP-POST` binding |
||||
|
||||
## Request Initiation |
||||
|
||||
Grafana supports: |
||||
|
||||
- SP-initiated requests |
||||
- IdP-initiated requests |
||||
|
||||
By default, SP-initiated requests are enabled. For instructions on how to enable IdP-initiated logins, see [IdP-initiated Single Sign-On (SSO)](#idp-initiated-single-sign-on-sso). |
||||
|
||||
## Enable SAML authentication in Grafana |
||||
|
||||
To use the SAML integration, in the `auth.saml` section of in the Grafana custom configuration file, set `enabled` to `true`. |
||||
|
||||
Refer to [Configuration](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/) for more information about configuring Grafana. |
||||
|
||||
## Identity provider (IdP) registration |
||||
|
||||
For the SAML integration to work correctly, you need to make the IdP aware of the SP. |
||||
|
||||
The integration provides two key endpoints as part of Grafana: |
||||
|
||||
- The `/saml/metadata` endpoint, which contains the SP metadata. You can either download and upload it manually, or you make the IdP request it directly from the endpoint. Some providers name it Identifier or Entity ID. |
||||
- The `/saml/acs` endpoint, which is intended to receive the ACS (Assertion Customer Service) callback. Some providers name it SSO URL or Reply URL. |
||||
|
||||
## Configure SAML using the Grafana configuration file |
||||
|
||||
1. In the `[auth.saml]` section in the Grafana configuration file, set [`enabled`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#enabled-3) to `true`. |
||||
2. Configure SAML options: |
||||
- Review all [available configuration options](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/) |
||||
- For IdP-specific configuration, refer to: |
||||
- [Configure SAML with Okta](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-okta/) |
||||
- [Configure SAML with Entra ID](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-azuread/) |
||||
3. Save the configuration file and then restart the Grafana server. |
||||
|
||||
When you are finished, the Grafana configuration might look like this example: |
||||
|
||||
```ini |
||||
[server] |
||||
root_url = https://grafana.example.com |
||||
|
||||
[auth.saml] |
||||
enabled = true |
||||
name = My IdP |
||||
auto_login = false |
||||
private_key_path = "/path/to/private_key.pem" |
||||
certificate_path = "/path/to/certificate.cert" |
||||
idp_metadata_url = "https://my-org.okta.com/app/my-application/sso/saml/metadata" |
||||
assertion_attribute_name = DisplayName |
||||
assertion_attribute_login = Login |
||||
assertion_attribute_email = Email |
||||
assertion_attribute_groups = Group |
||||
``` |
||||
|
||||
## Assertion mapping |
||||
|
||||
During the SAML SSO authentication flow, Grafana receives the ACS callback. The callback contains all the relevant information of the user under authentication embedded in the SAML response. Grafana parses the response to create (or update) the user within its internal database. |
||||
|
||||
For Grafana to map the user information, it looks at the individual attributes within the assertion. You can think of these attributes as Key/Value pairs (although, they contain more information than that). |
||||
|
||||
Grafana provides configuration options that let you modify which keys to look at for these values. The data we need to create the user in Grafana is Name, Login handle, and email. |
||||
|
||||
### The `assertion_attribute_name` option |
||||
|
||||
`assertion_attribute_name` is a special assertion mapping that can either be a simple key, indicating a mapping to a single assertion attribute on the SAML response, or a complex template with variables using the `$__saml{<attribute>}` syntax. If this property is misconfigured, Grafana will log an error message on startup and disallow SAML sign-ins. Grafana will also log errors after a login attempt if a variable in the template is missing from the SAML response. |
||||
|
||||
**Examples** |
||||
|
||||
```ini |
||||
#plain string mapping |
||||
assertion_attribute_name = displayName |
||||
``` |
||||
|
||||
```ini |
||||
#template mapping |
||||
assertion_attribute_name = $__saml{firstName} $__saml{lastName} |
||||
``` |
||||
|
||||
## SAML Name ID |
||||
|
||||
The `name_id_format` configuration field specifies the requested format of the NameID element in the SAML assertion. |
||||
|
||||
By default, this is set to `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` and does not need to be specified in the configuration file. |
||||
|
||||
The following list includes valid configuration field values: |
||||
|
||||
| `name_id_format` value in the configuration file or Terraform | `Name identifier format` on the UI | |
||||
| ------------------------------------------------------------- | ---------------------------------- | |
||||
| `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | Default | |
||||
| `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified` | Unspecified | |
||||
| `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` | Email address | |
||||
| `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent` | Persistent | |
||||
| `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | Transient | |
||||
|
||||
## IdP metadata |
||||
|
||||
You also need to define the public part of the IdP for message verification. The SAML IdP metadata XML defines where and how Grafana exchanges user information. |
||||
|
||||
Grafana supports three ways of specifying the IdP metadata. |
||||
|
||||
- Without a suffix `idp_metadata`, Grafana assumes base64-encoded XML file contents. |
||||
- With the `_path` suffix, Grafana assumes a path and attempts to read the file from the file system. |
||||
- With the `_url` suffix, Grafana assumes a URL and attempts to load the metadata from the given location. |
||||
|
||||
## Maximum issue delay |
||||
|
||||
Prevents SAML response replay attacks and internal clock skews between the SP (Grafana) and the IdP. You can set a maximum amount of time between the SP issuing the AuthnRequest and the SP (Grafana) processing it. |
||||
|
||||
The configuration options is specified as a duration, such as `max_issue_delay = 90s` or `max_issue_delay = 1h`. |
||||
|
||||
## Metadata valid duration |
||||
|
||||
SP metadata is likely to expire at some point, perhaps due to a certificate rotation or change of location binding. Grafana allows you to specify for how long the metadata should be valid. Leveraging the `validUntil` field, you can tell consumers until when your metadata is going to be valid. The duration is computed by adding the duration to the current time. |
||||
|
||||
The configuration option is specified as a duration, such as `metadata_valid_duration = 48h`. |
||||
|
||||
## Allow new user sign up |
||||
|
||||
By default, new Grafana users using SAML authentication will have an account created for them automatically. To decouple authentication and account creation and ensure only users with existing accounts can log in with SAML, set the `allow_sign_up` option to false. |
||||
|
||||
## Configure automatic login |
||||
|
||||
Set `auto_login` option to true to attempt login automatically, skipping the login screen. |
||||
This setting is ignored if multiple auth providers are configured to use auto login. |
||||
|
||||
For more information about automatic login behavior and troubleshooting, see [Automatic login](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/#automatic-oauth-login). |
||||
|
||||
``` |
||||
auto_login = true |
||||
``` |
||||
|
||||
## Configure allowed organizations |
||||
|
||||
With the [`allowed_organizations`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#allowed_organizations) option you can specify a list of organizations where the user must be a member of at least one of them to be able to log in to Grafana. |
||||
|
||||
To get the list of user's organizations from SAML attributes, you must configure the `assertion_attribute_org` option. This option specifies which SAML attribute contains the list of organizations the user belongs to. |
||||
|
||||
To put values containing spaces in the list, use the following JSON syntax: |
||||
|
||||
```ini |
||||
allowed_organizations = ["org 1", "second org"] |
||||
``` |
||||
|
||||
## Configuring SAML with HTTP-Post binding |
||||
|
||||
If multiple bindings are supported for SAML Single Sign-On (SSO) by the Identity Provider (IdP), Grafana will use the `HTTP-Redirect` binding by default. If the IdP only supports the `HTTP-Post binding` then updating the `content_security_policy_template` (in case `content_security_policy = true`) and `content_security_policy_report_only_template` (in case `content_security_policy_report_only = true`) might be required to allow Grafana to initiate a POST request to the IdP. These settings are used to define the [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) headers that are sent by Grafana. |
||||
|
||||
To allow Grafana to initiate a POST request to the IdP, update the `content_security_policy_template` and `content_security_policy_report_only_template` settings in the Grafana configuration file and add the identity provider domain to the `form-action` directive. By default, the `form-action` directive is set to `self` which only allows POST requests to the same domain as Grafana. To allow POST requests to the identity provider domain, update the `form-action` directive to include the identity provider domain, for example: `form-action 'self' https://idp.example.com`. |
||||
|
||||
{{< admonition type="note" >}} |
||||
For Grafana Cloud instances, please contact Grafana Support to update the `content_security_policy_template` and `content_security_policy_report_only_template` settings of your Grafana instance. Please provide the metadata URL/file of your IdP. |
||||
{{< /admonition >}} |
||||
|
||||
## IdP-initiated login |
||||
|
||||
By default, Grafana allows only service provider (SP) initiated logins (when the user logs in with SAML via the login page in Grafana). If you want users to log in into Grafana directly from your identity provider (IdP), set the `allow_idp_initiated` configuration option to `true` and configure `relay_state` with the same value specified in the IdP configuration. |
||||
|
||||
IdP-initiated SSO has some security risks, so make sure you understand the risks before enabling this feature. When using IdP-initiated login, Grafana receives unsolicited SAML responses and can't verify that login flow was started by the user. This makes it hard to detect whether SAML message has been stolen or replaced. Because of this, IdP-initiated login is vulnerable to login cross-site request forgery (CSRF) and man in the middle (MITM) attacks. We do not recommend using IdP-initiated login and keeping it disabled whenever possible. |
||||
|
||||
## Advanced configuration |
||||
|
||||
For advanced configuration and troubleshooting, please refer to the one of the following pages: |
||||
|
||||
- [Configure SAML request signing](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-request-signing/) |
||||
- [Configure SAML single logout](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-single-logout/) |
||||
- [Configure Organization mapping](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-org-mapping/) |
||||
- [Configure Role and Team sync](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-team-role-mapping/) |
||||
- [SAML configuration options](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/) |
||||
- [Troubleshooting](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/troubleshoot-saml/) |
@ -0,0 +1,62 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/ |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Configure Organisation mapping for SAML |
||||
title: Configure Organisation mapping for SAML |
||||
weight: 550 |
||||
--- |
||||
|
||||
# Configure organization mapping for SAML |
||||
|
||||
Organization mapping allows you to assign users to particular organization in Grafana depending on attribute value obtained from identity provider. |
||||
|
||||
1. In configuration file, set [`assertion_attribute_org`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_org) to the attribute name you store organization info in. This attribute can be an array if you want a user to be in multiple organizations. |
||||
1. Set [`org_mapping`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#org_mapping) option to the comma-separated list of `Organization:OrgId` pairs to map organization from IdP to Grafana organization specified by ID. If you want users to have different roles in multiple organizations, you can set this option to a comma-separated list of `Organization:OrgId:Role` mappings. |
||||
|
||||
For example, use following configuration to assign users from `Engineering` organization to the Grafana organization with ID `2` as Editor and users from `Sales` - to the org with ID `3` as Admin, based on `Org` assertion attribute value: |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
assertion_attribute_org = Org |
||||
org_mapping = Engineering:2:Editor, Sales:3:Admin |
||||
``` |
||||
|
||||
Starting from Grafana version 11.5, you can use the organization name instead of the organization ID in the `org_mapping` option. Ensure that the organization name you configure matches exactly with the organization name in Grafana, as it is case-sensitive. If the organization name is not found in Grafana, the mapping will be ignored. If the external organization or the organization name contains spaces, use the JSON syntax for the `org_mapping` option: |
||||
|
||||
```ini |
||||
org_mapping = ["Org 1:2:Editor", "ExternalOrg:ACME Corp.:Admin"] |
||||
``` |
||||
|
||||
If one of the mappings contains a `:`, use the JSON syntax and escape the `:` with a backslash: |
||||
|
||||
```ini |
||||
# Assign users from "External:Admin" to the organization with name "ACME Corp" as Admin |
||||
org_mapping = ["External\:Admin:ACME Corp:Admin"] |
||||
``` |
||||
|
||||
For example, to assign users from `Engineering` organization to the Grafana organization with name `ACME Corp` as Editor and users from `Sales` - to the org with id `3` as Admin, based on `Org` assertion attribute value: |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
assertion_attribute_org = Org |
||||
org_mapping = ["Engineering:ACME Corp:Editor", "Sales:3:Admin"] |
||||
``` |
||||
|
||||
You can specify multiple organizations both for the IdP and Grafana: |
||||
|
||||
- `org_mapping = Engineering:2, Sales:2` to map users from `Engineering` and `Sales` to `2` in Grafana. |
||||
- `org_mapping = Engineering:2, Engineering:3` to assign `Engineering` to both `2` and `3` in Grafana. |
||||
|
||||
You can use `*` as the SAML Organization if you want all your users to be in some Grafana organizations with a default role: |
||||
|
||||
- `org_mapping = *:2:Editor` to map all users to the organization which ID is `2` in Grafana as Editors. |
||||
|
||||
You can use `*` as the Grafana organization in the mapping if you want all users from a given SAML Organization to be added to all existing Grafana organizations. |
||||
|
||||
- `org_mapping = Engineering:*` to map users from `Engineering` to all existing Grafana organizations. |
||||
- `org_mapping = Administration:*:Admin` to map users from `Administration` to all existing Grafana organizations as Admins. |
@ -0,0 +1,20 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/ |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Configure SAML single logout |
||||
title: Configure SAML single logout |
||||
weight: 560 |
||||
--- |
||||
|
||||
# Configure SAML Single Logout |
||||
|
||||
The single logout feature allows users to log out from all applications associated with the current IdP session established via SAML SSO. If the `single_logout` option is set to `true` and a user logs out, Grafana requests IdP to end the user session which in turn triggers logout from all other applications the user is logged into using the same IdP session (applications should support single logout). Conversely, if another application connected to the same IdP logs out using single logout, Grafana receives a logout request from IdP and ends the user session. |
||||
|
||||
{{< admonition type="note" >}} |
||||
The improved SLO features, including proper handling of the IdP's SessionIndex, are currently behind the `improvedExternalSessionHandlingSAML` feature toggle. When this feature toggle is enabled, Grafana will correctly handle session-specific logouts. If the feature toggle is not enabled, logging out will end all of the user's sessions. |
||||
{{< /admonition >}} |
@ -0,0 +1,104 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/ |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Configure Role and Team sync for SAML |
||||
title: Configure Role and Team sync for SAML |
||||
weight: 540 |
||||
--- |
||||
|
||||
# Configure team sync for SAML |
||||
|
||||
To use SAML Team sync, set [`assertion_attribute_groups`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_groups) to the attribute name where you store user groups. Then Grafana will use attribute values extracted from SAML assertion to add user into the groups with the same name configured on the External group sync tab. |
||||
|
||||
{{< admonition type="warning" >}} |
||||
Grafana requires the SAML groups attribute to be configured with distinct `AttributeValue` elements for each group. Do not include multiple groups within a single `AttributeValue` delimited by a comma or any other character. Failure to do so will prevent correct group parsing. Example: |
||||
|
||||
```xml |
||||
<saml2:Attribute ...> |
||||
<saml2:AttributeValue ...>admins_group</saml2:AttributeValue> |
||||
<saml2:AttributeValue ...>division_1</saml2:AttributeValue> |
||||
</saml2:Attribute> |
||||
``` |
||||
|
||||
{{< /admonition >}} |
||||
|
||||
{{< admonition type="note" >}} |
||||
Team Sync allows you sync users from SAML to Grafana teams. It does not automatically create teams in Grafana. You need to create teams in Grafana before you can use this feature. |
||||
{{< /admonition >}} |
||||
|
||||
Given the following partial SAML assertion: |
||||
|
||||
```xml |
||||
<saml2:Attribute |
||||
Name="groups" |
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"> |
||||
<saml2:AttributeValue |
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:type="xs:string">admins_group |
||||
</saml2:AttributeValue> |
||||
<saml2:AttributeValue |
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:type="xs:string">division_1 |
||||
</saml2:AttributeValue> |
||||
</saml2:Attribute> |
||||
``` |
||||
|
||||
The configuration would look like this: |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
# ... |
||||
assertion_attribute_groups = groups |
||||
``` |
||||
|
||||
The following `External Group ID`s would be valid for input in the desired team's _External group sync_ tab: |
||||
|
||||
- `admins_group` |
||||
- `division_1` |
||||
|
||||
[Learn more about Team Sync](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-team-sync/) |
||||
|
||||
# Configure role sync for SAML |
||||
|
||||
Role sync allows you to map user roles from an identity provider to Grafana. To enable role sync, configure role attribute and possible values for the Editor, Admin, and Grafana Admin roles. For more information about user roles, refer to [Roles and permissions](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/). |
||||
|
||||
1. In the configuration file, set [`assertion_attribute_role`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_role) option to the attribute name where the role information will be extracted from. |
||||
1. Set the [`role_values_none`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_none) option to the values mapped to the `None` role. |
||||
1. Set the [`role_values_viewer`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_viewer) option to the values mapped to the `Viewer` role. |
||||
1. Set the [`role_values_editor`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_editor) option to the values mapped to the `Editor` role. |
||||
1. Set the [`role_values_admin`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_admin) option to the values mapped to the organization `Admin` role. |
||||
1. Set the [`role_values_grafana_admin`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_grafana_admin) option to the values mapped to the `Grafana Admin` role. |
||||
|
||||
If a user role doesn't match any of configured values, then the role specified by the `auto_assign_org_role` configuration option will be assigned. If the `auto_assign_org_role` field is not set then the user role will default to `Viewer`. |
||||
|
||||
For more information about roles and permissions in Grafana, refer to [Roles and permissions](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/). |
||||
|
||||
Example configuration: |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
assertion_attribute_role = role |
||||
role_values_none = none |
||||
role_values_viewer = external |
||||
role_values_editor = editor, developer |
||||
role_values_admin = admin, operator |
||||
role_values_grafana_admin = superadmin |
||||
``` |
||||
|
||||
**Important**: When role sync is configured, any changes of user roles and organization membership made manually in Grafana will be overwritten on next user login. Assign user organizations and roles in the IdP instead. |
||||
|
||||
If you don't want user organizations and roles to be synchronized with the IdP, you can use the `skip_org_role_sync` configuration option. |
||||
|
||||
Example configuration: |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
skip_org_role_sync = true |
||||
``` |
@ -0,0 +1,126 @@ |
||||
--- |
||||
aliases: |
||||
- ./saml/#set-up-saml-with-azure-ad |
||||
- ../saml/#set-up-saml-with-azure-ad |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Configure SAML with Entra ID |
||||
title: Configure SAML authentication with Entra ID |
||||
weight: 570 |
||||
--- |
||||
|
||||
# Configure SAML with Microsoft Entra ID |
||||
|
||||
Grafana supports user authentication through Microsoft Entra ID. This topic shows you how to configure SAML authentication in Grafana with [Entra ID](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id). |
||||
|
||||
{{< admonition type="note" >}} |
||||
If an Entra ID user belongs to more than 150 groups, a Graph API endpoint is used instead. |
||||
|
||||
Grafana versions 11.1 and below, do not support fetching the groups from the Graph API endpoint. As a result, users with more than 150 groups will not be able to retrieve their groups. Instead, it is recommended that you use the Azure AD connector. |
||||
|
||||
As of Grafana 11.2, the SAML integration offers a mechanism to retrieve user groups from the Graph API. |
||||
|
||||
Related links: |
||||
|
||||
- [Entra ID SAML limitations](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#groups-overage-claim) |
||||
- [Configure a Graph API application in Entra ID](#configure-a-graph-api-application-in-entra-id) |
||||
{{< /admonition >}} |
||||
|
||||
## Before you begin |
||||
|
||||
Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana, refer to [Roles and permissions](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/). |
||||
|
||||
If you have users that belong to more than 150 groups, configure a registered application to provide an Entra ID Graph API to retrieve the groups. Refer to [Setup Entra ID Graph API applications](#configure-a-graph-api-application-in-azure-ad). |
||||
|
||||
## Generate self-signed certificates |
||||
|
||||
Entra ID requires a certificate to verify the SAML requests' signature. You can generate a private key and a self-signed certificate using the following command (the private key used to sign the requests and the certificate contains the public key for verification): |
||||
|
||||
```sh |
||||
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes |
||||
``` |
||||
|
||||
This will generate a `key.pem` and `cert.pem` file that you can use for the `private_key_path` and `certificate_path` configuration options. |
||||
|
||||
## Add Microsoft Entra SAML Toolkit from the gallery |
||||
|
||||
> Taken from https://learn.microsoft.com/en-us/entra/identity/saas-apps/saml-toolkit-tutorial#add-microsoft-entra-saml-toolkit-from-the-gallery |
||||
|
||||
1. Go to the [Azure portal](https://portal.azure.com/#home) and sign in with your Entra ID account. |
||||
1. Search for **Enterprise Applications**. |
||||
1. In the **Enterprise applications** pane, select **New application**. |
||||
1. In the search box, enter **SAML Toolkit**, and then select the **Microsoft Entra SAML Toolkit** from the results panel. |
||||
1. Add a descriptive name and select **Create**. |
||||
|
||||
## Configure the SAML Toolkit application endpoints |
||||
|
||||
In order to validate Entra ID users with Grafana, you need to configure the SAML Toolkit application endpoints by creating a new SAML integration in the Entra ID organization. |
||||
|
||||
> For the following configuration, we will use `https://localhost` as the Grafana URL. Replace it with your Grafana URL. |
||||
|
||||
1. In the **SAML Toolkit application**, select **Set up single sign-on**. |
||||
1. In the **Single sign-on** pane, select **SAML**. |
||||
1. In the Set up **Single Sign-On with SAML** pane, select the pencil icon for **Basic SAML Configuration** to edit the settings. |
||||
1. In the **Basic SAML Configuration** pane, click on the **Edit** button and update the following fields: |
||||
- In the **Identifier (Entity ID)** field, enter `https://localhost/saml/metadata`. |
||||
- In the **Reply URL (Assertion Consumer Service URL)** field, enter `https://localhost/saml/acs`. |
||||
- In the **Sign on URL** field, enter `https://localhost`. |
||||
- In the **Relay State** field, enter `https://localhost`. |
||||
- In the **Logout URL** field, enter `https://localhost/saml/slo`. |
||||
1. Select **Save**. |
||||
1. At the **SAML Certificate** section, copy the **App Federation Metadata Url**. |
||||
- Use this URL in the `idp_metadata_url` field in the `custom.ini` file. |
||||
|
||||
### Generate a client secret |
||||
|
||||
1. In the **Overview** pane, select **Certificates & secrets**. |
||||
1. Select **New client secret**. |
||||
1. In the **Add a client secret** pane, enter a description for the secret. |
||||
1. Set the expiration date for the secret. |
||||
1. Select **Add**. |
||||
1. Copy the value of the secret. This value is used in the `client_secret` field in the [SAML configuration](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/). |
||||
|
||||
## Configure a Graph API application in Entra ID |
||||
|
||||
While an Entra ID tenant can be configured in Grafana via SAML, some additional information is only accessible via the Graph API. To retrieve this information, create a new application in Entra ID and grant it the necessary permissions. |
||||
|
||||
> [Entra ID SAML limitations](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#groups-overage-claim) |
||||
|
||||
> For the following configuration, the URL `https://localhost` will be used as the Grafana URL. Replace it with your Grafana instance URL. |
||||
|
||||
### Create a new App registration |
||||
|
||||
This app registration will be used as a Service Account to retrieve more information about the user from the Entra ID. |
||||
|
||||
1. Go to the [Azure portal](https://portal.azure.com/#home) and sign in with your Entra ID account. |
||||
1. In the left-hand navigation pane, select the Microsoft Entra ID service, and then select **App registrations**. |
||||
1. Click the **New registration** button. |
||||
1. In the **Register an application** pane, enter a name for the application. |
||||
1. In the **Supported account types** section, select the account types that can use the application. |
||||
1. In the **Redirect URI** section, select Web and enter `https://localhost/login/azuread`. |
||||
1. Click the **Register** button. |
||||
|
||||
### Set up permissions for the application |
||||
|
||||
1. In the overview pane, look for **API permissions** section and select **Add a permission**. |
||||
1. In the **Request API permissions** pane, select **Microsoft Graph**, and click **Application permissions**. |
||||
1. In the **Select permissions** pane, under the **GroupMember** section, select **GroupMember.Read.All**. |
||||
1. In the **Select permissions** pane, under the **User** section, select **User.Read.All**. |
||||
1. Click the **Add permissions** button at the bottom of the page. |
||||
1. In the **Request API permissions** pane, select **Microsoft Graph**, and click **Delegated permissions**. |
||||
1. In the **Select permissions** pane, under the **User** section, select **User.Read**. |
||||
1. Click the **Add permissions** button at the bottom of the page. |
||||
1. In the **API permissions** section, select **Grant admin consent for <your-organization>**. |
||||
|
||||
The following table shows what the permissions look like from the Entra ID portal: |
||||
|
||||
| Permissions name | Type | Admin consent required | Status | |
||||
| ---------------- | ----------- | ---------------------- | ------- | |
||||
| `Group.Read.All` | Application | Yes | Granted | |
||||
| `User.Read` | Delegated | No | Granted | |
||||
| `User.Read.All` | Application | Yes | Granted | |
||||
|
||||
{{< figure src="/media/docs/grafana/saml/graph-api-app-permissions.png" caption="Screen shot of the permissions listed in Entra ID for the App registration" >}} |
@ -0,0 +1,51 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/index/ |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Configure SAML with Okta |
||||
title: Configure SAML authentication with Okta |
||||
weight: 580 |
||||
--- |
||||
|
||||
# Configure SAML Okta |
||||
|
||||
Grafana supports user authentication through Okta, which is useful when you want your users to access Grafana using single sign on. This guide will follow you through the steps of configuring SAML authentication in Grafana with [Okta](https://okta.com/). You need to be an admin in your Okta organization to access Admin Console and create SAML integration. You also need permissions to edit Grafana configuration file and restart Grafana server. |
||||
|
||||
## Before you begin |
||||
|
||||
- To configure SAML integration with Okta, create an app integration inside the Okta organization first. [Add app integration in Okta](https://help.okta.com/en/prod/Content/Topics/Apps/apps-overview-add-apps.htm) |
||||
- Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana, refer to [Roles and permissions](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/). |
||||
|
||||
## Set up SAML with Okta |
||||
|
||||
1. Log in to the [Okta portal](https://login.okta.com/). |
||||
1. Go to the Admin Console in your Okta organization by clicking **Admin** in the upper-right corner. If you are in the Developer Console, then click **Developer Console** in the upper-left corner and then click **Classic UI** to switch over to the Admin Console. |
||||
1. In the Admin Console, navigate to **Applications** > **Applications**. |
||||
1. Click **Create App Integration** to start the Application Integration Wizard. |
||||
1. Choose **SAML 2.0** as the **Sign-in method**. |
||||
1. Click **Create**. |
||||
1. On the **General Settings** tab, enter a name for your Grafana integration. You can also upload a logo. |
||||
1. On the **Configure SAML** tab, enter the SAML information related to your Grafana instance: |
||||
|
||||
- In the **Single sign on URL** field, use the `/saml/acs` endpoint URL of your Grafana instance, for example, `https://grafana.example.com/saml/acs`. |
||||
- In the **Audience URI (SP Entity ID)** field, use the `/saml/metadata` endpoint URL, by default it is the `/saml/metadata` endpoint of your Grafana instance (for example `https://example.grafana.com/saml/metadata`). This could be configured differently, but the value here must match the `entity_id` setting of the SAML settings of Grafana. |
||||
- Leave the default values for **Name ID format** and **Application username**. |
||||
{{< admonition type="note" >}} |
||||
If you plan to enable SAML Single Logout, consider setting the **Name ID format** to `EmailAddress` or `Persistent`. This must match the `name_id_format` setting of the Grafana instance. |
||||
{{< /admonition >}} |
||||
- In the **ATTRIBUTE STATEMENTS (OPTIONAL)** section, enter the SAML attributes to be shared with Grafana. The attribute names in Okta need to match exactly what is defined within Grafana, for example: |
||||
|
||||
| Attribute name (in Grafana) | Name and value (in Okta profile) | Grafana configuration (under `auth.saml`) | |
||||
| --------------------------- | ---------------------------------------------------- | ----------------------------------------- | |
||||
| Login | Login - `user.login` | `assertion_attribute_login = Login` | |
||||
| Email | Email - `user.email` | `assertion_attribute_email = Email` | |
||||
| DisplayName | DisplayName - `user.firstName + " " + user.lastName` | `assertion_attribute_name = DisplayName` | |
||||
|
||||
- In the **GROUP ATTRIBUTE STATEMENTS (OPTIONAL)** section, enter a group attribute name (for example, `Group`, ensure it matches the `asssertion_attribute_groups` setting in Grafana) and set filter to `Matches regex .*` to return all user groups. |
||||
|
||||
1. Click **Next**. |
||||
1. On the final Feedback tab, fill out the form and then click **Finish**. |
@ -0,0 +1,113 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/ |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: SAML configuration options |
||||
title: SAML configuration options |
||||
weight: 520 |
||||
--- |
||||
|
||||
# SAML configuration options |
||||
|
||||
This page provides a comprehensive guide to configuring SAML authentication in Grafana. You'll find detailed configuration examples, available settings, and their descriptions to help you set up and customize SAML authentication for your Grafana instance. |
||||
|
||||
The table below describes all SAML configuration options. Continue reading below for details on specific options. Like any other Grafana configuration, you can apply these options as [environment variables](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#override-configuration-with-environment-variables). |
||||
|
||||
| Setting | Required | Description | Default | |
||||
| ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | |
||||
| `enabled` | No | Whether SAML authentication is allowed. | `false` | |
||||
| `name` | No | Name used to refer to the SAML authentication in the Grafana user interface. | `SAML` | |
||||
| `entity_id` | No | The entity ID of the service provider. This is the unique identifier of the service provider. | `https://{Grafana URL}/saml/metadata` | |
||||
| `single_logout` | No | Whether SAML Single Logout is enabled. | `false` | |
||||
| `allow_sign_up` | No | Whether to allow new Grafana user creation through SAML login. If set to `false`, then only existing Grafana users can log in with SAML. | `true` | |
||||
| `auto_login` | No | Whether SAML auto login is enabled. | `false` | |
||||
| `allow_idp_initiated` | No | Whether SAML IdP-initiated login is allowed. | `false` | |
||||
| `certificate` or `certificate_path` | Yes | Base64-encoded string or Path for the SP X.509 certificate. | | |
||||
| `private_key` or `private_key_path` | Yes | Base64-encoded string or Path for the SP private key. | | |
||||
| `signature_algorithm` | No | Signature algorithm used for signing requests to the IdP. Supported values are rsa-sha1, rsa-sha256, rsa-sha512. | | |
||||
| `idp_metadata`, `idp_metadata_path`, or `idp_metadata_url` | Yes | Base64-encoded string, Path or URL for the IdP SAML metadata XML. | | |
||||
| `max_issue_delay` | No | Maximum time allowed between the issuance of an AuthnRequest by the SP and the processing of the Response. | `90s` | |
||||
| `metadata_valid_duration` | No | Duration for which the SP metadata remains valid. | `48h` | |
||||
| `relay_state` | No | Relay state for IdP-initiated login. This should match the relay state configured in the IdP. | | |
||||
| `assertion_attribute_name` | No | Friendly name or name of the attribute within the SAML assertion to use as the user name. Alternatively, this can be a template with variables that match the names of attributes within the SAML assertion. | `displayName` | |
||||
| `assertion_attribute_login` | No | Friendly name or name of the attribute within the SAML assertion to use as the user login handle. | `mail` | |
||||
| `assertion_attribute_email` | No | Friendly name or name of the attribute within the SAML assertion to use as the user email. | `mail` | |
||||
| `assertion_attribute_groups` | No | Friendly name or name of the attribute within the SAML assertion to use as the user groups. | | |
||||
| `assertion_attribute_role` | No | Friendly name or name of the attribute within the SAML assertion to use as the user roles. | | |
||||
| `assertion_attribute_org` | No | Friendly name or name of the attribute within the SAML assertion to use as the user organization | | |
||||
| `allowed_organizations` | No | List of comma- or space-separated organizations. User should be a member of at least one organization to log in. | | |
||||
| `org_mapping` | No | List of comma- or space-separated Organization:OrgId:Role mappings. Organization can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. | | |
||||
| `role_values_none` | No | List of comma- or space-separated roles which will be mapped into the None role. | | |
||||
| `role_values_viewer` | No | List of comma- or space-separated roles which will be mapped into the Viewer role. | | |
||||
| `role_values_editor` | No | List of comma- or space-separated roles which will be mapped into the Editor role. | | |
||||
| `role_values_admin` | No | List of comma- or space-separated roles which will be mapped into the Admin role. | | |
||||
| `role_values_grafana_admin` | No | List of comma- or space-separated roles which will be mapped into the Grafana Admin (Super Admin) role. | | |
||||
| `skip_org_role_sync` | No | Whether to skip organization role synchronization. | `false` | |
||||
| `name_id_format` | No | Specifies the format of the requested NameID element in the SAML AuthnRequest. | `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | |
||||
| `client_id` | No | Client ID of the IdP service application used to retrieve more information about the user from the IdP. (Microsoft Entra ID only) | | |
||||
| `client_secret` | No | Client secret of the IdP service application used to retrieve more information about the user from the IdP. (Microsoft Entra ID only) | | |
||||
| `token_url` | No | URL to retrieve the access token from the IdP. (Microsoft Entra ID only) | | |
||||
| `force_use_graph_api` | No | Whether to use the IdP service application retrieve more information about the user from the IdP. (Microsoft Entra ID only) | `false` | |
||||
|
||||
## Example SAML configuration |
||||
|
||||
```ini |
||||
[auth.saml] |
||||
enabled = true |
||||
auto_login = false |
||||
certificate_path = "/path/to/certificate.cert" |
||||
private_key_path = "/path/to/private_key.pem" |
||||
idp_metadata_path = "/my/metadata.xml" |
||||
max_issue_delay = 90s |
||||
metadata_valid_duration = 48h |
||||
assertion_attribute_name = displayName |
||||
assertion_attribute_login = mail |
||||
assertion_attribute_email = mail |
||||
|
||||
assertion_attribute_groups = Group |
||||
assertion_attribute_role = Role |
||||
assertion_attribute_org = Org |
||||
role_values_viewer = external |
||||
role_values_editor = editor, developer |
||||
role_values_admin = admin, operator |
||||
role_values_grafana_admin = superadmin |
||||
org_mapping = Engineering:2:Editor, Engineering:3:Viewer, Sales:3:Editor, *:1:Editor |
||||
allowed_organizations = Engineering, Sales |
||||
``` |
||||
|
||||
## Example SAML configuration in Terraform |
||||
|
||||
{{< admonition type="note" >}} |
||||
Available in Public Preview in Grafana v11.1 behind the `ssoSettingsSAML` feature toggle. Supported in the Terraform provider since v2.17.0. |
||||
{{< /admonition >}} |
||||
|
||||
```terraform |
||||
resource "grafana_sso_settings" "saml_sso_settings" { |
||||
provider_name = "saml" |
||||
saml_settings { |
||||
name = "SAML" |
||||
auto_login = false |
||||
certificate_path = "/path/to/certificate.cert" |
||||
private_key_path = "/path/to/private_key.pem" |
||||
idp_metadata_path = "/my/metadata.xml" |
||||
max_issue_delay = "90s" |
||||
metadata_valid_duration = "48h" |
||||
assertion_attribute_name = "displayName" |
||||
assertion_attribute_login = "mail" |
||||
assertion_attribute_email = "mail" |
||||
assertion_attribute_groups = "Group" |
||||
assertion_attribute_role = "Role" |
||||
assertion_attribute_org = "Org" |
||||
role_values_editor = "editor, developer" |
||||
role_values_admin = "admin, operator" |
||||
role_values_grafana_admin = "superadmin" |
||||
org_mapping = "Engineering:2:Editor, Engineering:3:Viewer, Sales:3:Editor, *:1:Editor" |
||||
allowed_organizations = "Engineering, Sales" |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Go to [Terraform Registry](https://registry.terraform.io/providers/grafana/grafana/<GRAFANA_VERSION>/docs/resources/sso_settings) for a complete reference on using the `grafana_sso_settings` resource. |
@ -0,0 +1,111 @@ |
||||
--- |
||||
aliases: |
||||
- ../../../../saml/ |
||||
description: Learn how to configure SAML authentication in Grafana's UI. |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
menuTitle: Troubleshooting |
||||
title: Troubleshoot SAML configuration |
||||
weight: 590 |
||||
--- |
||||
|
||||
## Troubleshooting |
||||
|
||||
Following are common issues found in configuring SAML authentication in Grafana and how to resolve them. |
||||
|
||||
### Troubleshoot SAML authentication in Grafana |
||||
|
||||
To troubleshoot and get more log information, enable SAML debug logging in the configuration file. Refer to [Configuration](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#filters) for more information. |
||||
|
||||
```ini |
||||
[log] |
||||
filters = saml.auth:debug |
||||
``` |
||||
|
||||
### Infinite redirect loop / User gets redirected to the login page after successful login on the IdP side |
||||
|
||||
If you experience an infinite redirect loop when `auto_login = true` or redirected to the login page after successful login, it is likely that the `grafana_session` cookie's SameSite setting is set to `Strict`. This setting prevents the `grafana_session` cookie from being sent to Grafana during cross-site requests. To resolve this issue, set the `security.cookie_samesite` option to `Lax` in the Grafana configuration file. |
||||
|
||||
### SAML authentication fails with error: |
||||
|
||||
- `asn1: structure error: tags don't match` |
||||
|
||||
We only support one private key format: PKCS#8. |
||||
|
||||
The keys may be in a different format (PKCS#1 or PKCS#12); in that case, it may be necessary to convert the private key format. |
||||
|
||||
The following command creates a pkcs8 key file. |
||||
|
||||
```bash |
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes |
||||
``` |
||||
|
||||
#### **Convert** the private key format to base64 |
||||
|
||||
The following command converts keys to base64 format. |
||||
|
||||
Base64-encode the cert.pem and key.pem files: |
||||
(-w0 switch is not needed on Mac, only for Linux) |
||||
|
||||
```sh |
||||
$ base64 -w0 key.pem > key.pem.base64 |
||||
$ base64 -w0 cert.pem > cert.pem.base64 |
||||
``` |
||||
|
||||
The base64-encoded values (`key.pem.base64, cert.pem.base64` files) are then used for certificate and `private_key`. |
||||
|
||||
The keys you provide should look like: |
||||
|
||||
``` |
||||
-----BEGIN PRIVATE KEY----- |
||||
... |
||||
... |
||||
-----END PRIVATE KEY----- |
||||
``` |
||||
|
||||
### SAML login attempts fail with request response `origin not allowed` |
||||
|
||||
When the user logs in using SAML and gets presented with `origin not allowed`, the user might be issuing the login from an IdP (identity provider) service or the user is behind a reverse proxy. This potentially happens as the CSRF checks in Grafana deem the requests to be invalid. For more information [CSRF](https://owasp.org/www-community/attacks/csrf). |
||||
|
||||
To solve this issue, you can configure either the [`csrf_trusted_origins`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#csrf_trusted_origins) or [`csrf_additional_headers`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#csrf_additional_headers) option in the SAML configuration. |
||||
|
||||
Example of a configuration file: |
||||
|
||||
```ini |
||||
# config.ini |
||||
... |
||||
[security] |
||||
csrf_trusted_origins = https://grafana.example.com |
||||
csrf_additional_headers = X-Forwarded-Host |
||||
... |
||||
``` |
||||
|
||||
### SAML login attempts fail with request response "login session has expired" |
||||
|
||||
Accessing the Grafana login page from a URL that is not the root URL of the |
||||
Grafana server can cause the instance to return the following error: "login session has expired". |
||||
|
||||
If you are accessing Grafana through a proxy server, ensure that cookies are correctly |
||||
rewritten to the root URL of Grafana. |
||||
Cookies must be set on the same URL as the `root_url` of Grafana. This is normally the reverse proxy's domain/address. |
||||
|
||||
Review the cookie settings in your proxy server configuration to ensure that cookies are |
||||
not being discarded |
||||
|
||||
Review the following settings in your Grafana configuration: |
||||
|
||||
```ini |
||||
[security] |
||||
cookie_samesite = none |
||||
``` |
||||
|
||||
This setting should be set to none to allow Grafana session cookies to work correctly with redirects. |
||||
|
||||
```ini |
||||
[security] |
||||
cookie_secure = true |
||||
``` |
||||
|
||||
Ensure `cookie_secure` is set to true to ensure that cookies are only sent over HTTPS. |
@ -0,0 +1,25 @@ |
||||
--- |
||||
headless: true |
||||
labels: |
||||
products: |
||||
- enterprise |
||||
- oss |
||||
title: Upgrade guide introduction |
||||
--- |
||||
|
||||
We recommend that you upgrade Grafana often to stay current with the latest fixes and enhancements. |
||||
Because Grafana upgrades are backward compatible, the upgrade process is straightforward, and dashboards and graphs will not change. |
||||
|
||||
In addition to common tasks you should complete for all versions of Grafana, there might be additional upgrade tasks to complete for a version. |
||||
|
||||
{{% admonition type="note" %}} |
||||
There might be breaking changes in some releases. We outline all these changes in the [What's New](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/whatsnew/) document. |
||||
{{% /admonition %}} |
||||
|
||||
For versions of Grafana prior to v9.2, we published additional information in the [Release Notes](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/release-notes/). |
||||
|
||||
When available, we list all changes with links to pull requests or issues in the [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). |
||||
|
||||
{{% admonition type="note" %}} |
||||
When possible, we recommend that you test the Grafana upgrade process in a test or development environment. |
||||
{{% /admonition %}} |
@ -0,0 +1,82 @@ |
||||
--- |
||||
description: Guide for upgrading to Grafana v12.0 |
||||
keywords: |
||||
- grafana |
||||
- configuration |
||||
- documentation |
||||
- upgrade |
||||
- '12.0' |
||||
title: Upgrade to Grafana v12.0 |
||||
menuTitle: Upgrade to v12.0 |
||||
weight: 600 |
||||
--- |
||||
|
||||
# Upgrade to Grafana v12.0 |
||||
|
||||
{{< docs/shared lookup="upgrade/intro_2.md" source="grafana" version="<GRAFANA_VERSION>" >}} |
||||
|
||||
{{< docs/shared lookup="back-up/back-up-grafana.md" source="grafana" version="<GRAFANA_VERSION>" leveloffset="+1" >}} |
||||
|
||||
{{< docs/shared lookup="upgrade/upgrade-common-tasks.md" source="grafana" version="<GRAFANA_VERSION>" >}} |
||||
|
||||
## Technical notes |
||||
|
||||
### Grafana data source UID format enforcement |
||||
|
||||
**Ensure that your data source UIDs follow the correct standard** |
||||
|
||||
We've had standard ways to define UIDs for Grafana objects for years (at least [since Grafana v5](https://github.com/grafana/grafana/issues/7883)). While all of our internal code complies with this format, we haven't strictly enforced this format in REST APIs and provisioning paths that allow the creation and update of data sources. |
||||
|
||||
In Grafana v11.1, we [introduced](https://github.com/grafana/grafana/pull/86598) a warning that is sent to Grafana server logs every time a data source instance is created or updated using an invalid UID format. |
||||
|
||||
In Grafana v11.2, we [added](https://github.com/grafana/grafana/pull/89363/files) a new feature flag called `failWrongDSUID` that is turned off by default. When enabled, the REST APIs and provisioning reject any requests to create or update data source instances that have an incorrect UID. |
||||
|
||||
In Grafana v12.0, we're turning the feature flag `failWrongDSUID` on by default. |
||||
|
||||
#### Correct UID format |
||||
|
||||
You can find the exact regex definition [in the `grafana/grafana` repository](https://github.com/grafana/grafana/blob/c92f5169d1c83508beb777f71a93336179fe426e/pkg/util/shortid_generator.go#L32-L45). |
||||
|
||||
A data source UID can only contain: |
||||
|
||||
- Latin characters (`a-Z`) |
||||
- Numbers (`0-9`) |
||||
- Dash symbols (`-`) |
||||
|
||||
#### How do I know if I'm affected? |
||||
|
||||
- You can fetch all your data sources using the `/api/datasources` API. Review the `uid` fields, comparing them to the correct format, as shown [in the docs](https://grafana.com/docs/grafana/latest/developers/http_api/data_source/#get-all-data-sources). The following script can help, but note that it's missing authentication that you [have to add yourself](https://grafana.com/docs/grafana/latest/developers/http_api/#authenticating-api-requests): |
||||
|
||||
``` |
||||
curl http://localhost:3000/api/datasources | jq '.[] | select((.uid | test("^[a-zA-Z0-9\\-_]+$") | not) or (.uid | length > 40)) | {id, uid, name, type}' |
||||
``` |
||||
|
||||
- Alternatively, you can check the server logs for the `Invalid datasource uid` [error](https://github.com/grafana/grafana/blob/68751ed3107c4d15d33f34b15183ee276611785c/pkg/services/datasources/service/store.go#L429). |
||||
|
||||
#### What do I do if I'm affected? |
||||
|
||||
You'll need to create a new data source with the correct UID and update your dashboards and alert rules to use it. |
||||
|
||||
#### How do I update my dashboards to use the new or updated data source? |
||||
|
||||
- Go to the dashboard using the data source and update it by selecting the new or updated data source from the picker below your panel. |
||||
|
||||
OR |
||||
|
||||
- Update the dashboard's JSON model directly using search and replace. |
||||
|
||||
Navigate to [dashboard json model](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/view-dashboard-json-model/) and carefully replace all the instances of the old `uid` with the newly created `uid`. |
||||
|
||||
{{< figure src="/media/docs/grafana/screenshot-grafana-11-datasource-uid-enforcement.png" alt="Updating JSON Model of a Dashboard">}} |
||||
|
||||
#### How do I update my alert rules to use the new or updated data source? |
||||
|
||||
Open the alert rule you want to adjust and search for the data source that is being used for the query/alert condition. From there, select the new data source from the drop-down list and save the alert rule. |
||||
|
||||
### Enforcing stricter version compatibility checks in plugin CLI install commands |
||||
|
||||
Since Grafana 10.2, the endpoint to check compatible versions when installing a plugin using `grafana cli plugins install` changed, which led to Grafana dependency version no longer being taken into account. This might have led to some behavior where the CLI would install plugins that are not fully compatible based on the plugins definition of compatibility via `grafanaDependency` property in the `plugin.json` file. |
||||
|
||||
#### What if I want to ignore the compatibility check? |
||||
|
||||
We _do not_ recommend installing plugins declared as incompatible. However, if you need to force install a plugin despite it being declared as incompatible, refer to the [Installing a plugin from a ZIP](https://grafana.com/docs/grafana/latest/administration/plugin-management/#install-a-plugin-from-a-zip-file) guidance. |
@ -0,0 +1,95 @@ |
||||
--- |
||||
description: Feature and improvement highlights for Grafana v12.0 |
||||
keywords: |
||||
- grafana |
||||
- new |
||||
- documentation |
||||
- '12.0' |
||||
- release notes |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
title: What's new in Grafana v12.0 |
||||
posts: |
||||
- title: Observability as Code |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-14-git-sync-for-grafana-dashboards.md |
||||
- docs/grafana-cloud/whats-new/2025-04-11-new-dashboards-schema.md |
||||
- docs/grafana-cloud/whats-new/2025-05-05-new-dashboard-apis-released-as-experimental.md |
||||
- title: Drilldown apps |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-28-metrics-drilldown-improvements.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-logs-drilldown-improvements.md |
||||
- docs/grafana-cloud/whats-new/2025-04-17-ga-release-of-grafana-traces-drilldown.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-introducing-investigations |
||||
- title: Cloud Migration Assistant |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-11-grafana-cloud-migration-assistant-now-generally-available.md |
||||
- title: Dashboards and visualizations |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-11-dynamic-dashboards.md |
||||
- docs/grafana-cloud/whats-new/2025-04-11-blazing-fast-table-panel.md |
||||
- docs/grafana-cloud/whats-new/2025-04-07-sql-expressions.md |
||||
- title: Authentication and authorization |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-14-scim-user-and-team-provisioning.md |
||||
- title: Alerting |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-10-alert-rule-migration-tool.md |
||||
- docs/grafana-cloud/whats-new/2025-04-10-grafana-managed-alert-rule-recovering-state.md |
||||
- docs/grafana-cloud/whats-new/2025-04-11-grafana-managed-alert-rule-improvements.md |
||||
- title: Experimental themes |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-10-experimental-themes.md |
||||
- title: Explore |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-15-new-controls-for-logs-in-explore.md |
||||
- title: Traces |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-30-trace-correlations-instant-context-hops-from-any-trace.md |
||||
- title: Breaking Changes |
||||
items: |
||||
- docs/grafana-cloud/whats-new/2025-04-28-removal-of-editors_can_admin-configuration.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-dashboard-v2-schema-and-next-gen-dashboards.md |
||||
- docs/grafana-cloud/whats-new/2025-04-29-deduplication-and-renaming-of-metric-cache_size.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-removal-of-optional-actions-property-from-datalinkscontextmenu-component.md |
||||
- docs/grafana-cloud/whats-new/2025-04-29-enforcing-stricter-data-source-uid-format.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-removal-of-angular.md |
||||
- docs/grafana-cloud/whats-new/2025-04-29-deprecated-apis-for-ui-extensions-will-be-removed.md |
||||
- docs/grafana-cloud/whats-new/2025-04-29-enforcing-stricter-version-compatibility-checks-in-plugin-cli-install-commands.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-removal-of-‘aggregate-by’-in-tempo.md |
||||
- docs/grafana-cloud/whats-new/2025-04-28-removing-the-feature-toggle-ui-from-grafana-cloud.md |
||||
whats_new_grafana_version: 12.0 |
||||
weight: -49 |
||||
--- |
||||
|
||||
# What’s new in Grafana v12.0 |
||||
|
||||
Welcome to Grafana 12.0! We have a _lot_ to share. This release marks general availability for Grafana Drilldown (previously Explore Metrics, Logs, and Traces), Grafana-managed alerts and recording rules, Cloud migration, and plugin management tooling. You can also try new [preview and experimental](https://grafana.com/docs/release-life-cycle/) tools: Sync your dashboards directly to a GitHub repository with Git Sync, and try our new Terraform provider and CLI. Add tabs, new layouts and conditional logic to your dashboards, and load tables and geomaps far faster. Join and transform data limitlessly from multiple sources with SQL Expressions. In Grafana Cloud and Enterprise, sync your users and teams instantly from your SAML identity provider using SCIM (the System for Cross-Domain Identity Management). Lastly, don't forget to try on one of several new color themes for the user interface. |
||||
|
||||
Read on to learn about these and more improvements to Grafana! |
||||
|
||||
{{< youtube id=mHSzaVYBh38 >}} |
||||
|
||||
For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). For the specific steps we recommend when you upgrade to v12.0, check out our [Upgrade Guide](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/upgrade-guide/upgrade-v12.0/). |
||||
|
||||
## Breaking changes in Grafana v12.0 |
||||
|
||||
For Grafana v12.0, we've also provided a list of [breaking changes](https://grafana.com/docs/grafana/latest/whatsnew/whats-new-in-v12-0/#breaking-changes) to help you upgrade with greater confidence. For our purposes, a breaking change is any change that requires users or operators to do something. This includes: |
||||
|
||||
- Changes in one part of the system that could cause other components to fail |
||||
- Deprecations or removal of a feature |
||||
- Changes to an API that could break automation |
||||
- Changes that affect some plugins or functions of Grafana |
||||
- Migrations that can’t be rolled back |
||||
|
||||
For each change, the provided information: |
||||
|
||||
- Helps you determine if you’re affected |
||||
- Describes the change or relevant background information |
||||
- Guides you in how to mitigate for the change or migrate |
||||
- Provides more learning resources |
||||
|
||||
{{< docs/whats-new >}} |
@ -0,0 +1,24 @@ |
||||
import { e2e } from '../utils'; |
||||
|
||||
// Common flows for adding/editing variables on the new edit pane
|
||||
export const flows = { |
||||
newEditPaneVariableClick() { |
||||
e2e.components.NavToolbar.editDashboard.editButton().should('be.visible').click(); |
||||
e2e.components.PanelEditor.Outline.section().should('be.visible').click(); |
||||
e2e.components.PanelEditor.Outline.item('Variables').should('be.visible').click(); |
||||
e2e.components.PanelEditor.ElementEditPane.addVariableButton().should('be.visible').click(); |
||||
}, |
||||
newEditPanelCommonVariableInputs(variable: Variable) { |
||||
e2e.components.PanelEditor.ElementEditPane.variableType(variable.type).should('be.visible').click(); |
||||
e2e.components.PanelEditor.ElementEditPane.variableNameInput().clear().type(variable.name).blur(); |
||||
e2e.components.PanelEditor.ElementEditPane.variableLabelInput().clear().type(variable.label).blur(); |
||||
}, |
||||
}; |
||||
|
||||
export type Variable = { |
||||
type: string; |
||||
name: string; |
||||
label?: string; |
||||
description?: string; |
||||
value: string; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
import { FeatureToggles } from '@grafana/data'; |
||||
|
||||
type FeatureToggleName = keyof FeatureToggles; |
||||
|
||||
/** |
||||
* Check a featureToggle |
||||
* @param featureName featureToggle name |
||||
* @param def default value if featureToggles aren't defined, false if not provided |
||||
* @returns featureToggle value or def. |
||||
*/ |
||||
export function getFeatureToggle(featureName: FeatureToggleName, def = false) { |
||||
return window.grafanaBootData?.settings.featureToggles[featureName] ?? def; |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cipher |
||||
|
||||
import ( |
||||
"context" |
||||
) |
||||
|
||||
const ( |
||||
AesCfb = "aes-cfb" |
||||
AesGcm = "aes-gcm" |
||||
) |
||||
|
||||
type Cipher interface { |
||||
Encrypter |
||||
Decrypter |
||||
} |
||||
|
||||
type Encrypter interface { |
||||
Encrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) |
||||
} |
||||
|
||||
type Decrypter interface { |
||||
Decrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) |
||||
} |
||||
|
||||
type Provider interface { |
||||
ProvideCiphers() map[string]Encrypter |
||||
ProvideDeciphers() map[string]Decrypter |
||||
} |
@ -0,0 +1,14 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"crypto/pbkdf2" |
||||
"crypto/sha256" |
||||
) |
||||
|
||||
// aes256CipherKey is used to calculate a key for AES-256 blocks.
|
||||
// It returns a key of 32 bytes, which causes aes.NewCipher to choose AES-256.
|
||||
// The implementation is equal to that of the legacy secrets system.
|
||||
// If this changes, we either need to rotate all encrypted secrets, or keep a fallback implementation (being this).
|
||||
func aes256CipherKey(password string, salt []byte) ([]byte, error) { |
||||
return pbkdf2.Key(sha256.New, password, salt, 10000, 32) |
||||
} |
@ -0,0 +1,44 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"crypto/rand" |
||||
"encoding/hex" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestAes256CipherKey(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
t.Run("with regular password", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
key, err := aes256CipherKey("password", []byte("salt")) |
||||
require.NoError(t, err) |
||||
require.Len(t, key, 32) |
||||
}) |
||||
|
||||
t.Run("with very long password", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
key, err := aes256CipherKey("a very long secret key that is much larger than 32 bytes", []byte("salt")) |
||||
require.NoError(t, err) |
||||
require.Len(t, key, 32) |
||||
}) |
||||
|
||||
t.Run("withstands randomness", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
password := make([]byte, 512) |
||||
salt := make([]byte, 512) |
||||
_, err := rand.Read(password) |
||||
require.NoError(t, err, "failed to generate random password") |
||||
_, err = rand.Read(salt) |
||||
require.NoError(t, err, "failed to generate random salt") |
||||
|
||||
key, err := aes256CipherKey(hex.EncodeToString(password), salt) |
||||
require.NoError(t, err, "failed to generate key") |
||||
require.Len(t, key, 32, "key should be 32 bytes long") |
||||
}) |
||||
} |
@ -0,0 +1,118 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"context" |
||||
"crypto/aes" |
||||
cpr "crypto/cipher" |
||||
"crypto/rand" |
||||
"io" |
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
) |
||||
|
||||
const gcmSaltLength = 8 |
||||
|
||||
var ( |
||||
_ cipher.Encrypter = (*aesGcmCipher)(nil) |
||||
_ cipher.Decrypter = (*aesGcmCipher)(nil) |
||||
) |
||||
|
||||
type aesGcmCipher struct { |
||||
// randReader is used to generate random bytes for the nonce.
|
||||
// This allows us to change out the entropy source for testing.
|
||||
randReader io.Reader |
||||
} |
||||
|
||||
func newAesGcmCipher() aesGcmCipher { |
||||
return aesGcmCipher{ |
||||
randReader: rand.Reader, |
||||
} |
||||
} |
||||
|
||||
func (c aesGcmCipher) Encrypt(_ context.Context, payload []byte, secret string) ([]byte, error) { |
||||
salt, err := c.readEntropy(gcmSaltLength) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
key, err := aes256CipherKey(secret, salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
block, err := aes.NewCipher(key) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
gcm, err := cpr.NewGCM(block) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
nonce, err := c.readEntropy(gcm.NonceSize()) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
ciphertext := gcm.Seal(nil, nonce, payload, nil) |
||||
|
||||
// Salt Nonce Encrypted
|
||||
// | | Payload
|
||||
// | | |
|
||||
// | +---------v-------------+ |
|
||||
// +-->SSSSSSSNNNNNNNEEEEEEEEE<--+
|
||||
// +-----------------------+
|
||||
prefix := append(salt, nonce...) |
||||
ciphertext = append(prefix, ciphertext...) |
||||
|
||||
return ciphertext, nil |
||||
} |
||||
|
||||
func (c aesGcmCipher) Decrypt(_ context.Context, payload []byte, secret string) ([]byte, error) { |
||||
// The input payload looks like:
|
||||
// Salt Nonce Encrypted
|
||||
// | | Payload
|
||||
// | | |
|
||||
// | +---------v-------------+ |
|
||||
// +-->SSSSSSSNNNNNNNEEEEEEEEE<--+
|
||||
// +-----------------------+
|
||||
|
||||
if len(payload) < gcmSaltLength { |
||||
// If we don't return here, we'd panic.
|
||||
return nil, ErrPayloadTooShort |
||||
} |
||||
salt, payload := payload[:gcmSaltLength], payload[gcmSaltLength:] |
||||
// Can't get nonce until we get a size from the AEAD interface.
|
||||
|
||||
key, err := aes256CipherKey(secret, salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
block, err := aes.NewCipher(key) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
gcm, err := cpr.NewGCM(block) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if len(payload) < gcm.NonceSize() { |
||||
// If we don't return here, we'd panic.
|
||||
return nil, ErrPayloadTooShort |
||||
} |
||||
nonce, payload := payload[:gcm.NonceSize()], payload[gcm.NonceSize():] |
||||
|
||||
return gcm.Open(nil, nonce, payload, nil) |
||||
} |
||||
|
||||
func (c aesGcmCipher) readEntropy(n int) ([]byte, error) { |
||||
entropy := make([]byte, n) |
||||
if _, err := io.ReadFull(c.randReader, entropy); err != nil { |
||||
return nil, err |
||||
} |
||||
return entropy, nil |
||||
} |
@ -0,0 +1,144 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/hex" |
||||
"io" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestGcmEncryption(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
t.Run("encrypts correctly", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
// The expected values are generated by test_fixtures/aesgcm_encrypt_correct_output.rb
|
||||
|
||||
salt := []byte("abcdefgh") |
||||
nonce := []byte("123456789012") |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader(append(salt, nonce...)) |
||||
|
||||
payload := []byte("grafana unit test") |
||||
secret := "secret here" |
||||
|
||||
encrypted, err := cipher.Encrypt(t.Context(), payload, secret) |
||||
require.NoError(t, err, "failed to encrypt with GCM") |
||||
require.NotEmpty(t, encrypted, "encrypted payload should not be empty") |
||||
require.Equal(t, "61626364656667683132333435363738393031328123655291d1f5eebe34c54ba55900f68a2700818a8fda9e2921190b67271d97ce", |
||||
hex.EncodeToString(encrypted), "encrypted payload should match expected value") |
||||
|
||||
// Sanity check that all our pre-provided random data is used.
|
||||
_, err = cipher.randReader.Read([]byte{0}) |
||||
require.ErrorIs(t, err, io.EOF, "expected us to have read the entire random source") |
||||
}) |
||||
|
||||
t.Run("fails if random source is empty", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) |
||||
|
||||
payload := []byte("grafana unit test") |
||||
secret := "secret here" |
||||
|
||||
_, err := cipher.Encrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected error when random source is empty") |
||||
}) |
||||
|
||||
t.Run("fails if random source does not provide nonce", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
// Scenario: the random source has enough entropy for the salt, but not for the nonce.
|
||||
// In this case, we should fail with an error.
|
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte("abcdefgh")) // 8 bytes for salt, but not enough for nonce
|
||||
|
||||
payload := []byte("grafana unit test") |
||||
secret := "secret here" |
||||
|
||||
_, err := cipher.Encrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected error when random source does not provide nonce") |
||||
}) |
||||
} |
||||
|
||||
func TestGcmDecryption(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
t.Run("decrypts correctly", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
// The expected values are generated by test_fixtures/aesgcm_encrypt_correct_output.rb
|
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) // should not be used
|
||||
|
||||
payload, err := hex.DecodeString("61626364656667683132333435363738393031328123655291d1f5eebe34c54ba55900f68a2700818a8fda9e2921190b67271d97ce") |
||||
require.NoError(t, err, "failed to decode pre-computed encrypted payload") |
||||
secret := "secret here" |
||||
|
||||
decrypted, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.NoError(t, err, "failed to decrypt with GCM") |
||||
require.Equal(t, "grafana unit test", string(decrypted), "decrypted payload should match expected value") |
||||
}) |
||||
|
||||
t.Run("fails if payload is shorter than salt", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) // should not be used
|
||||
|
||||
payload := []byte{1, 2, 3, 4} |
||||
secret := "secret here" |
||||
|
||||
_, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected error when payload is shorter than salt") |
||||
}) |
||||
|
||||
t.Run("fails if payload has length of salt but no nonce", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) // should not be used
|
||||
|
||||
payload := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // salt and a little more
|
||||
secret := "secret here" |
||||
|
||||
_, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected error when payload has length of salt but no nonce") |
||||
}) |
||||
|
||||
t.Run("fails when authentication tag is wrong", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) // should not be used
|
||||
|
||||
// Removed 2 bytes from the end of the payload to simulate a wrong authentication tag.
|
||||
payload, err := hex.DecodeString("61626364656667683132333435363738393031328123655291d1f5eebe34c54ba55900f68a2700818a8fda9e2921190b67271d") |
||||
require.NoError(t, err, "failed to decode pre-computed encrypted payload") |
||||
secret := "secret here" |
||||
|
||||
_, err = cipher.Decrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected to fail validation") |
||||
}) |
||||
|
||||
t.Run("fails if secret does not match", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := newAesGcmCipher() |
||||
cipher.randReader = bytes.NewReader([]byte{}) // should not be used
|
||||
|
||||
payload, err := hex.DecodeString("61626364656667683132333435363738393031328123655291d1f5eebe34c54ba55900f68a2700818a8fda9e2921190b67271d97ce") |
||||
require.NoError(t, err, "failed to decode pre-computed encrypted payload") |
||||
secret := "should have been 'secret here'" |
||||
|
||||
_, err = cipher.Decrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected to fail decryption") |
||||
}) |
||||
} |
@ -0,0 +1,52 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"context" |
||||
"crypto/aes" |
||||
cpr "crypto/cipher" |
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
) |
||||
|
||||
const cfbSaltLength = 8 |
||||
|
||||
var _ cipher.Decrypter = aesCfbDecipher{} |
||||
|
||||
type aesCfbDecipher struct{} |
||||
|
||||
func (aesCfbDecipher) Decrypt(_ context.Context, payload []byte, secret string) ([]byte, error) { |
||||
// payload is formatted:
|
||||
// Salt Nonce Encrypted
|
||||
// | | Payload
|
||||
// | | |
|
||||
// | +---------v-------------+ |
|
||||
// +-->SSSSSSSNNNNNNNEEEEEEEEE<--+
|
||||
// +-----------------------+
|
||||
|
||||
if len(payload) < cfbSaltLength+aes.BlockSize { |
||||
// If we don't return here, we'd panic.
|
||||
return nil, ErrPayloadTooShort |
||||
} |
||||
|
||||
salt := payload[:cfbSaltLength] |
||||
|
||||
key, err := aes256CipherKey(secret, salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
block, err := aes.NewCipher(key) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
iv, payload := payload[cfbSaltLength:][:aes.BlockSize], payload[cfbSaltLength+aes.BlockSize:] |
||||
payloadDst := make([]byte, len(payload)) |
||||
|
||||
//nolint:staticcheck // We need to support CFB _decryption_, though we don't support it for future encryption.
|
||||
stream := cpr.NewCFBDecrypter(block, iv) |
||||
|
||||
// XORKeyStream can work in-place if the two arguments are the same.
|
||||
stream.XORKeyStream(payloadDst, payload) |
||||
return payloadDst, nil |
||||
} |
@ -0,0 +1,70 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"encoding/hex" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestCfbDecryption(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
t.Run("decrypts correctly", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
// The expected values are generated by test_fixtures/aescfb_encrypt_correct_output.rb
|
||||
|
||||
cipher := aesCfbDecipher{} |
||||
|
||||
payload, err := hex.DecodeString("616263646566676831323334353637383930313234353637f1114227cb6af678cad6ee35f67f25f40b") |
||||
require.NoError(t, err, "failed to decode hex string") |
||||
secret := "secret here" |
||||
|
||||
decrypted, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.NoError(t, err, "failed to decrypt with CFB") |
||||
require.Equal(t, "grafana unit test", string(decrypted), "decrypted payload should match expected value") |
||||
}) |
||||
|
||||
t.Run("fails if payload is too short", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := aesCfbDecipher{} |
||||
|
||||
payload := []byte{1, 2, 3, 4} |
||||
secret := "secret here" |
||||
|
||||
_, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.Error(t, err, "expected error when payload is shorter than salt") |
||||
}) |
||||
|
||||
t.Run("fails if payload is not an AES-encrypted value", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := aesCfbDecipher{} |
||||
|
||||
payload, err := hex.DecodeString("616263646566676831323334353637383930313234353637f1114227cb") |
||||
require.NoError(t, err, "failed to decode hex string") |
||||
secret := "secret here" |
||||
|
||||
// We don't have any authentication tag, so we can't return an error in this case.
|
||||
decrypted, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.NoError(t, err, "expected no error") |
||||
require.NotEqual(t, "grafana unit test", string(decrypted), "decrypted payload should not match real exposed secret") |
||||
}) |
||||
|
||||
t.Run("fails if secret is wrong", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cipher := aesCfbDecipher{} |
||||
|
||||
payload, err := hex.DecodeString("616263646566676831323334353637383930313234353637f1114227cb6af678cad6ee35f67f25f40b") |
||||
require.NoError(t, err, "failed to decode hex string") |
||||
secret := "should've been 'secret here'" |
||||
|
||||
// We don't have any authentication tag, so we can't return an error in this case.
|
||||
decrypted, err := cipher.Decrypt(t.Context(), payload, secret) |
||||
require.NoError(t, err, "expected no error") |
||||
require.NotEqual(t, "grafana unit test", string(decrypted), "decrypted payload should not match real exposed secret") |
||||
}) |
||||
} |
@ -0,0 +1,7 @@ |
||||
package provider |
||||
|
||||
import "errors" |
||||
|
||||
// ErrPayloadTooShort is returned when the payload is too short to be decrypted.
|
||||
// In some situations, the error may instead be io.ErrUnexpectedEOF or a cipher-specific error.
|
||||
var ErrPayloadTooShort = errors.New("payload too short") |
@ -0,0 +1,18 @@ |
||||
package provider |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
) |
||||
|
||||
func ProvideCiphers() map[string]cipher.Encrypter { |
||||
return map[string]cipher.Encrypter{ |
||||
cipher.AesGcm: newAesGcmCipher(), |
||||
} |
||||
} |
||||
|
||||
func ProvideDeciphers() map[string]cipher.Decrypter { |
||||
return map[string]cipher.Decrypter{ |
||||
cipher.AesGcm: newAesGcmCipher(), |
||||
cipher.AesCfb: aesCfbDecipher{}, |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
package provider_test |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher/provider" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestNoCfbEncryptionCipher(t *testing.T) { |
||||
// CFB encryption is insecure, and as such we should not permit any cipher for encryption to be added.
|
||||
// Changing/removing this test MUST be accompanied with an approval from the app security team.
|
||||
|
||||
ciphers := provider.ProvideCiphers() |
||||
require.NotContains(t, ciphers, cipher.AesCfb, "CFB cipher should not be used for encryption") |
||||
} |
@ -0,0 +1,35 @@ |
||||
#!/usr/bin/env ruby |
||||
# Used by ../decipher_aescfb_test.go |
||||
# Why Ruby? It has a mostly available OpenSSL library that can be easily fetched (and most who have Ruby already have it!). And it is easy to read for this purpose. |
||||
|
||||
require 'openssl' |
||||
|
||||
salt = "abcdefgh" |
||||
nonce = "1234567890124567" |
||||
|
||||
secret = "secret here" |
||||
plaintext = "grafana unit test" |
||||
|
||||
# reimpl of aes256CipherKey |
||||
# the key is always the same value given the inputs |
||||
iterations = 10_000 |
||||
len = 32 |
||||
hash = OpenSSL::Digest::SHA256.new |
||||
key = OpenSSL::KDF.pbkdf2_hmac(secret, salt: salt, iterations: iterations, length: len, hash: hash) |
||||
|
||||
cipher = OpenSSL::Cipher::AES256.new(:CFB).encrypt |
||||
cipher.iv = nonce |
||||
cipher.key = key |
||||
encrypted = cipher.update(plaintext) |
||||
|
||||
def to_hex(s) |
||||
s.unpack('H*').first |
||||
end |
||||
|
||||
# Salt Nonce Encrypted |
||||
# | | Payload |
||||
# | | | |
||||
# | +---------v-------------+ | |
||||
# +-->SSSSSSSNNNNNNNEEEEEEEEE<--+ |
||||
# +-----------------------+ |
||||
printf("%s%s%s%s\n", to_hex(salt), to_hex(nonce), cipher.final, to_hex(encrypted)) |
@ -0,0 +1,38 @@ |
||||
#!/usr/bin/env ruby |
||||
# Used by ../cipher_aesgcm_test.go |
||||
# Why Ruby? It has a mostly available OpenSSL library that can be easily fetched (and most who have Ruby already have it!). And it is easy to read for this purpose. |
||||
|
||||
require 'openssl' |
||||
|
||||
# randReader field |
||||
salt = "abcdefgh" |
||||
nonce = "123456789012" |
||||
|
||||
# inputs to Encrypt |
||||
secret = "secret here" |
||||
plaintext = "grafana unit test" |
||||
|
||||
# reimpl of aes256CipherKey |
||||
# the key is always the same value given the inputs |
||||
iterations = 10_000 |
||||
len = 32 |
||||
hash = OpenSSL::Digest::SHA256.new |
||||
key = OpenSSL::KDF.pbkdf2_hmac(secret, salt: salt, iterations: iterations, length: len, hash: hash) |
||||
|
||||
cipher = OpenSSL::Cipher::AES256.new(:GCM).encrypt |
||||
cipher.iv = nonce |
||||
cipher.key = key |
||||
cipher.auth_data = "" |
||||
encrypted = cipher.update(plaintext) |
||||
|
||||
def to_hex(s) |
||||
s.unpack('H*').first |
||||
end |
||||
|
||||
# Salt Nonce Encrypted |
||||
# | | Payload |
||||
# | | | |
||||
# | +---------v-------------+ | |
||||
# +-->SSSSSSSNNNNNNNEEEEEEEEE<--+ |
||||
# +-----------------------+ |
||||
printf("%s%s%s%s%s\n", to_hex(salt), to_hex(nonce), cipher.final, to_hex(encrypted), to_hex(cipher.auth_tag)) |
@ -0,0 +1,199 @@ |
||||
package service |
||||
|
||||
import ( |
||||
"bytes" |
||||
"context" |
||||
"encoding/base64" |
||||
"fmt" |
||||
|
||||
"go.opentelemetry.io/otel/attribute" |
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log" |
||||
"github.com/grafana/grafana/pkg/infra/tracing" |
||||
"github.com/grafana/grafana/pkg/infra/usagestats" |
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption" |
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
encryptionprovider "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher/provider" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
const ( |
||||
encryptionAlgorithmDelimiter = '*' |
||||
) |
||||
|
||||
// Service must not be used for cipher.
|
||||
// Use secrets.Service implementing envelope encryption instead.
|
||||
type Service struct { |
||||
tracer tracing.Tracer |
||||
log log.Logger |
||||
|
||||
cfg *setting.Cfg |
||||
usageMetrics usagestats.Service |
||||
|
||||
ciphers map[string]cipher.Encrypter |
||||
deciphers map[string]cipher.Decrypter |
||||
} |
||||
|
||||
func NewEncryptionService( |
||||
tracer tracing.Tracer, |
||||
usageMetrics usagestats.Service, |
||||
cfg *setting.Cfg, |
||||
) (*Service, error) { |
||||
if cfg.SecretsManagement.SecretKey == "" { |
||||
return nil, fmt.Errorf("`[secrets_manager]secret_key` is not set") |
||||
} |
||||
|
||||
if cfg.SecretsManagement.Encryption.Algorithm == "" { |
||||
return nil, fmt.Errorf("`[secrets_manager.encryption]algorithm` is not set") |
||||
} |
||||
|
||||
s := &Service{ |
||||
tracer: tracer, |
||||
log: log.New("encryption"), |
||||
|
||||
ciphers: encryptionprovider.ProvideCiphers(), |
||||
deciphers: encryptionprovider.ProvideDeciphers(), |
||||
|
||||
usageMetrics: usageMetrics, |
||||
cfg: cfg, |
||||
} |
||||
|
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm |
||||
|
||||
if err := s.checkEncryptionAlgorithm(algorithm); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
s.registerUsageMetrics() |
||||
|
||||
return s, nil |
||||
} |
||||
|
||||
func (s *Service) checkEncryptionAlgorithm(algorithm string) error { |
||||
var err error |
||||
defer func() { |
||||
if err != nil { |
||||
s.log.Error("Wrong security encryption configuration", "algorithm", algorithm, "error", err) |
||||
} |
||||
}() |
||||
|
||||
if _, ok := s.ciphers[algorithm]; !ok { |
||||
err = fmt.Errorf("no cipher registered for encryption algorithm '%s'", algorithm) |
||||
return err |
||||
} |
||||
|
||||
if _, ok := s.deciphers[algorithm]; !ok { |
||||
err = fmt.Errorf("no decipher registered for encryption algorithm '%s'", algorithm) |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (s *Service) registerUsageMetrics() { |
||||
s.usageMetrics.RegisterMetricsFunc(func(context.Context) (map[string]any, error) { |
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm |
||||
|
||||
return map[string]any{ |
||||
fmt.Sprintf("stats.%s.encryption.cipher.%s.count", encryption.UsageInsightsPrefix, algorithm): 1, |
||||
}, nil |
||||
}) |
||||
} |
||||
|
||||
func (s *Service) Decrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) { |
||||
ctx, span := s.tracer.Start(ctx, "cipher.service.Decrypt") |
||||
defer span.End() |
||||
|
||||
var err error |
||||
defer func() { |
||||
if err != nil { |
||||
s.log.FromContext(ctx).Error("Decryption failed", "error", err) |
||||
} |
||||
}() |
||||
|
||||
var ( |
||||
algorithm string |
||||
toDecrypt []byte |
||||
) |
||||
algorithm, toDecrypt, err = s.deriveEncryptionAlgorithm(payload) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
decipher, ok := s.deciphers[algorithm] |
||||
if !ok { |
||||
err = fmt.Errorf("no decipher available for algorithm '%s'", algorithm) |
||||
return nil, err |
||||
} |
||||
|
||||
span.SetAttributes(attribute.String("cipher.algorithm", algorithm)) |
||||
|
||||
var decrypted []byte |
||||
decrypted, err = decipher.Decrypt(ctx, toDecrypt, secret) |
||||
|
||||
return decrypted, err |
||||
} |
||||
|
||||
func (s *Service) deriveEncryptionAlgorithm(payload []byte) (string, []byte, error) { |
||||
if len(payload) == 0 { |
||||
return "", nil, fmt.Errorf("unable to derive encryption algorithm") |
||||
} |
||||
|
||||
if payload[0] != encryptionAlgorithmDelimiter { |
||||
return cipher.AesCfb, payload, nil // backwards compatibility
|
||||
} |
||||
|
||||
payload = payload[1:] |
||||
algorithmDelimiterIdx := bytes.Index(payload, []byte{encryptionAlgorithmDelimiter}) |
||||
if algorithmDelimiterIdx == -1 { |
||||
return cipher.AesCfb, payload, nil // backwards compatibility
|
||||
} |
||||
|
||||
algorithmB64 := payload[:algorithmDelimiterIdx] |
||||
payload = payload[algorithmDelimiterIdx+1:] |
||||
|
||||
algorithm := make([]byte, base64.RawStdEncoding.DecodedLen(len(algorithmB64))) |
||||
|
||||
_, err := base64.RawStdEncoding.Decode(algorithm, algorithmB64) |
||||
if err != nil { |
||||
return "", nil, err |
||||
} |
||||
|
||||
return string(algorithm), payload, nil |
||||
} |
||||
|
||||
func (s *Service) Encrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) { |
||||
ctx, span := s.tracer.Start(ctx, "cipher.service.Encrypt") |
||||
defer span.End() |
||||
|
||||
var err error |
||||
defer func() { |
||||
if err != nil { |
||||
s.log.Error("Encryption failed", "error", err) |
||||
} |
||||
}() |
||||
|
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm |
||||
|
||||
cipher, ok := s.ciphers[algorithm] |
||||
if !ok { |
||||
err = fmt.Errorf("no cipher available for algorithm '%s'", algorithm) |
||||
return nil, err |
||||
} |
||||
|
||||
span.SetAttributes(attribute.String("cipher.algorithm", algorithm)) |
||||
|
||||
var encrypted []byte |
||||
encrypted, err = cipher.Encrypt(ctx, payload, secret) |
||||
|
||||
prefix := make([]byte, base64.RawStdEncoding.EncodedLen(len([]byte(algorithm)))+2) |
||||
base64.RawStdEncoding.Encode(prefix[1:], []byte(algorithm)) |
||||
prefix[0] = encryptionAlgorithmDelimiter |
||||
prefix[len(prefix)-1] = encryptionAlgorithmDelimiter |
||||
|
||||
ciphertext := make([]byte, len(prefix)+len(encrypted)) |
||||
copy(ciphertext, prefix) |
||||
copy(ciphertext[len(prefix):], encrypted) |
||||
|
||||
return ciphertext, nil |
||||
} |
@ -0,0 +1,78 @@ |
||||
package service |
||||
|
||||
import ( |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing" |
||||
"github.com/grafana/grafana/pkg/infra/usagestats" |
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
func newGcmService(t *testing.T) *Service { |
||||
t.Helper() |
||||
|
||||
usageStats := &usagestats.UsageStatsMock{} |
||||
settings := &setting.Cfg{ |
||||
SecretsManagement: setting.SecretsManagerSettings{ |
||||
SecretKey: "SdlklWklckeLS", |
||||
EncryptionProvider: "secretKey.v1", |
||||
Encryption: setting.EncryptionSettings{ |
||||
DataKeysCacheTTL: 5 * time.Minute, |
||||
DataKeysCleanupInterval: 1 * time.Nanosecond, |
||||
Algorithm: cipher.AesGcm, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
svc, err := NewEncryptionService(tracing.InitializeTracerForTest(), usageStats, settings) |
||||
require.NoError(t, err, "failed to set up encryption service") |
||||
return svc |
||||
} |
||||
|
||||
func TestService(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
t.Run("decrypt empty payload should return error", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
svc := newGcmService(t) |
||||
_, err := svc.Decrypt(t.Context(), []byte(""), "1234") |
||||
require.Error(t, err) |
||||
|
||||
assert.Equal(t, "unable to derive encryption algorithm", err.Error()) |
||||
}) |
||||
|
||||
t.Run("encrypt and decrypt with GCM should work", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
svc := newGcmService(t) |
||||
encrypted, err := svc.Encrypt(t.Context(), []byte("grafana"), "1234") |
||||
require.NoError(t, err) |
||||
|
||||
decrypted, err := svc.Decrypt(t.Context(), encrypted, "1234") |
||||
require.NoError(t, err) |
||||
|
||||
assert.Equal(t, []byte("grafana"), decrypted) |
||||
// We'll let the provider deal with testing details.
|
||||
}) |
||||
|
||||
t.Run("decrypting legacy ciphertext should work", func(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
// Raw slice of bytes that corresponds to the following ciphertext:
|
||||
// - 'grafana' as payload
|
||||
// - '1234' as secret
|
||||
// - no encryption algorithm metadata
|
||||
ciphertext := []byte{73, 71, 50, 57, 121, 110, 90, 109, 115, 23, 237, 13, 130, 188, 151, 118, 98, 103, 80, 209, 79, 143, 22, 122, 44, 40, 102, 41, 136, 16, 27} |
||||
|
||||
svc := newGcmService(t) |
||||
decrypted, err := svc.Decrypt(t.Context(), ciphertext, "1234") |
||||
require.NoError(t, err) |
||||
assert.Equal(t, []byte("grafana"), decrypted) |
||||
}) |
||||
} |
@ -0,0 +1,3 @@ |
||||
package encryption |
||||
|
||||
const UsageInsightsPrefix = "secrets_manager" |
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue