SSL And Password Protection for Kibana

Mar 19 2014

Kibana is a front-end to Logstash, a log collection server. By default, Kibana is configured without support for logins and without support for SSL. This is a fast-paced explanation of how I tried to get Kibana reasonably secure.

To begin, I have a Logstash server up and running on an Ubuntu 13.10 server. It has been collecting logs for a while. What I want to do is add Kibana as a front-end. But I want Kibana to be password-protected and served over SSL. And more importantly, I don't want the Logstash server (listening on port 9200) to be accessible directly without both SSL and password protection.

The latest version of Kibana is a browser-based app. It uses JSONP to directly query the Logstash Elasticsearch server (the one on port 9200). While I like the idea, securing this is a high priority for me.

To tighten up the security, I decided on this strategy: Use nginx as an HTTP server for Kibana's files, and also as a proxy for traffic to Elasticsearch. Fortunately for me, this is not the first time anyone's tried this. In fact, the Kibana GitHub repo has some good resources for this configuration.

Here's what I did, in abbreviated form:

  1. Install nginx as a web server (On Ubuntu, this was just sudo apt-get install nginx)
  2. Load my SSL certificates onto the server, putting them in /etc/nginx/
  3. Install the apache2-util package to get htpasswd
  4. Create a password file in /etc/nginx/conf.d/kibana.htpasswd using htpasswd -c ...
  5. Grab the latest copy of Kibana and unzip it into /home/logstash/kibana. While Kibana comes built-in with the latest (1.3.3) Logstash JAR file, I have found it easier to work with a stand-alone Kibana codebase.
  6. Edit /home/logstash/kibana/config.js and change the elasticsearch: line to elasticsearch: "https://"+window.location.hostname,
  7. Modify the /etc/nginx/sites-available/default file based on this example from Kibana, making sure to add password protection to all of the location sections.
  8. Start the server

My firewall only allows traffic onto this server for HTTPS (port 443). Since nginx is proxying traffic locally to port 9200, I don't have to expose that port at all.

My final default nginx config file looked like this:

server {
  listen                *:443;
  ssl on;
  ssl_certificate cert.pem;
  ssl_certificate_key cert.key;
  ssl_session_timeout 5m;
  ssl_protocols SSLv3 TLSv1;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
  ssl_prefer_server_ciphers on;

  server_name           kibana;
  access_log            /var/log/nginx/kibana.access.log;
  index  index.html  index.htm;

  location / {
    root  /home/logstash/kibana;
    index  index.html  index.htm;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }

  location ~ ^/_aliases$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }
  location ~ ^/.*/_aliases$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }
  location ~ ^/_nodes$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }
    location ~ ^/.*/_search$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }
  location ~ ^/.*/_mapping {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
  }

  location ~ ^/kibana-int/dashboard/.*$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    limit_except GET {
      proxy_pass http://127.0.0.1:9200;
      auth_basic "Restricted";
      auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
    }
  }
  location ~ ^/kibana-int/temp.*$ {
    proxy_pass http://127.0.0.1:9200;
    proxy_read_timeout 90;
    limit_except GET {
      proxy_pass http://127.0.0.1:9200;
      auth_basic "Restricted";
      auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
    }
  }
}

There may be some routes to simplifying all of those location sections, but I haven't gotten to that yet.