Skip to main content

Secure Elasticsearch Access with SSO, TLS, and Auditing

Secure Elasticsearch access with SSO while keeping clusters off the public internet, authenticating with short-lived client certificates, and recording every request in the audit log.

Setup takes four steps: create a join token, configure the Elasticsearch server, grant access through AlmaForge RBAC, and connect with native clients or the REST API.

Prerequisites​

Elasticsearch CLI​

On your computer, check Java, which the SQL client requires:

Terminal
java -versionopenjdk version "27" 2026-09-15OpenJDK Runtime Environment Homebrew (build 27)OpenJDK 64-Bit Server VM Homebrew (build 27, mixed mode, sharing)

If it is missing:

Terminal
brew install openjdk...==> Summary...export PATH="$(brew --prefix openjdk)/bin:$PATH"java -versionopenjdk version "27" 2026-09-15OpenJDK Runtime Environment Homebrew (build 27)OpenJDK 64-Bit Server VM Homebrew (build 27, mixed mode, sharing)

export prints nothing. Add that line to ~/.zshrc to keep the PATH setting. You will copy the matching SQL client from the database server after installing Elasticsearch. For Linux, install Java through your distribution's package manager.

Elasticsearch server​

On your computer, open a second terminal and SSH into the database server. Replace <ssh-user> and <database-host> with your SSH account and server address:

Terminal
ssh <ssh-user>@<database-host>...[<ssh-user>@<database-host> ~]$

On the database server, check that you can run administrator commands, then check the operating system and installed database package:

Terminal
sudo -v# No output on success. A password prompt may appear first.grep -E '^(ID_LIKE|VERSION_ID)=' /etc/os-releaseID_LIKE="rhel centos fedora"VERSION_ID="9.8"rpm -q elasticsearchelasticsearch-9.5.4-1.x86_64

sudo -v must succeed. If the package check says Elasticsearch is not installed, install it from the Elastic package repository before continuing. Otherwise, the command prints its version.

Use an RPM-based Linux host with dnf for this example.

AlmaForge CLI​

On your computer, check whether alma is installed:

Terminal
alma version --clientClient Version: v2026.1.0+<build>

If the command is not found, install it and repeat the check:

Terminal
curl -fL https://get.almaforge.com/install.sh | sh...==> AlmaForge installed successfullyalma version --clientClient Version: v2026.1.0+<build>

The installer supports Linux and macOS on both amd64 and arm64. See CLI installation for package-specific options.

AlmaForge Cluster​

On your computer, sign in to your cluster and check your permissions.

Get your cluster's hostname from its administrator. If you do not have an AlmaForge cluster yet, complete the standalone deployment before continuing. Replace almaforge.example.com below with that hostname. Run the login command and complete the sign-in in your browser:

Terminal
alma login --proxy=almaforge.example.com:443
text
...
> Profile URL: https://almaforge.example.com:443
Logged in as: [email protected]
Cluster: almaforge.example.com
Roles: admin
...

After signing in, check your account and roles:

Terminal
alma status
text
> Profile URL:        https://almaforge.example.com:443
Logged in as: [email protected]
Cluster: almaforge.example.com
Roles: admin
...

In the status output, check that:

  • Cluster names the cluster you intend to configure.
  • Logged in as shows your account.
  • Roles includes admin.

If admin is missing, ask a cluster administrator to grant that role, then run alma logout and repeat the login and status commands. Continue once Roles includes admin.

Step 1. Create AlmaForge join token​

On your computer, create an AlmaForge join token. The database agent uses it to join your AlmaForge cluster:

Terminal
alma create token --services=databaseToken created.  client_id:     <client-id>  client_secret: <client-secret>

You will copy client_id and client_secret into the agent configuration in Step 2. They are valid for 30 minutes. If they expire before the agent joins, create a new token and use its values.

Step 2. Configure the Elasticsearch server​

Install and configure the agent​

The agent is almad with its Database Service enabled. It connects outward to your cluster on port 443 and carries connections between users and Elasticsearch.

On the Elasticsearch server, check whether almad is installed:

Terminal
almad versionAlmaForge v2026.1.0+<build>

If the command is not found, install the AlmaForge package:

Terminal
curl -fL https://get.almaforge.com/install.sh | sh...==> AlmaForge installed successfullyalmad versionAlmaForge v2026.1.0+<build>

On an RPM-based system, the installer uses the RPM package and installs the almad systemd service. It supports both amd64 and arm64. The version command must succeed before you configure the agent. For other Linux distributions, see Linux package installation.

Already running an agent on this host?

Keep its existing settings and enable ALMA_DATABASE_SERVICE=true. Preserve the other services and label selectors. The join credentials must permit the database service. The local target created below does not need a label selector.

On the Elasticsearch server, open the agent configuration:

Terminal
sudoedit /etc/almaforge/almad.env# Your text editor opens.

For a new agent, enter the settings below. Replace ALMA_PROXY with your cluster's hostname and port 443. Set ALMA_CLIENT_ID and ALMA_CLIENT_SECRET to the values from Step 1:

almad.env:

/etc/almaforge/almad.env
# Database Service agent on the Elasticsearch host.
# Install at /etc/almaforge/almad.env as root:root with mode 0600.
ALMA_PROXY=almaforge.example.com:443

# Printed by `alma create token --services=database`.
ALMA_CLIENT_ID=paste-client-id-here
ALMA_CLIENT_SECRET=paste-client-secret-here

ALMA_DATABASE_SERVICE=true
ALMA_SSH_SERVICE=false

# Agent labels, separate from the database's labels.
ALMA_LABELS=env=demo

Save the file and close the editor. Restrict it to root because it contains the join credentials:

Terminal
sudo chown root:root /etc/almaforge/almad.env# No output on success.sudo chmod 0600 /etc/almaforge/almad.env# No output on success.

Create the TLS certificates​

The agent and Elasticsearch run on the same server. Their connection uses the loopback interface (localhost, or 127.0.0.1), so this traffic stays on that server. A self-signed certificate for localhost is sufficient. You do not need a public hostname or a certificate from a public CA.

Elasticsearch uses the AlmaForge database CA to verify client certificates presented by the agent.

Keep running these commands on the Elasticsearch server.

Download your cluster's public database CA. Replace almaforge.example.com with your cluster's hostname:

Terminal
curl -fsS -o almaforge-db-ca.pem 'https://almaforge.example.com:443/api/v1/auth/export?type=db'# No output on success.openssl x509 -in almaforge-db-ca.pem -noout# No output means the file contains a valid certificate.sudo install -d -o root -g elasticsearch -m 0750 /etc/elasticsearch/certs

Create a self-signed server certificate and private key for localhost:

Terminal
sudo openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \    -subj '/CN=localhost' \    -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' \    -keyout /etc/elasticsearch/certs/server.key -out /etc/elasticsearch/certs/server.crt...-----sudo chown root:elasticsearch /etc/elasticsearch/certs/server.key /etc/elasticsearch/certs/server.crt# No output on success.sudo chmod 0640 /etc/elasticsearch/certs/server.key# No output on success.sudo sh -c 'cat almaforge-db-ca.pem /etc/elasticsearch/certs/server.crt > /etc/elasticsearch/certs/clients.pem'sudo chown root:elasticsearch /etc/elasticsearch/certs/clients.pemsudo chmod 0644 /etc/elasticsearch/certs/clients.pem# No output on success.

Enable TLS in Elasticsearch​

Keep the password for the existing elastic administrator available for creating the sample index. If you do not have it, reset it while the server is still running with its existing TLS configuration:

Terminal
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password --username elastic --interactive

Enter a new password at the prompt and store it in your password manager. This changes the elastic account's password, so update any other clients that use that account.

Open /etc/elasticsearch/roles.yml and add the following role. Preserve any existing role definitions. It permits reading only the demo index:

Terminal
sudoedit /etc/elasticsearch/roles.yml

roles.yml:

/etc/elasticsearch/roles.yml
# Grant the certificate-authenticated REST connection read access to demo.
alma_demo_reader:
indices:
- names: [demo]
privileges: [read, view_index_metadata]

Open /etc/elasticsearch/elasticsearch.yml on the server:

Terminal
sudoedit /etc/elasticsearch/elasticsearch.yml# Your text editor opens.

Replace the existing xpack.security.http.ssl block with the PEM settings below, and add the anonymous authentication settings. Remove any HTTP TLS keystore.path, truststore.path, keystore.password, keystore.key_password, and truststore.password settings from the old block. The PEM key and certificate cannot be combined with the old stores. Preserve the existing xpack.security.transport.ssl settings, which protect communication between Elasticsearch nodes.

elasticsearch.yml:

/etc/elasticsearch/elasticsearch.yml
# Elasticsearch TLS and anonymous read access for AlmaForge.
# Merge into /etc/elasticsearch/elasticsearch.yml.
xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: /etc/elasticsearch/certs/server.key
xpack.security.http.ssl.certificate: /etc/elasticsearch/certs/server.crt
xpack.security.http.ssl.certificate_authorities:
[/etc/elasticsearch/certs/clients.pem]
xpack.security.http.ssl.client_authentication: required
xpack.security.authc.anonymous.username: anonymous
xpack.security.authc.anonymous.roles: alma_demo_reader
xpack.security.authc.anonymous.authz_exception: true

Save the file. List the secure setting names in the Elasticsearch keystore:

Terminal
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore list

Remove each of these HTTP TLS store entries if it appears in the list:

  • xpack.security.http.ssl.keystore.secure_password
  • xpack.security.http.ssl.keystore.secure_key_password
  • xpack.security.http.ssl.truststore.secure_password

For each listed entry, replace <setting-name> below with that name:

Terminal
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore remove <setting-name>

Keep transport TLS entries and all other secure settings. Restart Elasticsearch after removing the obsolete HTTP store settings:

Terminal
sudo systemctl restart elasticsearch# No output on success.systemctl is-active elasticsearchactive

Create the sample index and documents as the elastic administrator. Each command prompts for its password. Connections through AlmaForge use the anonymous read role and cannot create or modify these documents:

Terminal
sudo curl -fsS --user elastic --cacert /etc/elasticsearch/certs/server.crt \    --cert /etc/elasticsearch/certs/server.crt \    --key /etc/elasticsearch/certs/server.key \    -H 'Content-Type: application/json' -X PUT https://localhost:9200/demo/_doc/1 -d '{"name": "alice"}'sudo curl -fsS --user elastic --cacert /etc/elasticsearch/certs/server.crt \    --cert /etc/elasticsearch/certs/server.crt \    --key /etc/elasticsearch/certs/server.key \    -H 'Content-Type: application/json' -X PUT https://localhost:9200/demo/_doc/2 -d '{"name": "bob"}'sudo curl -fsS --user elastic --cacert /etc/elasticsearch/certs/server.crt \    --cert /etc/elasticsearch/certs/server.crt \    --key /etc/elasticsearch/certs/server.key \    -H 'Content-Type: application/json' -X PUT https://localhost:9200/demo/_doc/3 -d '{"name": "carol"}'

Register Elasticsearch with the agent​

Still on the Elasticsearch server, display the public server certificate:

Terminal
sudo cat /etc/elasticsearch/certs/server.crt-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----

Copy the entire certificate, including the BEGIN and END lines. The agent uses it to verify Elasticsearch. Then open the target configuration:

Terminal
sudo install -d -o root -g root -m 0700 /etc/almaforge/almad.d# No output on success.sudoedit /etc/almaforge/almad.d/elasticsearch.yaml# Your text editor opens.

For a new file, copy the following YAML. If the file already exists, update the existing resource and preserve its other settings. Replace <paste the complete server certificate here> with the certificate you copied. Indent every certificate line by six spaces beneath caCert: |:

target.yaml.in:

/etc/almaforge/almad.d/elasticsearch.yaml
# Connect the local Elasticsearch HTTPS listener through AlmaForge.
# Replace the placeholder with the contents of /etc/elasticsearch/certs/server.crt.
apiVersion: almaforge.com/v1
kind: DatabaseTarget
metadata:
name: elasticsearch
labels:
db: elasticsearch
env: prod
spec:
protocol: elasticsearch
uri: localhost:9200
tls:
caCert: |
<paste the complete server certificate here>

Check that the certificate includes both its BEGIN and END lines, then save the file and close the editor. Restrict the file to root:

Terminal
sudo chown root:root /etc/almaforge/almad.d/elasticsearch.yaml# No output on success.sudo chmod 0600 /etc/almaforge/almad.d/elasticsearch.yaml# No output on success.

Start and check the agent​

Enable the services at boot and start the agent:

Terminal
sudo systemctl enable elasticsearch almad...sudo systemctl restart almad# No output on success.systemctl is-active elasticsearch almadactiveactive

Both services must report active. If either fails, inspect its log:

Terminal
sudo journalctl -u elasticsearch -u almad -n 50 --no-pager...<date> <time> <database-host> almad[<pid>]: <log-message>...

Resolve any reported errors before continuing. The agent reads the target configuration at startup, so restart almad after changing /etc/almaforge/almad.d/elasticsearch.yaml.

Install the SQL client​

On the Elasticsearch server, check the installed package version:

Terminal
rpm -q elasticsearchelasticsearch-9.5.4-1.x86_64

On your computer, copy the matching SQL client JAR. Replace <ssh-user> and <database-host> with the account and address used above. If your server version differs, replace 9.5.4 in both the copy command and the launcher below:

Terminal
mkdir -p "$HOME/.local/share/elasticsearch-sql-cli" "$HOME/.local/bin"export PATH="$HOME/.local/bin:$PATH"scp <ssh-user>@<database-host>:/usr/share/elasticsearch/bin/elasticsearch-sql-cli-9.5.4.jar \    "$HOME/.local/share/elasticsearch-sql-cli/"elasticsearch-sql-cli-9.5.4.jar             100% ...

mkdir and export print nothing. Keep the export line in ~/.zshrc for future terminals. Create the launcher:

Terminal
cat > "$HOME/.local/bin/elasticsearch-sql-cli" <<'EOF'#!/bin/shexec java --enable-native-access=ALL-UNNAMED -jar "$HOME/.local/share/elasticsearch-sql-cli/elasticsearch-sql-cli-9.5.4.jar" "$@"EOFchmod 0755 "$HOME/.local/bin/elasticsearch-sql-cli"

Both commands finish without output. The Java option lets the client's terminal library run on modern Java. Check the launcher:

Terminal
elasticsearch-sql-cli --helpElasticsearch SQL CLI
Non-option arguments:uri
Option Description------ -----------...-h, --help Show help...

The command must print its usage and options without a Java or file-not-found error. The Elasticsearch SQL CLI reference describes the standalone client.

Step 3. Grant access​

On your computer, confirm that the agent has registered the database:

Terminal
alma get dbHost             Name            Protocol       URI                 Labels             Version---------------- --------------- -------------- ------------------- ------------------ --------<database-host>  elasticsearch   elasticsearch  <database-uri>      db=elasticsearch ... 2026.1.0

Check that elasticsearch appears with protocol elasticsearch and your database server in the Host column. If either is missing, see Database is missing or has no host.

Download elasticsearch-access.yaml and save it locally. This role allows the session user demo on targets labeled db: elasticsearch. Elasticsearch receives these connections as anonymous, with the permissions configured on the server. The session user does not create or select an Elasticsearch account in this setup. AlmaForge records the signed-in person on the session.

examples/databases/elasticsearch/roles/elasticsearch-access.yaml
# Allow access to the dedicated Elasticsearch target.
# Elasticsearch grants anonymous read access to the sample demo index.
apiVersion: almaforge.com/v1
kind: Role
metadata:
name: elasticsearch-access
spec:
allow:
databaseLabels:
db: elasticsearch
databaseUsers:
- demo

Apply the role:

Terminal
alma apply -f elasticsearch-access.yamlrole 'elasticsearch-access' has been created

Creating the role does not assign it to anyone. To grant it to an SSO group, list your connectors:

Terminal
alma get oidcKind          Name------------- -----------OIDCConnector company-sso

Find the connector your team uses to sign in. Replace <connector-name> with its name from the output, then open it in your text editor:

Terminal
alma edit oidc/<connector-name># Your text editor opens. After saving your changes:oidc connector "<connector-name>" has been updated

Under spec.claimsToRoles, find the entry for the group that should have Elasticsearch access. Add elasticsearch-access to that entry's roles list. Preserve the existing roles and mappings, including those that grant admin, then save and close the editor. The command applies your changes. If the group has no mapping yet, follow OIDC role mapping to add one.

Step 4. Connect​

On your computer, sign out of saved sessions, then sign back in with an account in the group granted access in Step 3 to get a session with the new role. logout clears saved sessions for all clusters. Use your cluster's hostname in the login command:

Terminal
alma logoutLogged out all users from all proxies.alma login --proxy=almaforge.example.com:443...> Profile URL:        https://almaforge.example.com:443  Logged in as:       [email protected]  Cluster:            almaforge.example.com  Roles:              elasticsearch-access  ...alma status> Profile URL:        https://almaforge.example.com:443  Logged in as:       [email protected]  Cluster:            almaforge.example.com  Roles:              elasticsearch-access  ...

Check that Roles includes elasticsearch-access. If it is missing, check your SSO group membership and the mapping from Step 3 before continuing.

Connect to the sample data:

Terminal
alma db connect --db-user=demo elasticsearch...sql> SELECT name FROM demo ORDER BY name;     name---------------alicebobcarol

The three names confirm that the SQL client reaches the sample index through AlmaForge. Type exit; to close the SQL client. It prints Bye!.

Advanced configuration​

REST API access​

On your computer, keep this command running in a separate terminal:

Terminal
alma proxy db --tunnel --port 9200 --db-user=demo elasticsearchStarted authenticated tunnel for the Elasticsearch database "elasticsearch" in cluster "almaforge.example.com" on 127.0.0.1:9200....

In your other local terminal, send a request to that listener:

Terminal
curl 'http://127.0.0.1:9200/demo/_count?pretty'{  "count" : 3,  "_shards" : {    "total" : 1,    "successful" : 1,    "skipped" : 0,    "failed" : 0  }}

The local listener uses HTTP. The tunnel authenticates you to AlmaForge, and the agent uses HTTPS with a client certificate to reach Elasticsearch.

A count of 3 confirms access to the sample documents. Press Ctrl+C in the proxy terminal when finished.

Architecture​

The database agent runs beside Elasticsearch and opens an outbound tunnel to the AlmaForge Proxy. Elasticsearch listens on localhost and trusts the agent's client certificates.

Elasticsearch database access topology

Troubleshooting​

Database is missing or has no host​

On the database server, check that /etc/almaforge/almad.d/elasticsearch.yaml exists and ALMA_DATABASE_SERVICE=true is set in /etc/almaforge/almad.env. The agent must reach your cluster on port 443. Inspect its log:

Terminal
sudo journalctl -u almad -n 50 --no-pager...<date> <time> <database-host> almad[<pid>]: <log-message>...

Restart almad after correcting its configuration, then repeat alma get db on your computer.

Access is denied​

Run alma status and check for elasticsearch-access. If it is missing, verify the SSO group mapping from Step 3, then sign out and back in. The role's database labels must match the target file.

The SQL client does not start​

Check that Java and elasticsearch-sql-cli are on your PATH, and that the launcher names the JAR version copied from your server. Run the SQL client in an interactive terminal.

Requests return anonymous​

This is the expected database identity for this example. The client certificate controls which connections Elasticsearch accepts. Its anonymous role supplies database permissions after that TLS check.

Elasticsearch fails after TLS configuration changes​

The packaged installation can contain passwords for its original PKCS#12 stores. The setup removes those specific keystore password entries before switching to PEM files. Inspect the Elasticsearch log for any remaining setting that refers to the old stores.

Limitations​

  • In this release, AlmaForge does not provision Elasticsearch users. --db-user selects the client certificate's common name. Elasticsearch's authentication configuration determines the account used for requests.
  • This example maps all certificate-authenticated connections without HTTP credentials to the same anonymous account. For distinct database identities, configure an Elasticsearch PKI realm and certificate-to-role mappings.
  • The role's databaseNames does not restrict indices. Configure Elasticsearch roles to limit which indices an account can access.

References​