How to display grafana graphs in my website's admin panel securely?

fracz picture fracz · Feb 17, 2017 · Viewed 10.4k times · Source

I have created a few nice plots in grafana. I want to display some of them directly in my website's admin panel instead of forcing users to go to the grafana dashboards and forcing them to double-authenticate (once for my website and once for grafana).

One option is to enable anonymous access in grafana and use the share/embed in iframe option available to every graph in grafana. While it works gread, it seems like a huge vulnerability if anyone that knows the appropriate URL can see the grafana data.

Then I have seen that grafana has the HTTP API but I can see no possibility to display a certain graph there.

I have tried a solution with a PHP Proxy that will add an authorization headers and connect to grafana embed URL if the user is authenticated correclty on my website. However, it does not work and it is a nightmare to configure.

The final option is to grab pngs of the graphs from grafana on the server side and serve them only for authenticated admins in my website. However, in such case I loose all the cool stuff grafana offers OOTB, like expanding/collapsing time range, automatic refreshing etc.

Answer

pt12lol picture pt12lol · Aug 24, 2017

Based on this answer and this answer I was able to embed Grafana dashboard inside my page.

Put your iframe:

<iframe id="dashboard"></iframe>

And then feed it with Grafana's content using AJAX requests like this:

<script type="text/javascript">
  $.ajax(
    {
      type: 'GET',
      url: 'http://localhost:3000/dashboard/db/your-dashboard-here',
      contentType: 'application/json',
      beforeSend: function(xhr, settings) {
        xhr.setRequestHeader(
          'Authorization', 'Basic ' + window.btoa('admin:admin')
        );
      },
      success: function(data) {
        $('#dashboard').attr('src', 'http://localhost:3000/dashboard/db/your-dashboard-here');
        $('#dashboard').contents().find('html').html(data);
      }
    }
  );
</script>

AJAX request is obligatory because it enables you to set header with your credentials.

In this moment you are getting empty response from Grafana server because of CORS. What you have to do is to enable some proxy for Grafana. There is example configuration of Grafana and nginx docker containers using docker-compose below:

version: '2.1'
services:
  grafana:
    image: grafana/grafana
  nginx:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 3000:80

The last thing you have to do is providing your nginx.conf file:

events {
    worker_connections  1024;
}

http {
#
# Acts as a nginx HTTPS proxy server
# enabling CORS only to domains matched by regex
# /https?://.*\.mckinsey\.com(:[0-9]+)?)/
#
# Based on:
# * http://blog.themillhousegroup.com/2013/05/nginx-as-cors-enabled-https-proxy.html
# * http://enable-cors.org/server_nginx.html
#
server {
  listen 80;

  location / {
    #if ($http_origin ~* (https?://.*\.tarunlalwani\.com(:[0-9]+)?$)) {
    #   set $cors "1";
    #}
    set $cors "1";

    # OPTIONS indicates a CORS pre-flight request
    if ($request_method = 'OPTIONS') {
       set $cors "${cors}o";
    }

    # Append CORS headers to any request from
    # allowed CORS domain, except OPTIONS
    if ($cors = "1") {
       add_header Access-Control-Allow-Origin $http_origin always;
       add_header Access-Control-Allow-Credentials  true always;
       proxy_pass      http://grafana:3000;
    }

    # OPTIONS (pre-flight) request from allowed
    # CORS domain. return response directly
    if ($cors = "1o") {
       add_header 'Access-Control-Allow-Origin' '$http_origin' always;
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
       add_header 'Access-Control-Allow-Credentials' 'true' always;
       add_header 'Access-Control-Allow-Headers' 'Origin,Content-Type,Accept,Authorization' always;
       add_header Content-Length 0;
       add_header Content-Type text/plain;
       return 204;
    }

    # Requests from non-allowed CORS domains
       proxy_pass      http://grafana:3000;
  }
}

}

This file bases on provided here, but important difference is

add_header 'Access-Control-Allow-Headers' 'Origin,Content-Type,Accept,Authorization' always;

This indicates that you allow to set Authorization header.