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: falseLDAP 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' -kThis 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' -kYou 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.
