OpenSearch Security Part 3: Adding Users and Roles

In this third part of our OpenSearch Security series we will show you how to set up users and roles to control access to indices and data.

In the previous article of this series, we demonstrated how to set up the OpenSearch security plugin by using the demo installer. We also looked at TLS encryption on the transport and REST-layer. Now it’s time to add our first user and assign an OpenSearch security role that governs access to indices and data.

Use Case

In this article, we will implement a very straightforward use case. We will set up two indices on our cluster, named my-index-a and my-index-b.
We will then configure two users. The first user has read and write access to my-index-a, but no access to my-index-b.
The second user will have read-only access to my-index-a, and full access to my-index-b.

Prerequisites

This article assumes that you went through all steps in the previous article, so you have a running OpenSearch cluster with the security demo configuration installed.
To set up the two indices we are going to use, execute the following curl commands:
curl -u admin:admin -k -X PUT "https://localhost:9200/my-index-a?pretty"
curl -u admin:admin -k -X PUT "https://localhost:9200/my-index-b?pretty"
Both calls should return with an output like this:
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "my-index-a"
}
The -u switch adds HTTP Basic Authentication headers to the curl call. For simplicity, we use the admin user, which has full access to the cluster.
We also add the -k switch to accept our self-signed demo TLS certificates. Omitting this switch would result in an error message:
curl: (60) SSL certificate problem: Invalid certificate chain

OpenSearch Security Configuration Handling

All security configuration settings are stored in an OpenSearch index. At the time of writing, this index is called .opendistro_security. This system index is protected and is not accessible for regular users. To make changes, like adding users and roles, we will use the securityadmin command line tool.
The tool will read and parse one or more security configuration files and upload the contents of the files to the .opendistro_security index. Changes to the index will get propagated to each node and will take effect immediately.

Adding Security Roles

The security roles are defined in the file roles.yml:
<OS installation dir>/plugins/opensearch-security/securityconfig/roles.yml
For our use case, we want to add two new roles. Let’s start with the first one. This role should grant a user read- and write access to my-index-a, but no access to my-index-b.
OpenSearch uses a white-list approach for defining access controls: If not explicitly granted, a user has no access to any indices or data. This makes the second part of the use case easy to implement. To deny access to my-index-b we have to do nothing. However, we need to define a security role to grant read and write access to my-index-a.
A security role has a name and two main sections: Cluster-level permissions and index-level permissions. For the sake of simplicity, we will not go into the details of cluster-level permissions in this article.
Defining index-level permissions is also straightforward. We need to define one or more permissions (aka “actions”) that a user is allowed to perform, and we need to specify for which indices these permissions shall apply. For our use case, we define our first role like this:
my-role-a:
  cluster_permissions:
    - cluster_all
  index_permissions:
    - index_patterns:
      - "my-index-a"
      allowed_actions:
        - crud
Here, my-role-a is the name of the security role. You can use any naming schema you like.
Then we define an index pattern to which this role should grant access. As the term “index pattern” implies, you can use concrete index names, but wildcards, regular expressions, and even date math expressions are also allowed. In our case, we just want to grant access to a single index, so we use the concrete index name.
Next, we need to define what actions a user should be allowed to perform on that index. We use one of the pre-defined action groups OpenSearch ships with, named crud. This action group grants create, read, update and delete permissions.
For our second role, we use the following definition. This time, we use two index patterns with different actions:
my-role-b:
  cluster_permissions:
    - cluster_all
  index_permissions:
    - index_patterns:
      - "my-index-a"
      allowed_actions:
        - read
    - index_patterns:
      - "my-index-b"
      allowed_actions:
        - "*"
This role grants read access to my-index-a and full access to my-index-b. Note that for my-index-b, we have simply used a wildcard in the allowed_actions section.

Adding Users

Now that we have defined our two new roles in roles.yml let’s add some users. For this, we edit the file internal_users.yml. We just need to specify the names of our new users and their password hashes.
user-a:
  hash: "..."

user-b:
  hash: "..."
The hash entry is the BCrypt hash of the password. OpenSearch ships with a hash.sh script that you can use to generate a BCrypt hash on your machine directly. The script is in the tools directory. Example:
./hash.sh -p password-a
$2y$12$Zd5/KuxBgl1jSyx2w7UeFePv3KnUF0dE8yniajrNQBNmU1v3Vx6w.
We generate a hash for both users and add it to the entries in internal_users.yml:
user-a:
  hash: "$2y$12$Zd5/KuxBgl1jSyx2w7UeFePv3KnUF0dE8yniajrNQBNmU1v3Vx6w."

user-b:
  hash: "$2y$12$Kowf68LCvmN.Jo3vYRS7/OANBu/g0xr6o6B/8BSzWh56.oQWtref6"

Assigning Users to Roles

As the last step, we need to “connect” our newly created users with the newly created roles. In other words, we define the security roles a user should have.
For this, we edit the file roles_mapping.yml and add two new entries:
my-role-a:
  users:
    - "user-a"

my-role-b:
  users:
    - "user-b"
The top-level entry defines the role name we want to add users to. The users array contains the names of the users.
These two entries basically say: Please add user-a to the my-role-a security role and user-b to the my-role-b security role.

Uploading the Configuration Changes

We are now ready to upload the changed configuration to our OpenSearch cluster. For this, we use the securityadmin command-line tool, also located in the tools folder. We cd into the tools folder and execute:
./securityadmin.sh \
  -cd ../securityconfig/ \
  -cacert ../../../config/root-ca.pem \
  -cert ../../../config/kirk.pem \
  -key ../../../config/kirk-key.pem

The -cd (–configdir) argument tells securityadmin where to find the configuration files.
The following three arguments are required for authentication. Since we want to make changes to the security configuration, we have to use the TLS admin certificate. Regular users are not allowed to perform changes to the security configuration.
We define the root CA that was used to sign the admin certificate (-cacert), the admin TLS PEM certificate (-cert), and the admin TLS certificate private key (-key).
If everything went well, you should see an output like this:
Will connect to localhost:9300 ... done
...
Connected as CN=kirk,OU=client,O=client,L=test,C=de
...
Will update '_doc/roles' with ../securityconfig/roles.yml 
   SUCC: Configuration for 'roles' created or updated
Will update '_doc/rolesmapping' with ../securityconfig/roles_mapping.yml 
   SUCC: Configuration for 'rolesmapping' created or updated
Will update '_doc/internalusers' with ../securityconfig/internal_users.yml 
   SUCC: Configuration for 'internalusers' created or updated
...
Done with success
Done with success! We have created two new users with restricted access to our OpenSearch cluster.
In the next part of this series, we will dig deeper into more flexible ways to assign users to roles. See you there!
Interested? Get in touch!