Skip to main content

Elasticsearch

Elasticsearch 8.x acts as the shared data store for every service in the lab: OpenCTI, TheHive, Cortex, and Kibana SIEM.

Configuration

File: config/elasticsearch/elasticsearch.yml

Key settings:

SettingValueReason
discovery.type: single-nodesingle-nodeLab only — disables cluster health waiting
xpack.security.enabled: truetrueEnables authentication for all access
xpack.security.http.ssl.enabled: falsefalsePlain HTTP inside Docker network
xpack.license.self_generated.type: basicbasicEnables SIEM features without paid license
ES_JAVA_OPTS=-Xms2g -Xmx2g2 GBTune to available RAM (host RAM ÷ 4)

Credentials

The elastic superuser password is set via the ELASTIC_PASSWORD environment variable at first container startup. If you change it after the fact, you must use the Elasticsearch API:

curl -X POST -u "elastic:OLD_PASSWORD" \
-H "Content-Type: application/json" \
"http://localhost:9200/_security/user/elastic/_password" \
-d '{"password":"NEW_PASSWORD"}'

Indices used per service

Index prefixService
opencti-*OpenCTI (STIX objects, graph)
thehiveTheHive (cases, observables, alerts)
cortexCortex (job history)
.kibana*Kibana saved objects
.siem-signals-*Kibana SIEM detection alerts
cti-lab-logs-*Logstash (ingested logs)

Health check

curl -u "elastic:${ELASTIC_PASSWORD}" http://localhost:9200/_cluster/health?pretty

Expected output:

{
"cluster_name" : "cti-lab",
"status" : "yellow",
"number_of_nodes" : 1,
...
}

yellow is normal for a single-node cluster (replica shards cannot be assigned).

Useful queries

# List all indices
curl -u "elastic:$ELASTIC_PASSWORD" "http://localhost:9200/_cat/indices?v"

# OpenCTI index sizes
curl -u "elastic:$ELASTIC_PASSWORD" "http://localhost:9200/_cat/indices/opencti-*?v&s=store.size:desc"

# Check a specific index
curl -u "elastic:$ELASTIC_PASSWORD" "http://localhost:9200/thehive/_stats/store?pretty"