Elasticsearch: certificates and passwords
If I got a dollar every time I hear “we had to disable security and authentication in Elasticsearch because otherwise it was unusable”, I’d be a bit richer by now ;-)
Honestly, I don’t quite get where the confusion comes from. It’s 2024, yet some of us are still surprised by “what do you mean: certificate and password???”
Let me explain security in Elasticsearch in a few simple paragraphs.

I’ll start by saying this isn’t just about Elasticsearch, but anything that communicates over HTTPS. It could be an HTTP server, a database, or any other system.
To have HTTPS instead of HTTP, you need a certificate. Normally, when we secure communication with our HTTP server (to get the padlock in the address bar), we need to obtain a certificate. Such a certificate is issued by some organization and applies to a specific.server.com.
But how do we know that the certificate is okay? The organization is certified by someone else, who in turn might be certified by another higher authority, creating a chain of trust up to the “highest authority”. The certificate of the highest authority is embedded in our browser or operating system, so we don’t have to click “yes, this certificate and the whole chain are fine” every time we access a secure site.
The problem arises when we have a certificate that is not signed by a “higher authority”, but by ourselves. Maybe we’re running something locally for testing, without any meaningful.address.com, and we don’t want to pay for a certificate, but we still want HTTPS to prevent eavesdropping. For testing in production-like conditions, we use a self-signed certificate and a chain (even if it’s just one link) that nobody known to our operating system has signed.
Elasticsearch generates such a chain by default from version 8, if no “official” certificate is provided.
For example, when we start Elasticsearch using Docker (not using Docker Hub, mind you):
docker run --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.14.2
Elasticsearch will generate its own chain and save it at /usr/share/elasticsearch/config/certs/http_ca.crt.
Now: since this chain is not approved by our OS, we must either say “ignore it” (not recommended), or tell our client software “it’s okay, I trust THIS chain”. And you need to provide THIS chain, simple ;-)
With curl, you can use --cacert for this purpose.
So, something like this (if we want to run curl inside the container with Elasticsearch running) won’t work (because we haven’t trusted the chain):
docker exec es curl https://localhost:9200
To connect to Elasticsearch, we need to do this:
docker exec es curl --cacert /usr/share/elasticsearch/config/certs/http_ca.crt https://localhost:9200
At this point, someone might shout “but it still doesn’t work, and the chain is confirmed!!!!eleven”
Now what we have is a secure connection, meaning no one can eavesdrop on what is being sent and received (although they can still see where the connection is going, when, and roughly how much data is being transferred). To authenticate ourselves, we need to say “mom, it’s me, pass the sandwich” (again, this isn’t just about Elasticsearch but many other systems too).
There are various ways to do this. For tests (automated), providing a username and password in Basic Auth should suffice, so we can set the password after starting the container:
docker exec -it es elasticsearch-reset-password -u elastic --interactive
(I also recommend docker exec -it es elasticsearch-reset-password -u elastic --help for other modes.)
Another option is to set the password in an environment variable when starting the container, for example:
docker run --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "ELASTIC_PASSWORD=changeme" docker.elastic.co/elasticsearch/elasticsearch:8.14.2
Once we have both a secure connection and the password set, we can send a request to Elasticsearch like this:
docker exec es curl --cacert /usr/share/elasticsearch/config/certs/http_ca.crt --user elastic:changeme https://localhost:9200
- Okay, but how do I connect with something other than
curl? - That depends on what you’re connecting with and what options it has.
You can copy the chain and somehow approve it in your client:
docker cp es:/usr/share/elasticsearch/config/certs/http_ca.crt .
You can create a fingerprint and approve the chain by the fingerprint, for example:
docker exec es openssl x509 -noout -fingerprint -sha256 -inform pem -in /usr/share/elasticsearch/config/certs/http_ca.crt
Recap, as there two concepts here:
- one thing is SSL/TLS/HTTPS and secured connection to the (Elasticsearch) server
- another thing is passwords and API keys
Two final notes:
- It’s worth reading the documentation, e.g., on how to connect with the Java client: Connecting with Java client
- In production, instead of passwords, I recommend using API keys: Create API key
- It doesn’t harm to take a look at HTTP TLS/SSL settings
Thanks and happy searching! ;-)