This article is part of a series:
- OpenSearch LDAP Authentication & Active Directory
- PKI authentication in OpenSearch
Quick links:
Introduction and background
In this article, we will explore how to use Active Directory (AD) via Lightweight Directory Access Protocol (LDAP). The example files can be found here.
What is Active Directory?
Active Directory (AD) is a Microsoft implementation service which serves the purpose of locating, securing, managing, and organizing computer and network resources. These resources include files, users, groups, peripherals and network devices.
One of the main benefits is the centralization of resource management. AD simplifies the administrator’s tasks, and provides a set of security tools for permission management.
Lightweight directory access protocol (LDAP) is one of the protocols supported by Active Directory and allows users to locate data about the mentioned resources.
Configuring LDAP Authentication
We are going to use docker-compose to configure a nLDAP server, an LDAP admin interface, and a single node OpenSearch cluster.
The files we need to configure are:
- Config.yml
- Internal_users.yml
- Roles_mapping.yml
- Directory.ldif
config.yml
This file contains LDAP details and credentials:
--- _meta: type: "config" config_version: 2 config: dynamic: http: anonymous_auth_enabled: false authc: internal_auth: order: 0 description: "HTTP basic authentication using the internal user database" http_enabled: true transport_enabled: true http_authenticator: type: basic challenge: false authentication_backend: type: internal ldap_auth: order: 1 description: "Authenticate using LDAP" http_enabled: true transport_enabled: true http_authenticator: type: basic challenge: false authentication_backend: type: ldap config: enable_ssl: false enable_start_tls: false enable_ssl_client_auth: false verify_hostnames: true hosts: - openldap:389 bind_dn: cn=readonly,dc=example,dc=org password: changethistoo userbase: ou=People,dc=example,dc=org usersearch: (cn={0}) username_attribute: cn authz: ldap_roles: description: "Authorize using LDAP" http_enabled: true transport_enabled: true authorization_backend: type: ldap config: enable_ssl: false enable_start_tls: false enable_ssl_client_auth: false verify_hostnames: true hosts: - openldap:389 bind_dn: cn=readonly,dc=example,dc=org password: changethistoo userbase: ou=People,dc=example,dc=org usersearch: (cn={0}) username_attribute: cn skip_users: - admin - kibanaserver rolebase: ou=Groups,dc=example,dc=org rolesearch: (uniqueMember={0}) userroleattribute: null userrolename: disabled rolename: cn resolve_nested_roles: false
LDAP supports both authentication (user and password validation) and authorization (map LDAP groups to OpenSearch roles.
The config blocks are almost the same for both authc and authz. You just need to provide the bind_dn, and the user/roles base.
internal_users.yml
Here we override the initial users to only have admin and kibanaserver users available, and take all the rest of the users from the LDAP server.
--- # This is the internal user database # The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh _meta: type: "internalusers" config_version: 2 admin: hash: "$2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG" reserved: true backend_roles: - "admin" description: "Demo admin user" kibanaserver: hash: "$2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H." reserved: true description: "Demo kibanaserver user"
roles_mapping.yml
This file contains the instructions to assign OpenSearch roles to the LDAP users depending on the group.
--- _meta: type: "rolesmapping" config_version: 2 all_access: reserved: false backend_roles: - "admin" - "Administrator" description: "Maps admin to all_access" own_index: reserved: false users: - "*" description: "Allow full access to an index named like the username" kibana_user: reserved: false backend_roles: - "kibanauser" - "Developers" description: "Maps kibanauser to kibana_user" readall: reserved: false backend_roles: - "readall" - "Developers" manage_snapshots: reserved: false backend_roles: - "snapshotrestore" - "Developers" kibana_server: reserved: true users: - "kibanaserver"
For example, the “Developers” group will have read access to all the documents, and administrators will have access to do anything.
directory.ldif
This file contains the LDAP components (users, groups) and will be useful to fill our users’ database.
We will add the following:
Users:
Gllermaly -> Administrator
Jsmith -> Developer
# — OUs ————————————-
dn: ou=Groups,dc=example,dc=org
objectClass: organizationalunit
objectClass: top
ou: Groups
dn: ou=People,dc=example,dc=org
objectClass: organizationalunit
objectClass: top
ou: People
# — People ———————————-
dn: cn=gllermaly,ou=People,dc=example,dc=org
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
cn: gllermaly
userpassword: password
givenname: Gustavo
sn: Llermaly
mail: gustavo@llermaly.com
uid: 1001
dn: cn=jsmith,ou=People,dc=example,dc=org
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
cn: jsmith
userpassword: password
givenname: John
sn: Smith
mail: john@smith.com
uid: 1002
# — Groups ———————————-
dn: cn=Administrator,ou=Groups,dc=example,dc=org
objectClass: groupofuniquenames
objectClass: top
ou: Groups
cn: Administrator
uniquemember: cn=gllermaly, ou=People, dc=example,dc=org
dn: cn=Developers,ou=Groups,dc=example,dc=org
objectClass: groupofuniquenames
objectClass: top
ou: Groups
cn: Developers
uniquemember: cn=gllermaly, ou=People, dc=example,dc=org
uniquemember: cn=jsmith, ou=People, dc=example,dc=org
Putting it all together
Now you can create the docker-compose file that will use all these files. Put everything in the same folder:
version: '3' services: opensearch-ldap-node1: image: opensearchproject/opensearch:2.2.0 container_name: opensearch-ldap-node1 environment: - cluster.name=opensearch-ldap-cluster - node.name=opensearch-ldap-node1 - discovery.seed_hosts=opensearch-ldap-node1 - cluster.initial_master_nodes=opensearch-ldap-node1 - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems hard: 65536 volumes: - opensearch-ldap-data1:/usr/share/opensearch/data - ./config.yml:/usr/share/opensearch/plugins/opensearch-security/securityconfig/config.yml - ./internal_users.yml:/usr/share/opensearch/plugins/opensearch-security/securityconfig/internal_users.yml - ./roles_mapping.yml:/usr/share/opensearch/plugins/opensearch-security/securityconfig/roles_mapping.yml ports: - 9200:9200 - 9600:9600 # required for Performance Analyzer networks: - opensearch-net openldap: image: osixia/openldap container_name: openldap command: --copy-service # seemingly required to load directory.ldif ports: - 389:389 - 636:636 environment: - LDAP_ADMIN_PASSWORD=changethis - LDAP_READONLY_USER=true - LDAP_READONLY_USER_PASSWORD=changethistoo volumes: - ./directory.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/directory.ldif networks: - opensearch-net openldap-admin: image: osixia/phpldapadmin container_name: openldap-admin ports: - 6444:443 environment: - PHPLDAPADMIN_LDAP_HOSTS=openldap networks: - opensearch-net volumes: opensearch-ldap-data1: networks: opensearch-net:
Now run docker-compose up and wait for the containers to be ready.
The last step is to apply the changes in the security settings running the built in script. SSH Into the node and run the following commands:
1
"/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh" -f "/usr/share/opensearch/plugins/opensearch-security/securityconfig/config.yml" -icl -key "/usr/share/opensearch/config/kirk-key.pem" -cert "/usr/share/opensearch/config/kirk.pem" -cacert "/usr/share/opensearch/config/root-ca.pem" -nhnv
2
"/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh" -f "/usr/share/opensearch/plugins/opensearch-security/securityconfig/internal_users.yml" -icl -key "/usr/share/opensearch/config/kirk-key.pem" -cert "/usr/share/opensearch/config/kirk.pem" -cacert "/usr/share/opensearch/config/root-ca.pem" -nhnv
3
"/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh" -f "/usr/share/opensearch/plugins/opensearch-security/securityconfig/roles_mapping.yml" -icl -key "/usr/share/opensearch/config/kirk-key.pem" -cert "/usr/share/opensearch/config/kirk.pem" -cacert "/usr/share/opensearch/config/root-ca.pem" -nhnv
Now you can run queries authenticating with the LDAP users.
This query should succeed as gllermaly user is an Administrator
curl -XPUT 'https://localhost:9200/new-index/_doc/1' -H 'Content-Type: application/json' -d '{"title": "My document"}' -u 'gllermaly:password' -k
This one will fail because the jsmith user does not have write permissions:
curl -XPUT 'https://localhost:9200/new-index/_doc/1' -H 'Content-Type: application/json' -d '{"title": "My document"}' -u 'jsmith:password' -k
You can go to localhost:6444 to see a Web administrator of the LDAP server.
To dive deeper into user roles, please see Access Control – Users, Roles and Permissions.
Conclusion
In just a few steps you can add LDAP authentication to OpenSearch and integrate your existing users. Using role mappings you can easily align some of the LDAP users properties to OpenSearch cluster roles.
This configuration applies both for LDAP and Active directory servers.