ELK Stack — TLS/SSL
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):
- Generate new certificates signed by the same CA
- Update
http.p12on one node at a time - Restart each node and verify it rejoins the cluster
- Update Kibana, Logstash, and Beats configurations
- 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.