Adding HTTPS to Kibana and Elasticsearch

While moving ELK to my Docker Swarm, I want to encrypt everything going over the network. This post will go over adding Kibana to my new ELK stack, enabling https, and talking to my Elasticsearch over https.

I already took care of exposing my Elasticsearch over SSL in this post: https://www.frakkingsweet.com/elasticsearch-java-ssl/

Add a Kibana service to the main docker-compose.yml file, here is that section, see below for the full file:

version: '3.7'
services:
  kibana:
    image: docker.elastic.co/kibana/kibana:7.2.0
    ports: 
      - 5601:5601
    networks:
      - ext

Inside of my prod overrides file, docker-compose.prod.yml, I add the Kibana service with the appropriate overrides. I access my Elasticsearch instance through a DNS entry that is a CNAME pointing to my docker cluster. We'll say that entry is elasticsearch.example.com. I created another DNS entry, kibana.example.com, that points to the docker cluster. This will make it easier in the future when I setup a reverse proxy and instead of dealing with ports like https://kibana.example.com:5601 I will be able to access it by hitting https://kibana.example.com. No need to remember the port numbers.

Here is the Kibana services portion of the compose override file. See below for the full docker-compose.prod.yml file. Kibana uses a different format for the environment variables than Elasticsearch. With Kibana everything is upper case and all periods are replaced with a _. Another difference, we don't need to put the secrets in a special location, so that section is nice and simple.

version: '3.7'
services:
  kibana:
    environment:
      - SERVER_NAME=kibana.example.com
      - ELASTICSEARCH_HOSTS=https://elasticsearch.example.com:9200
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/run/secrets/root.pub.pem
      - SERVER_SSL_ENABLED=true
      - SERVER_SSL_CERTIFICATE=/run/secrets/certificate.crt.pem
      - SERVER_SSL_KEY=/run/secrets/certificate.key.pem
      - SERVER_SSL_CERTIFICATEAUTHORITIES=/run/secrets/root.pub.pem
    secrets:
      - root.pub.pem
      - certificate.key.pem
      - certificate.crt.pem

And for the full, complete files.

docker-compose.yml

version: '3.7'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    environment: 
      - "ES_JAVA_OPTS=-Xms7g -Xmx7g"
      - discovery.type=single-node
    ports:
      - 9200:9200
    networks:
      - ext
  kibana:
    image: docker.elastic.co/kibana/kibana:7.2.0
    ports: 
      - 5601:5601
    networks:
      - ext
networks:
  ext:

docker-compose.prod.yml

version: "3.7"
services:
  elasticsearch:
    environment:
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=/usr/share/elasticsearch/config/secrets/certificate.key.pem
      - xpack.security.http.ssl.certificate=/usr/share/elasticsearch/config/secrets/certificate.crt.pem
      - xpack.security.http.ssl.certificate_authorities=/usr/share/elasticsearch/config/secrets/root.pub.pem
      - xpack.security.transport.ssl.enabled=false
      - xpack.security.transport.ssl.key=/usr/share/elasticsearch/config/secrets/certificate.key.pem
      - xpack.security.transport.ssl.certificate=/usr/share/elasticsearch/config/secrets/certificate.crt.pem
      - xpack.security.transport.ssl.certificate_authorities=/usr/share/elasticsearch/config/secrets/root.pub.pem
    volumes:
      - elasticsearchdata:/usr/share/elasticsearch/data
      - elasticsearchconfig:/usr/share/elasticsearch/config
      - elasticsearchlogs:/usr/share/elasticsearch/logs
    secrets:
      - source: root.pub.pem
        target: /usr/share/elasticsearch/config/secrets/root.pub.pem
        uid: '1000'
        gid: '1000'
      - source: certificate.key.pem
        target: /usr/share/elasticsearch/config/secrets/certificate.key.pem
        uid: '1000'
        gid: '1000'
      - source: certificate.crt.pem
        target: /usr/share/elasticsearch/config/secrets/certificate.crt.pem
        uid: '1000'
        gid: '1000'
  kibana:
    environment:
      - SERVER_NAME=kibana.example.com
      - ELASTICSEARCH_HOSTS=https://elasticsearch.example.com:9200
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/run/secrets/root.pub.pem
      - SERVER_SSL_ENABLED=true
      - SERVER_SSL_CERTIFICATE=/run/secrets/certificate.crt.pem
      - SERVER_SSL_KEY=/run/secrets/certificate.key.pem
      - SERVER_SSL_CERTIFICATEAUTHORITIES=/run/secrets/root.pub.pem
    secrets:
      - root.pub.pem
      - certificate.key.pem
      - certificate.crt.pem

secrets:
  root.pub.pem:
    external: true
    name: certificates.example.com.ca.pub.pem
  certificate.key.pem:
    external: true
    name: certificates.wildcard.example.com.key.pem
  certificate.crt.pem:
    external: true
    name: certificates.wildcard.example.com.pub.pem
volumes:
  elasticsearchdata:
    driver: local
    driver_opts:
      type: nfs
      device: ":/volumes/elasticsearch/data"
      o: "addr=${NFSSERVER},vers=4,rw"
  elasticsearchconfig:
    driver: local
    driver_opts:
      type: nfs
      device: ":/volumes/elasticsearch/config"
      o: "addr=${NFSSERVER},vers=4,rw"
  elasticsearchlogs:
    driver: local
    driver_opts:
      type: nfs
      device: ":/volumes/elasticsearch/logs"
      o: "addr=${NFSSERVER},vers=4,rw"

As before, to deploy the updated stack:

docker stack deploy -c docker-compose.yml -c docker-compose.prod.yml prod --with-registry-auth

That's it. So far this is the easiest part of building a new ELK stack in Docker.