Creating dedicated Elasticsearch users and roles keeps API clients, ingest components, and human operators off the built-in elastic superuser and makes least-privilege access practical for daily cluster work.
Native users and roles are stored in the .security index and are managed through the _security/user and _security/role APIs. Roles bundle cluster privileges such as monitor with index privileges such as read or view_index_metadata, and a user receives the combined privileges from every assigned role.
Creating or updating either object requires an authenticated account with the manage_security cluster privilege. The built-in elastic user is best treated as a bootstrap account for creating narrower native users, and self-managed clusters that use a private HTTP CA may require curl to trust that CA or use --cacert /path/to/http_ca.crt before the API calls succeed.
Steps to create users and roles in Elasticsearch:
- Authenticate as an administrative user to confirm access to the security APIs.
$ curl --silent --show-error --fail --user elastic "https://localhost:9200/_security/_authenticate?pretty" Enter host password for user 'elastic': { "username" : "elastic", "roles" : [ "superuser" ], "full_name" : null, "email" : null, "metadata" : { "_reserved" : true }, "enabled" : true, "authentication_realm" : { "name" : "reserved", "type" : "reserved" }, "lookup_realm" : { "name" : "reserved", "type" : "reserved" }, "authentication_type" : "realm" }Use any account that already has manage_security, and keep elastic for bootstrap-only administration where possible.
- Create a role with only the required cluster and index privileges.
$ curl --silent --show-error --fail --user elastic -H "Content-Type: application/json" -X PUT "https://localhost:9200/_security/role/logs_reader?pretty" -d '{ "cluster" : ["monitor"], "indices" : [ { "names" : ["logs-*"], "privileges" : ["read", "view_index_metadata"] } ] }' Enter host password for user 'elastic': { "role" : { "created" : true } }Reuse the same role name with PUT to update the stored privilege set, and keep index patterns and privileges as narrow as the workload allows.
- Review the stored role definition before assigning it to a user.
$ curl --silent --show-error --fail --user elastic "https://localhost:9200/_security/role/logs_reader?pretty" Enter host password for user 'elastic': { "logs_reader" : { "cluster" : [ "monitor" ], "indices" : [ { "names" : [ "logs-*" ], "privileges" : [ "read", "view_index_metadata" ], "allow_restricted_indices" : false } ], "applications" : [ ], "run_as" : [ ], "metadata" : { }, "transient_metadata" : { "enabled" : true } } }Checking the saved role first makes it easier to catch an over-broad wildcard or a missing cluster privilege before the user starts authenticating with it.
- Create a native user and assign the role to it.
$ curl --silent --show-error --fail --user elastic -H "Content-Type: application/json" -X PUT "https://localhost:9200/_security/user/logs-viewer?pretty" -d '{ "password" : "ChangeMe-LogsViewer-92!", "roles" : ["logs_reader"], "full_name" : "Logs Viewer" }' Enter host password for user 'elastic': { "created" : true }Replace the sample password before running the request, and avoid saving reusable credentials in shell history, scripts, or ticket notes.
- Authenticate as the new user to confirm the account resolves through the native realm.
$ curl --silent --show-error --fail --user logs-viewer "https://localhost:9200/_security/_authenticate?pretty" Enter host password for user 'logs-viewer': { "username" : "logs-viewer", "roles" : [ "logs_reader" ], "full_name" : "Logs Viewer", "email" : null, "metadata" : { }, "enabled" : true, "authentication_realm" : { "name" : "default_native", "type" : "native" }, "lookup_realm" : { "name" : "default_native", "type" : "native" }, "authentication_type" : "realm" }The default_native realm in the response confirms that the account is being served from the native realm rather than from the reserved built-in user set.
- Confirm the effective privileges for the new user.
$ curl --silent --show-error --fail --user logs-viewer "https://localhost:9200/_security/user/_privileges?pretty" Enter host password for user 'logs-viewer': { "cluster" : [ "monitor" ], "global" : [ ], "indices" : [ { "names" : [ "logs-*" ], "privileges" : [ "read", "view_index_metadata" ], "allow_restricted_indices" : false } ], "applications" : [ ], "run_as" : [ ] }This API only reports the privileges of the currently authenticated user, which makes it a direct least-privilege check after the role assignment.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
