ELK Stack — TLS/SSL

4 minute read

TLS/SSL

Elasticsearch requires TLS encryption on two layers:

  • Transport layer — node-to-node communication within the cluster
  • HTTP layer — client-to-node communication (REST API, Kibana, Beats, Logstash)

Without encryption, credentials and data travel in plain text over the network. When xpack.security.enabled is true, transport TLS is mandatory.


The elasticsearch-certutil Tool

Elasticsearch ships with elasticsearch-certutil to generate all the certificates you need.

It supports three modes:

Mode Purpose
ca Generate a Certificate Authority (CA)
cert Generate node certificates signed by a CA
http Interactive wizard for HTTP-layer certificates

Step-by-Step: Generating Certificates

Step 1 — Create a Certificate Authority

/usr/share/elasticsearch/bin/elasticsearch-certutil ca \
  --out /etc/elasticsearch/certs/elastic-stack-ca.p12 \
  --pass ""

This creates a PKCS#12 file containing the CA certificate and private key. In production, protect this file with a strong passphrase.

Step 2 — Generate Transport Certificates

/usr/share/elasticsearch/bin/elasticsearch-certutil cert \
  --ca /etc/elasticsearch/certs/elastic-stack-ca.p12 \
  --ca-pass "" \
  --out /etc/elasticsearch/certs/transport.p12 \
  --pass ""

Copy transport.p12 to every Elasticsearch node:

scp /etc/elasticsearch/certs/transport.p12 es-node-2:/etc/elasticsearch/certs/
scp /etc/elasticsearch/certs/transport.p12 es-node-3:/etc/elasticsearch/certs/

Set correct ownership:

chown elasticsearch:elasticsearch /etc/elasticsearch/certs/transport.p12
chmod 640 /etc/elasticsearch/certs/transport.p12

Step 3 — Generate HTTP Certificates

The http wizard generates certificates with the correct Subject Alternative Names (SANs):

/usr/share/elasticsearch/bin/elasticsearch-certutil http

The interactive wizard asks:

Generate a CSR? [y/N] N
Use an existing CA? [y/N] y
CA Path: /etc/elasticsearch/certs/elastic-stack-ca.p12
Password for CA: <enter>
Certificate validity (days) [5y]: 3y
Generate a certificate per node? [y/N] y

# For each node, provide:
#   - Node name (es-node-1)
#   - Hostnames (es-node-1, es-node-1.example.com)
#   - IP addresses (10.0.1.10)

This creates a zip file containing:

elasticsearch-ssl-http.zip
├── elasticsearch/
│   ├── es-node-1/
│   │   └── http.p12
│   ├── es-node-2/
│   │   └── http.p12
│   └── es-node-3/
│       └── http.p12
└── kibana/
    └── elasticsearch-ca.pem

Step 4 — Extract CA Certificate in PEM Format

Beats and Logstash often need the CA in PEM format:

openssl pkcs12 -in /etc/elasticsearch/certs/elastic-stack-ca.p12 \
  -clcerts -nokeys -out /etc/elasticsearch/certs/ca.crt \
  -passin pass:

Configuring Transport TLS

Add the following to elasticsearch.yml on every node:

# /etc/elasticsearch/elasticsearch.yml

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: certs/transport.p12
xpack.security.transport.ssl.keystore.password: ""
xpack.security.transport.ssl.truststore.path: certs/transport.p12
xpack.security.transport.ssl.truststore.password: ""

If using PEM files instead of PKCS#12:

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.key: certs/transport.key
xpack.security.transport.ssl.certificate: certs/transport.crt
xpack.security.transport.ssl.certificate_authorities: ["certs/ca.crt"]

Tip: Store keystore passwords in the Elasticsearch keystore instead of plain text:

/usr/share/elasticsearch/bin/elasticsearch-keystore add \
  xpack.security.transport.ssl.keystore.password

Configuring HTTP TLS

# /etc/elasticsearch/elasticsearch.yml

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: certs/http.p12
xpack.security.http.ssl.keystore.password: ""

With PEM files:

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: certs/http.key
xpack.security.http.ssl.certificate: certs/http.crt
xpack.security.http.ssl.certificate_authorities: ["certs/ca.crt"]

After configuration, restart Elasticsearch:

sudo systemctl restart elasticsearch

Verify HTTPS is working:

curl --cacert /etc/elasticsearch/certs/ca.crt \
  -u elastic:password \
  https://es-node-1:9200

Configuring Kibana to Use HTTPS

Kibana → Elasticsearch (backend connection)

# /etc/kibana/kibana.yml

elasticsearch.hosts: ["https://es-node-1:9200"]
elasticsearch.ssl.certificateAuthorities: ["/etc/kibana/certs/elasticsearch-ca.pem"]
elasticsearch.ssl.verificationMode: full

Kibana HTTPS (browser → Kibana)

Generate a certificate for Kibana:

/usr/share/elasticsearch/bin/elasticsearch-certutil cert \
  --ca /etc/elasticsearch/certs/elastic-stack-ca.p12 \
  --ca-pass "" \
  --name kibana-server \
  --out /etc/kibana/certs/kibana-server.p12 \
  --pass ""

Extract PEM files:

openssl pkcs12 -in /etc/kibana/certs/kibana-server.p12 \
  -nocerts -nodes -out /etc/kibana/certs/kibana.key -passin pass:

openssl pkcs12 -in /etc/kibana/certs/kibana-server.p12 \
  -clcerts -nokeys -out /etc/kibana/certs/kibana.crt -passin pass:

Configure Kibana:

# /etc/kibana/kibana.yml

server.ssl.enabled: true
server.ssl.certificate: /etc/kibana/certs/kibana.crt
server.ssl.key: /etc/kibana/certs/kibana.key

Restart Kibana:

sudo systemctl restart kibana

Access Kibana at https://kibana-host:5601.


Configuring Logstash SSL Output

# /etc/logstash/conf.d/output.conf

output {
  elasticsearch {
    hosts => ["https://es-node-1:9200"]
    user => "logstash_writer"
    password => "password"
    ssl_enabled => true
    ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"]
    ssl_verification_mode => "full"
  }
}

For mutual TLS (mTLS) where Logstash also presents a certificate:

output {
  elasticsearch {
    hosts => ["https://es-node-1:9200"]
    ssl_enabled => true
    ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"]
    ssl_certificate => "/etc/logstash/certs/logstash.crt"
    ssl_key => "/etc/logstash/certs/logstash.key"
  }
}

Configuring Filebeat SSL

# /etc/filebeat/filebeat.yml

output.elasticsearch:
  hosts: ["https://es-node-1:9200"]
  username: "filebeat_writer"
  password: "password"
  ssl:
    enabled: true
    certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
    verification_mode: full

For mutual TLS:

output.elasticsearch:
  hosts: ["https://es-node-1:9200"]
  ssl:
    enabled: true
    certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
    certificate: "/etc/filebeat/certs/filebeat.crt"
    key: "/etc/filebeat/certs/filebeat.key"

Certificate Rotation

Certificates expire. Plan for rotation before they expire.

Check certificate expiry:

openssl pkcs12 -in /etc/elasticsearch/certs/http.p12 \
  -clcerts -nokeys -passin pass: | \
  openssl x509 -noout -enddate

Output:

notAfter=Apr  1 00:00:00 2029 GMT

Rotation process (rolling — zero downtime):

  1. Generate new certificates signed by the same CA
  2. Update http.p12 on one node at a time
  3. Restart each node and verify it rejoins the cluster
  4. Update Kibana, Logstash, and Beats configurations
  5. Restart clients
# On each node, one at a time:
cp /etc/elasticsearch/certs/http.p12 /etc/elasticsearch/certs/http.p12.bak
cp /path/to/new/http.p12 /etc/elasticsearch/certs/http.p12
chown elasticsearch:elasticsearch /etc/elasticsearch/certs/http.p12
sudo systemctl restart elasticsearch

# Verify the node rejoined
curl --cacert /etc/elasticsearch/certs/ca.crt \
  -u elastic:password \
  https://es-node-1:9200/_cat/nodes?v

Let’s Encrypt Integration

For public-facing Kibana instances, you can use Let’s Encrypt certificates:

# Install certbot
sudo apt install certbot

# Obtain a certificate
sudo certbot certonly --standalone -d kibana.example.com

# Configure Kibana
# /etc/kibana/kibana.yml
server.ssl.enabled: true
server.ssl.certificate: /etc/letsencrypt/live/kibana.example.com/fullchain.pem
server.ssl.key: /etc/letsencrypt/live/kibana.example.com/privkey.pem

Set up automatic renewal:

# Add to crontab
0 3 * * * certbot renew --post-hook "systemctl restart kibana" >> /var/log/certbot-renew.log 2>&1

Quick Reference

Task Command / Setting
Generate CA elasticsearch-certutil ca --out ca.p12
Generate transport cert elasticsearch-certutil cert --ca ca.p12 --out transport.p12
Generate HTTP certs elasticsearch-certutil http (interactive)
Extract PEM from P12 openssl pkcs12 -in file.p12 -clcerts -nokeys -out file.crt
Enable transport TLS xpack.security.transport.ssl.enabled: true
Enable HTTP TLS xpack.security.http.ssl.enabled: true
Kibana → ES SSL elasticsearch.ssl.certificateAuthorities in kibana.yml
Kibana HTTPS server.ssl.enabled: true in kibana.yml
Logstash SSL ssl_enabled => true in elasticsearch output
Filebeat SSL ssl.enabled: true under output.elasticsearch
Check cert expiry openssl x509 -noout -enddate
Rotate certs Replace files → rolling restart → update clients

Next: RBAC and Roles — control who can access what.