How to access OpenSearch using OAuth¶
This guide shows how to authenticate OpenSearch requests with OAuth tokens issued by the Canonical Identity Platform (Hydra).
Prerequisites¶
Minimum 4 CPUs, 16 GB RAM
Network access between the host and LXD containers
MicroK8s installed locally (for the Identity Platform)
A working LXD cloud with Juju bootstrapped (see the Environment setup stage of our tutorial for more details)
Deploy OpenSearch on LXD¶
Deploy OpenSearch and the data-integrator charm in a new model:
juju add-model opensearch-model localhost/localhost
juju deploy opensearch -n 3 --channel 2/stable
juju deploy data-integrator --channel=stable \
--config index-name=admin-index \
--config extra-user-roles=admin
At this stage, opensearch is blocked until it is integrated with a TLS provider, and data-integrator is blocked
until it is integrated with OpenSearch. Both integrations are created later in this guide.
Deploy the Identity Platform on MicroK8s¶
The Identity Platform runs on Kubernetes. The steps below use MicroK8s, but any Juju-supported K8s cluster will work.
Prepare MicroK8s¶
Install MicroK8s and enable the required addons:
sudo snap install microk8s --classic
sudo microk8s enable hostpath-storage dns
sudo microk8s enable metallb:10.0.0.2-10.0.0.5
Note
The MetalLB address range must provide at least two IP addresses
(one for traefik-public and one for traefik-admin).
If you are also running COS Lite or other LoadBalancer services on the
same MicroK8s instance, increase the range accordingly.
Add MicroK8s to your Juju controller:
sudo microk8s config > microk8s-cluster.yaml
export KUBECONFIG="$PWD/microk8s-cluster.yaml"
juju add-k8s microk8s-cluster -c <controller-name>
Deploy Identity Platform¶
Create a model on the MicroK8s cloud and deploy the Identity Platform bundle:
juju add-model -c <controller-name> oauth microk8s-cluster/localhost
juju deploy identity-platform --channel edge --trust true
Wait until all units become active. The kratos-external-idp-integrator will remain blocked — this is expected and
does not affect OAuth functionality.
Create cross-model integrations¶
The Identity Platform and OpenSearch run in separate models (and potentially on different clouds). Use Juju cross-model offers to connect them.
Offer certificates and OAuth from the Identity Platform model¶
Switch to the Identity Platform model and offer the certificates and OAuth endpoints:
juju switch oauth
juju offer self-signed-certificates:certificates
juju offer hydra:oauth
Consume and integrate in the OpenSearch model¶
Switch to the OpenSearch model, consume the offers, and integrate them with OpenSearch:
juju switch opensearch-model
juju consume admin/oauth.self-signed-certificates
juju consume admin/oauth.hydra
juju integrate opensearch:certificates admin/oauth.self-signed-certificates
juju integrate opensearch admin/oauth.hydra
Wait until all opensearch units become active:
watch juju status
Create an OAuth client and obtain a token¶
With the Identity Platform running, create an OAuth client in Hydra and use it to request an access token.
Create a client in Hydra¶
Create an OAuth client:
juju switch oauth
juju run hydra/leader create-oauth-client \
grant-types='["client_credentials"]' \
audience='["opensearch"]' \
scope='["openid","profile","email","phone","offline"]'
Record the client-id and client-secret from the output.
Get the Hydra public URL¶
Retrieve the proxied endpoints from Traefik:
juju run traefik-public/0 show-proxied-endpoints
Note the hydra.url value (e.g. https://10.0.0.3/oauth-hydra).
Set environment variables:
export OAUTH_CLIENT_ID=<client-id>
export OAUTH_CLIENT_SECRET=<client-secret>
export HYDRA_URL=<hydra-url>
Retrieve the CA certificate¶
Both Hydra and OpenSearch serve TLS certificates issued by the self-signed-certificates charm, so the same CA verifies
both. Save it to a file:
juju run self-signed-certificates/leader get-ca-certificate --format=json \
| jq -r '.[].results."ca-certificate"' > cert.pem
Note
Use --cacert cert.pem with every curl command below so that TLS certificates are
verified. Do not use curl -k (--insecure), which disables verification entirely and
exposes the request, including the client secret and the access token.
Fetch an access token¶
Request an access token from Hydra using the client credentials:
curl --cacert cert.pem -u "${OAUTH_CLIENT_ID}:${OAUTH_CLIENT_SECRET}" \
-X POST "${HYDRA_URL}/oauth2/token" \
-d "scope=openid" \
-d "grant_type=client_credentials" \
-d "audience=opensearch"
Export the token from the JSON response:
export OAUTH_ACCESS_TOKEN=<access-token>
Test the token (before role mapping)¶
Switch to the OpenSearch model and query the cluster with the token:
juju switch opensearch-model
export OPENSEARCH_ADDRESS="$(juju status | grep opensearch/0 | awk -F' ' '{print $5}')"
curl --cacert cert.pem -H "Authorization: Bearer ${OAUTH_ACCESS_TOKEN}" \
"https://${OPENSEARCH_ADDRESS}:9200/_cat/indices"
Expect a 403 security_exception — the client has no mapped roles yet.
{"error":{"root_cause":[{"type":"security_exception","reason":"no permissions for [indices:monitor/settings/get] and User [name=e9c3b483-90be-4843-b821-1152e40aaa0a, backend_roles=[], requestedTenant=null]"}],"type":"security_exception","reason":"no permissions for [indices:monitor/settings/get] and User [name=e9c3b483-90be-4843-b821-1152e40aaa0a, backend_roles=[], requestedTenant=null]"},"status":403}
Get a username from the Data Integrator¶
The data-integrator charm only receives a user once it is integrated with OpenSearch. Integrate the two applications:
juju integrate data-integrator opensearch
Wait until data-integrator becomes active:
watch juju status
Retrieve credentials from the data-integrator charm:
juju run data-integrator/0 get-credentials
Note the username field (e.g. opensearch-client_4):
export DATA_INTEGRATOR_USER=<username>
Configure role mapping¶
Map the OAuth client ID to the Data Integrator user:
juju config opensearch roles_mapping="{\"$OAUTH_CLIENT_ID\":\"$DATA_INTEGRATOR_USER\"}"
Wait for the charm to apply the change:
juju status --watch 5s
Verify access¶
Re-run the same request — it should now succeed:
curl --cacert cert.pem -H "Authorization: Bearer ${OAUTH_ACCESS_TOKEN}" \
"https://${OPENSEARCH_ADDRESS}:9200/_cat/indices"
You should now receive a 200 OK response with a list of indices, for example:
green open .plugins-ml-config QnsDThyaTAKw8cASRYeQMw 1 0 1 0 4kb 4kb
green open .opensearch-observability coXcpdLWSOqbSQ136tbADg 1 0 0 0 208b 208b
green open top_queries-2025.08.29-70656 GDHtcml_R6Okh2siIKgmPw 1 0 40 6 108kb 108kb
green open .opendistro_security RPVY1SdfT_KzAPAX-aUCuw 1 0 10 1 71kb 71kb
yellow open admin-index 1BQKqmjTQVa6_CeBTi53Gw 1 1 0 0 208b 208b
green open .charm_node_lock 8KbPHHy3QneIW8uWbTuBhQ 1 0 1 0 4.1kb 4.1kb
Next steps¶
Enable JWT authentication — an alternative token-based authentication method.
Integrate with an application — connect client applications using the
data-integratorcharm.