Secure your Elasticsearch 8 cluster with comprehensive SSL/TLS encryption, user authentication, role-based access control, and network hardening. This tutorial covers production-grade security configuration to protect your search infrastructure.
Prerequisites
- Root or sudo access
- At least 4GB RAM
- Java 11 or higher installed
- Basic understanding of PKI certificates
What this solves
Elasticsearch 8 ships with security disabled by default in development environments, leaving your search cluster vulnerable to unauthorized access and data breaches. This tutorial implements comprehensive security hardening including SSL/TLS encryption for all communications, user authentication with strong passwords, role-based access control (RBAC) for granular permissions, API key management for programmatic access, and network-level security controls.
You'll configure transport layer security between nodes, HTTP layer encryption for client connections, create custom user roles with minimal required permissions, implement API key authentication for applications, and harden the network stack with firewall rules and bind address restrictions.
Step-by-step configuration
Install Elasticsearch 8 with security features
First, install Elasticsearch 8 which includes the security features we need for this hardening configuration.
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 -y elasticsearch
Generate SSL/TLS certificates for cluster communication
Create the certificate authority and node certificates for secure transport layer communication between Elasticsearch nodes.
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca --out /etc/elasticsearch/elastic-stack-ca.p12 --pass ""
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca /etc/elasticsearch/elastic-stack-ca.p12 --out /etc/elasticsearch/elastic-certificates.p12 --pass ""
sudo chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-*.p12
sudo chmod 660 /etc/elasticsearch/elastic-*.p12
Generate HTTP SSL certificates for client connections
Create certificates for HTTPS communication between clients and Elasticsearch, including proper hostname verification.
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil http
Follow prompts:
Generate CSR? n
Use existing CA? y
CA Path: /etc/elasticsearch/elastic-stack-ca.p12
Password: (leave empty)
Certificate validity: 5y
Generate certificate per node? y
Node name: elasticsearch-node1
Hostnames: localhost,127.0.0.1,your-server-ip
IP addresses: 127.0.0.1,your-server-ip
More nodes? n
Provide password for private key? n
Output file: /tmp/elasticsearch-ssl-http.zip
cd /tmp
sudo unzip elasticsearch-ssl-http.zip
sudo cp elasticsearch/http.p12 /etc/elasticsearch/
sudo chown elasticsearch:elasticsearch /etc/elasticsearch/http.p12
sudo chmod 660 /etc/elasticsearch/http.p12
Configure Elasticsearch security settings
Enable security features, configure SSL/TLS for transport and HTTP layers, and set network binding options for production security.
# Cluster and node configuration
cluster.name: production-cluster
node.name: elasticsearch-node1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
Network and discovery settings
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node
Security configuration
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
Transport layer SSL/TLS
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.client_authentication: required
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
HTTP layer SSL/TLS
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: http.p12
Security audit logging
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include: access_denied, access_granted, anonymous_access_denied, authentication_failed, connection_denied, tampered_request, run_as_denied, run_as_granted
Set JVM heap size and memory settings
Configure JVM memory allocation for optimal performance and security. Set heap size to half of available RAM, maximum 32GB.
# Set heap size to 50% of available RAM (adjust based on your system)
-Xms2g
-Xmx2g
Start and enable Elasticsearch service
Start Elasticsearch with the new security configuration and enable automatic startup on system boot.
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch
sudo systemctl status elasticsearch
Set the built-in superuser password
Configure the elastic superuser password for initial cluster access and user management.
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i
Enter a strong password when prompted
Save this password securely - you'll need it for administration
Create custom roles for application access
Define granular roles following the principle of least privilege for different application access patterns.
# Create a read-only role for monitoring applications
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/role/monitoring_user" -H 'Content-Type: application/json' -d'
{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-", "metrics-"],
"privileges": ["read", "view_index_metadata"]
}
]
}'
Create an application role with write access to specific indices
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/role/app_writer" -H 'Content-Type: application/json' -d'
{
"cluster": [],
"indices": [
{
"names": ["app-logs-", "app-data-"],
"privileges": ["create", "create_index", "index", "write"]
}
]
}'
Create a data analyst role with broader read access
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/role/data_analyst" -H 'Content-Type: application/json' -d'
{
"cluster": ["monitor"],
"indices": [
{
"names": ["*"],
"privileges": ["read", "view_index_metadata"]
}
]
}'
Create application users with role assignments
Create dedicated users for different applications and assign them appropriate roles based on their access requirements.
# Create monitoring user
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/user/monitoring_service" -H 'Content-Type: application/json' -d'
{
"password": "MonitoringPass123!",
"roles": ["monitoring_user"],
"full_name": "Monitoring Service Account",
"email": "monitoring@example.com"
}'
Create application writer user
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/user/app_service" -H 'Content-Type: application/json' -d'
{
"password": "AppServicePass123!",
"roles": ["app_writer"],
"full_name": "Application Service Account",
"email": "app@example.com"
}'
Create data analyst user
curl -k -u elastic:your-password -X POST "https://localhost:9200/_security/user/analyst" -H 'Content-Type: application/json' -d'
{
"password": "AnalystPass123!",
"roles": ["data_analyst"],
"full_name": "Data Analyst",
"email": "analyst@example.com"
}'
Generate API keys for programmatic access
Create API keys for applications that need programmatic access without storing user passwords in configuration files.
# Create API key for monitoring application
curl -k -u monitoring_service:MonitoringPass123! -X POST "https://localhost:9200/_security/api_key" -H 'Content-Type: application/json' -d'
{
"name": "monitoring-api-key",
"expiration": "365d",
"role_descriptors": {
"monitoring_role": {
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-", "metrics-"],
"privileges": ["read"]
}
]
}
}
}'
Create API key for application with write access
curl -k -u app_service:AppServicePass123! -X POST "https://localhost:9200/_security/api_key" -H 'Content-Type: application/json' -d'
{
"name": "app-write-api-key",
"expiration": "90d",
"role_descriptors": {
"app_write_role": {
"cluster": [],
"indices": [
{
"names": ["app-logs-*"],
"privileges": ["create_index", "index", "write"]
}
]
}
}
}'
Configure firewall rules for network security
Implement network-level access controls to restrict Elasticsearch access to authorized systems only.
# Install and enable UFW firewall
sudo apt install -y ufw
sudo ufw --force enable
Allow SSH (adjust port if needed)
sudo ufw allow 22/tcp
Allow Elasticsearch HTTPS from specific networks
Replace with your actual network ranges
sudo ufw allow from 10.0.0.0/8 to any port 9200 proto tcp
sudo ufw allow from 172.16.0.0/12 to any port 9200 proto tcp
sudo ufw allow from 192.168.0.0/16 to any port 9200 proto tcp
Allow transport layer communication (node-to-node)
sudo ufw allow from 10.0.0.0/8 to any port 9300 proto tcp
sudo ufw allow from 172.16.0.0/12 to any port 9300 proto tcp
sudo ufw allow from 192.168.0.0/16 to any port 9300 proto tcp
Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
Check firewall status
sudo ufw status verbose
Configure system security limits
Set system resource limits and security parameters optimized for Elasticsearch production deployment.
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536
elasticsearch soft nproc 4096
elasticsearch hard nproc 4096
# Configure system sysctl parameters
sudo tee -a /etc/sysctl.d/99-elasticsearch.conf <Virtual memory settings for Elasticsearch
vm.max_map_count=262144
Network security hardening
net.ipv4.tcp_syncookies=1
net.ipv4.ip_forward=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
EOF
Apply sysctl settings
sudo sysctl -p /etc/sysctl.d/99-elasticsearch.conf
Configure log rotation and audit settings
Set up proper log rotation and audit trail configuration for security monitoring and compliance.
/var/log/elasticsearch/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
copytruncate
su elasticsearch elasticsearch
}
# Set proper permissions on Elasticsearch directories
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/log/elasticsearch
sudo chown -R root:elasticsearch /etc/elasticsearch
sudo chmod 750 /etc/elasticsearch
sudo chmod 660 /etc/elasticsearch/*.yml
Restart Elasticsearch to apply all changes
sudo systemctl restart elasticsearch
Verify your setup
Test the security configuration by verifying SSL/TLS encryption, authentication, and role-based access control.
# Check Elasticsearch cluster health with HTTPS
curl -k -u elastic:your-password "https://localhost:9200/_cluster/health?pretty"
Verify SSL certificate information
openssl s_client -connect localhost:9200 -servername localhost < /dev/null 2>/dev/null | openssl x509 -noout -text | grep -A2 "Subject:"
Test API key authentication
curl -k -H "Authorization: ApiKey your-api-key-credentials" "https://localhost:9200/_cluster/health"
Check security features status
curl -k -u elastic:your-password "https://localhost:9200/_xpack?pretty"
Verify user and role configuration
curl -k -u elastic:your-password "https://localhost:9200/_security/user?pretty"
curl -k -u elastic:your-password "https://localhost:9200/_security/role?pretty"
Test unauthorized access (should fail)
curl -k "https://localhost:9200/_cluster/health"
Check audit logs
sudo tail -f /var/log/elasticsearch/production-cluster_audit.json
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Elasticsearch won't start after security config | Certificate permission issues | sudo chown elasticsearch:elasticsearch /etc/elasticsearch/.p12 && sudo chmod 660 /etc/elasticsearch/.p12 |
| SSL handshake failures | Certificate hostname mismatch | Regenerate HTTP certificates with correct hostnames and IP addresses |
| API requests return 401 Unauthorized | Missing or incorrect authentication | Verify username/password or API key format: base64(api_key_id:api_key) |
| Memory lock warnings in logs | System limits not configured | Configure /etc/security/limits.d/elasticsearch.conf and restart |
| High CPU usage after enabling audit | Excessive audit event logging | Refine audit events in elasticsearch.yml to log only critical events |
| Connection timeouts from applications | Firewall blocking connections | Check firewall rules and ensure application networks are allowed |
| Certificate expired errors | SSL certificates have expired | Regenerate certificates with longer validity period and update keystore |
Next steps
- Setup centralized log aggregation with Elasticsearch 8, Logstash 8, and Kibana 8 (ELK Stack)
- Setup Elasticsearch 8 authentication with LDAP and Active Directory integration
- Configure Elasticsearch 8 cluster with multiple nodes for high availability and scalability
- Setup Elasticsearch monitoring with Metricbeat and Kibana dashboards
- Configure Elasticsearch Index Lifecycle Management (ILM) for automated data retention
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration variables
DOMAIN="${1:-localhost}"
BIND_IP="${2:-127.0.0.1}"
ES_USER="elasticsearch"
ES_HOME="/usr/share/elasticsearch"
ES_CONF="/etc/elasticsearch"
ES_DATA="/var/lib/elasticsearch"
# Usage message
usage() {
echo "Usage: $0 [domain] [bind_ip]"
echo " domain: Domain name for SSL certificate (default: localhost)"
echo " bind_ip: IP address to bind Elasticsearch (default: 127.0.0.1)"
exit 1
}
# Check if domain is valid
if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9.-]+$ ]]; then
echo -e "${RED}Error: Invalid domain name${NC}"
usage
fi
# Cleanup on error
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop elasticsearch 2>/dev/null || true
systemctl disable elasticsearch 2>/dev/null || true
}
trap cleanup ERR
# Check prerequisites
echo -e "${YELLOW}[0/8] Checking prerequisites...${NC}"
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}Error: This script must be run as root${NC}"
exit 1
fi
# Detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
FIREWALL_CMD="firewall-cmd"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
# Install required packages
echo -e "${YELLOW}[1/8] Installing prerequisites...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_UPDATE
$PKG_INSTALL wget gnupg2 curl
else
$PKG_INSTALL wget gnupg2 curl
fi
# Add Elasticsearch repository and install
echo -e "${YELLOW}[2/8] Adding Elasticsearch repository...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | 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" > /etc/apt/sources.list.d/elastic-8.x.list
$PKG_UPDATE
$PKG_INSTALL elasticsearch
else
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
cat > /etc/yum.repos.d/elasticsearch.repo << EOF
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md
EOF
$PKG_INSTALL --enablerepo=elasticsearch elasticsearch
fi
# Configure Elasticsearch security settings
echo -e "${YELLOW}[3/8] Configuring Elasticsearch security...${NC}"
cat > "${ES_CONF}/elasticsearch.yml" << EOF
# Cluster configuration
cluster.name: secure-cluster
node.name: \${HOSTNAME}
# Network settings
network.host: ${BIND_IP}
http.port: 9200
# Path settings
path.data: ${ES_DATA}
path.logs: /var/log/elasticsearch
# Security settings
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.http.ssl.enabled: true
# SSL/TLS Configuration
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.client_authentication: required
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates.p12
# Discovery settings
discovery.type: single-node
EOF
# Set correct permissions
chown root:elasticsearch "${ES_CONF}/elasticsearch.yml"
chmod 640 "${ES_CONF}/elasticsearch.yml"
# Generate SSL certificates
echo -e "${YELLOW}[4/8] Generating SSL certificates...${NC}"
cd "${ES_CONF}"
"${ES_HOME}/bin/elasticsearch-certutil" ca --silent --pem --out ca.zip
unzip -o ca.zip
"${ES_HOME}/bin/elasticsearch-certutil" cert --silent --ca-cert ca/ca.crt --ca-key ca/ca.key --dns "${DOMAIN}" --ip "${BIND_IP}" --pem --out cert.zip
unzip -o cert.zip
"${ES_HOME}/bin/elasticsearch-certutil" cert --silent --ca-cert ca/ca.crt --ca-key ca/ca.key --dns "${DOMAIN}" --ip "${BIND_IP}" --out elastic-certificates.p12
# Set certificate permissions
chown elasticsearch:elasticsearch *.p12 *.zip
chmod 640 *.p12
chown -R elasticsearch:elasticsearch ca/ instance/
chmod -R 640 ca/* instance/*
chmod 750 ca/ instance/
# Configure JVM settings
echo -e "${YELLOW}[5/8] Configuring JVM settings...${NC}"
sed -i 's/-Xms1g/-Xms512m/' /etc/elasticsearch/jvm.options
sed -i 's/-Xmx1g/-Xmx512m/' /etc/elasticsearch/jvm.options
# Start and enable Elasticsearch
echo -e "${YELLOW}[6/8] Starting Elasticsearch service...${NC}"
systemctl daemon-reload
systemctl enable elasticsearch
systemctl start elasticsearch
# Wait for Elasticsearch to start
sleep 30
# Reset built-in user passwords
echo -e "${YELLOW}[7/8] Setting up authentication...${NC}"
ELASTIC_PASSWORD=$("${ES_HOME}/bin/elasticsearch-reset-password" -u elastic -b -s)
KIBANA_PASSWORD=$("${ES_HOME}/bin/elasticsearch-reset-password" -u kibana_system -b -s)
# Create API key for application access
API_KEY_RESPONSE=$(curl -s -u "elastic:${ELASTIC_PASSWORD}" -k -X POST "https://${BIND_IP}:9200/_security/api_key" -H "Content-Type: application/json" -d '{
"name": "app_access_key",
"role_descriptors": {
"app_role": {
"cluster": ["monitor"],
"indices": [
{
"names": ["*"],
"privileges": ["read", "write", "index", "create"]
}
]
}
},
"expiration": "30d"
}')
API_KEY=$(echo "$API_KEY_RESPONSE" | grep -o '"encoded":"[^"]*"' | cut -d'"' -f4)
# Configure firewall
echo -e "${YELLOW}[8/8] Configuring firewall...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
if command -v ufw >/dev/null 2>&1; then
ufw --force enable
ufw allow from "${BIND_IP}" to any port 9200
fi
else
if command -v firewall-cmd >/dev/null 2>&1; then
systemctl start firewalld
systemctl enable firewalld
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='${BIND_IP}' port protocol='tcp' port='9200' accept"
firewall-cmd --reload
fi
fi
# Verification
echo -e "${YELLOW}Verifying installation...${NC}"
if systemctl is-active --quiet elasticsearch; then
echo -e "${GREEN}✓ Elasticsearch service is running${NC}"
else
echo -e "${RED}✗ Elasticsearch service failed to start${NC}"
exit 1
fi
if curl -s -k -u "elastic:${ELASTIC_PASSWORD}" "https://${BIND_IP}:9200" >/dev/null; then
echo -e "${GREEN}✓ Elasticsearch is responding with SSL${NC}"
else
echo -e "${RED}✗ Elasticsearch SSL connection failed${NC}"
exit 1
fi
# Display credentials
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}Elasticsearch 8 Security Setup Complete!${NC}"
echo -e "${GREEN}================================${NC}"
echo "Elasticsearch URL: https://${BIND_IP}:9200"
echo "Username: elastic"
echo "Password: ${ELASTIC_PASSWORD}"
echo "Kibana System Password: ${KIBANA_PASSWORD}"
echo "API Key: ${API_KEY}"
echo ""
echo "SSL Certificate files created in: ${ES_CONF}/"
echo "Configuration file: ${ES_CONF}/elasticsearch.yml"
echo ""
echo -e "${YELLOW}Important: Save these credentials securely!${NC}"
Review the script before running. Execute with: bash install.sh