Elasticsearch and SSL

While moving my ELK stack into Docker I wanted to enable SSL. It's allowed for free in 7.2.0 so I might as well.

While moving my ELK stack into Docker I wanted to enable SSL. It's allowed for free in 7.2.0 so I might as well.

Enabling SSL wasn't complicated, except for one piece. The certificates must be under the config directory for Elasticsearch. This is due to some security pieces that are in place.

To enable SSL without using a custom image, add the following environment variables to your docker-compose file.

  - 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

Now, using the secrets feature of Docker Swarm to add those certificates. I first tried using the regular Docker secret location, /run/secrets, because I didn't want to diverge from the Docker standard. After about an hour of fighting it I found one small form entry that explained why it needed to be there, got it. I modified my docker-compose.yml file to store the certificates in directory under the config directory. I used /usr/share/elasticsearch/config/secrets. Most guides used certs but they weren't for Docker. Since these are pulled from the Docker secrets I went with secrets.

I use 2 docker-compose files. One that contains the basic information that would run on any system (hopefully) and one that I use to augment that one and run in my real, production, environment.

My docker-compose.yml file is this:

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
networks:
  ext:

And my docker-compose.prod.yml is this:

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'
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.test.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"

To deploy this to my swarm I use the following command:

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

One thing I was concerned about was that the secrets are stored under the config directory which I have mounted to my NFS share. I was afraid that the individual secret file would be stored on the NFS share itself and easily accessible by anybody who could see it. There was a file that was created, but it was empty, no data. Disaster averted.

On a side note, when I first started this, Java was throwing an error stating that my certificate chain was invalid. Running openssl verified that it was in fact a valid chain. Turns out, in my public key PEM file, I had the public key listed twice. I removed the second copy and it started working.