Why?
If you have an existing PKI infrastructure, mutual TLS is a convenient way to do authentication and authorization. OpenSearch supports it, although the documentation is lacking in practical examples. Let's fix that!
Configuring OpenSearch Security
Files we're touching
This post uses the OpenSearch security YAML files for configuration. While the docs recommend "creat[ing] other users, roles, mappings, action groups, and tenants using OpenSearch Dashboards or the REST API," we're going against that advice to make things a bit more clear.
In a typical Deb or RPM installation, all files listed are found in
/etc/opensearch/opensearch-security.
| Filename | Purpose |
|---|---|
| config.yml | define valid auth methods, ordering |
| internal_users.yml | Define/configure internal users (not needed for mTLS auth) |
| roles.yml | assigns permissions to roles |
| roles_mapping.yml | assigns roles created in roles.yml with users |
Working Example with mTLS
Below is a working snippet of config.yml from my lab. Basic auth is enabled as well.
_meta:
type: "config"
config_version: "2"
config:
dynamic:
http:
anonymous_auth_enabled: true
authc:
basic_internal_auth_domain:
http_enabled: true
transport_enabled: true
order: "4"
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: intern
clientcert_auth_domain:
description: "Authenticate via SSL client certificates"
http_enabled: true
transport_enabled: false
order: 2
http_authenticator:
type: clientcert
config:
username_attribute: cn #optional, if omitted DN becomes username
challenge: false
authentication_backend:
type: noop
Note username_attribute: cn above. That means we are using the client certificate's Common Name to determine role mappings. The default is to use the full Distingushed Name (DN).
In my lab, I have a vector client that needs to write to OpenSearch. Let's look at its certificate CN:
cat vector-client.service.rl.crt | certtool -i | grep "Subject:"
Subject: CN=vector-client.service.rl,OU=clients,O=🀚🀛🀜 🀈🀉🀊 🀓🀔🀕 🀀🀀 🀄︎🀄︎ RIICHILAB 🀄︎
We also need to make sure the client cert has "TLS WWW Client" listed as a Key Purpose.
cat vector-client.service.rl.crt | certtool -i | grep -3 "Purpose"
Extensions:
Key Purpose (not critical):
TLS WWW Client.
The CN in the certificate has to match sure the user definition in roles_mapping.yml:
vector_role:
users:
- "vector-client.service.rl"
And the role vector_role must be defined in roles.yml:
vector_role:
reserved: false
hidden: false
cluster_permissions:
- "indices:data/write/bulk"
- "cluster_monitor"
index_permissions:
- index_patterns:
- "logs-*"
allowed_actions:
- "data_access"
- "create_index"
Once all these things are in place, run the apply-security-admin.sh script to activate the config. See my earlier blog post for more details on this script and how it works.
Troubleshooting
It doesn't always work the first time. Or the second, third, etc...
Here are some ways to troubleshoot when you don't get the results you're expecting.
For the API calls below, I am invoking cURL as follows:
curl -XGET -H 'accept: application/json' --cert ${client_cert} --key ${client_key} --cacert ${ca} https://${opensearch_endpoint}:9200/${api_route}
Some API calls require super admin permissions, others should be run using the client certificate. Consult the table below for more details. (And sorry the table renders poorly, I'm working on it ;)
| API Route | As | Purpose | Example Output |
|---|---|---|---|
_plugins/_security/authinfo?pretty |
client | What users and roles does OpenSearch associate with this certificate? | { "user" : "User [name=vector-client.service.rl, backend_roles=[], requestedTenant=null]", "user_name" : "vector-client.service.rl", "user_requested_tenant" : null, "remote_address" : "172.19.98.22:58992", "backend_roles" : [ ], "custom_attribute_names" : [ ], "roles" : [ "vector_role" ], "tenants" : { "vector-client.service.rl" : true }, "principal" : "CN=vector-client.service.rl,OU=clients,O=🀚🀛🀜 🀈🀉🀊 🀓🀔🀕 🀀🀀 🀄︎🀄︎ RIICHILAB 🀄︎", "peer_certificates" : "2", "sso_logout_url" : null } |
_plugins/_security/api/roles/vector_role |
super admin | Does this role exist, and does it have the permissions I expect? | {"vector_role":{"cluster_permissions":["indices:data/write/bulk","cluster_monitor"],"hidden":false,"index_permissions":[{"allowed_actions":["data_access","create_index"],"fls":[],"index_patterns":["logs-*"],"masked_fields":[]}],"reserved":false,"static":false,"tenant_permissions":[]}} |
_plugins/_security/api/rolesmapping |
super admin | Is the client certificate CN associated with the role I expect it to be? | {"rbac_admin_role":{"and_backend_roles":[],"backend_roles":["rbac_admin_role"],"hidden":false,"hosts":[],"reserved":false,"users":[]},"kibana_server_role":{"and_backend_roles":[],"backend_roles":[],"hidden":false,"hosts":[],"reserved":false,"users":["dashboards-client.service.rl"]},"operator_role":{"and_backend_roles":[],"backend_roles":[],"hidden":false,"hosts":[],"reserved":false,"users":["operator","[email protected]"]},"anonymous_users_role":{"and_backend_roles":[],"backend_roles":["opendistro_security_anonymous_backendrole"],"hidden":false,"hosts":[],"reserved":false,"users":[]},"all_access":{"and_backend_roles":[],"backend_roles":["admin"],"description":"Maps admin to all_access","hidden":false,"hosts":[],"reserved":true,"users":[]},"vector_role":{"and_backend_roles":[],"backend_roles":[],"hidden":false,"hosts":[],"reserved":false,"users":["vector-client.service.rl"]}} |
Permissions are a whole 'nother ball of wax...
You now have a better idea of how to configure OpenSearch security with mutual TLS. However, figuring out what permissions are needed for which use cases is a bit more of a hassle. I hope to share more details on specific clients (such as vector) in future blog posts. Stay tuned!