Privacy Tools Guide

Elasticsearch has been the source of more large-scale data breaches than almost any other technology. The default configuration has no authentication and listens on all interfaces. This guide covers the full security stack: network binding, TLS, authentication, RBAC, and audit logging.

Installation

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | \
  sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] \
  https://artifacts.elastic.co/packages/8.x/apt stable main" | \
  sudo tee /etc/apt/sources.list.d/elastic-8.x.list

sudo apt update && sudo apt install elasticsearch
# Do NOT start yet — configure security first

Step 1: Configure elasticsearch.yml

cluster.name: production-cluster
node.name: node-01

# NEVER use 0.0.0.0 in production
network.host: 127.0.0.1
http.port: 9200

path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch

# Security (enabled by default in ES 8)
xpack.security.enabled: true
xpack.security.enrollment.enabled: false

# TLS for HTTP layer
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: /etc/elasticsearch/certs/http.p12

# TLS for transport layer
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: /etc/elasticsearch/certs/transport.p12
xpack.security.transport.ssl.truststore.path: /etc/elasticsearch/certs/transport.p12

# Audit logging
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include: >
  authentication_success,authentication_failed,access_denied,connection_denied

Step 2: Generate TLS Certificates

# Generate CA
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca \
  --out /etc/elasticsearch/certs/elastic-ca.p12 --pass ""

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

# Generate HTTP cert (interactive)
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil http

sudo chmod 640 /etc/elasticsearch/certs/*.p12
sudo chown elasticsearch:elasticsearch /etc/elasticsearch/certs/*.p12

Step 3: Set Passwords for Built-In Users

sudo systemctl start elasticsearch

# Auto-generate passwords for all built-in users
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto
# Save the output — shows passwords for elastic, kibana_system, logstash_system, etc.

# Test authentication
curl -k -u elastic:YOUR_PASSWORD https://127.0.0.1:9200/

Step 4: Role-Based Access Control

# Read-only role
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_security/role/read_only" \
  -H "Content-Type: application/json" \
  -d '{
    "indices": [{
      "names": ["*"],
      "privileges": ["read", "view_index_metadata"]
    }]
  }'

# Logstash ingestion role
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_security/role/logstash_writer" \
  -H "Content-Type: application/json" \
  -d '{
    "indices": [{
      "names": ["logs-*", "metrics-*"],
      "privileges": ["create_index", "index", "create"]
    }]
  }'

# Create a user
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_security/user/analyst" \
  -H "Content-Type: application/json" \
  -d '{
    "password": "SecureAnalystPass123!",
    "roles": ["read_only"],
    "full_name": "Data Analyst"
  }'

Step 5: Network Isolation with UFW

sudo ufw deny 9200/tcp
sudo ufw deny 9300/tcp

# Only allow from specific app servers
sudo ufw allow from 10.0.0.5 to any port 9200 proto tcp

sudo ufw reload

Step 6: Monitor Audit Logs

tail -f /var/log/elasticsearch/*.audit.json | \
  jq 'select(.event.type == "authentication_failed") |
      {time: .["@timestamp"], user: .user.name, ip: .origin.address}'

Verify Security Configuration

# Unauthenticated access should fail with 401
curl -k https://127.0.0.1:9200/

# Authenticated access
curl -k -u elastic:YOUR_PASSWORD https://127.0.0.1:9200/_cluster/health

Multi-Node Cluster Security

Elasticsearch clusters have internal communication (transport layer) that must also be secured:

# Generate transport certificates for multi-node cluster
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert \
  --name node-01 \
  --dns node-01.example.com \
  --ip 10.0.0.10 \
  --ca /etc/elasticsearch/certs/elastic-ca.p12

# Repeat for each node with unique names and IPs

In elasticsearch.yml for node-01:

cluster.name: production-cluster
node.name: node-01
node.roles: [data, master]

network.host: 10.0.0.10
discovery.seed_hosts: ["10.0.0.10", "10.0.0.11", "10.0.0.12"]
cluster.initial_master_nodes: ["node-01", "node-02", "node-03"]

# TLS for transport (node-to-node)
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: /etc/elasticsearch/certs/node-01.p12
xpack.security.transport.ssl.truststore.path: /etc/elasticsearch/certs/elastic-ca.p12

Each node verifies every other node’s certificate. An attacker cannot join the cluster without valid certificates.

Index-Level Encryption

Even with TLS, data on disk is unencrypted unless you use LUKS. Add full-disk encryption:

# Install cryptsetup
sudo apt install cryptsetup

# Create encrypted volume for Elasticsearch data
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 elasticsearch-data

# Format and mount
sudo mkfs.ext4 /dev/mapper/elasticsearch-data
sudo mount /dev/mapper/elasticsearch-data /var/lib/elasticsearch

# Update elasticsearch.yml
path.data: /var/lib/elasticsearch

With full-disk encryption, if someone steals the physical drive, data is unreadable without the encryption key.

Snapshot Security

Elasticsearch snapshots can be stored on S3, local filesystem, or other backends. Secure them:

# Configure S3 repository with encryption
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_snapshot/my_s3_repo" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "s3",
    "settings": {
      "bucket": "my-elasticsearch-backups",
      "region": "us-east-1",
      "server_side_encryption": true,
      "storage_class": "STANDARD_IA",
      "base_path": "elasticsearch",
      "compress": true
    }
  }'

# Create snapshot
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_snapshot/my_s3_repo/snapshot_$(date +%s)"

Always enable:

Preventing Common Elasticsearch Breaches

Breach Pattern 1: Unauthenticated Access

Public ES instances have been compromised thousands of times. Always:

xpack.security.enabled: true
network.host: 127.0.0.1  # Never 0.0.0.0

Breach Pattern 2: Default Credentials

Elasticsearch comes with default passwords. Always:

/usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto

This generates random passwords for built-in users.

Breach Pattern 3: Privilege Escalation

Users with read access to one index shouldn’t read all indexes:

# Create minimal privilege role
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_security/role/logs_reader" \
  -H "Content-Type: application/json" \
  -d '{
    "indices": [{
      "names": ["logs-*"],
      "privileges": ["read"]
    }]
  }'

# Create user with only logs_reader role
curl -k -u elastic:YOUR_PASSWORD \
  -X PUT "https://127.0.0.1:9200/_security/user/logstash" \
  -H "Content-Type: application/json" \
  -d '{
    "password": "StrongLogstashPassword",
    "roles": ["logs_reader"]
  }'

Logstash now reads only logs-* indices, cannot access user data or configuration.

Breach Pattern 4: Insufficient Audit Logging

Configure detailed auditing:

xpack.security.audit.enabled: true
xpack.security.audit.logfile.enabled: true
xpack.security.audit.logfile.events.include:
  - authentication_success
  - authentication_failed
  - access_denied
  - index_access
  - connection_granted
  - connection_denied

Monitor audit logs for suspicious activity:

# Failed authentication attempts
tail -f /var/log/elasticsearch/*.audit.json | \
  jq 'select(.event.type == "authentication_failed")'

# Unauthorized access attempts
tail -f /var/log/elasticsearch/*.audit.json | \
  jq 'select(.event.type == "access_denied")'

Performance Impact of Security

Security adds overhead:

On modern hardware, this is acceptable. Elasticsearch remains fast even with security enabled.

If performance becomes critical:

  1. Use separate security clusters (policy enforcement) and data clusters
  2. Disable audit logging on high-throughput nodes
  3. Use AES-NI hardware acceleration (available on most CPUs)

Security Checklist

Before deploying Elasticsearch to production:


Built by theluckystrike — More at zovo.one