Ubuntu / Debian
sudo apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev ldap-utilsRocky / CentOS / RHEL / Fedora
sudo dnf install python2-devel python3-devel openldap-develPasswords that contain the $ character are required to be escaped in the form $$ because the underlying modules that process the backend configuration uses Python Template Strings.
The LDAP backend attempts a few different LDAP operations to authenticate users against an LDAP server:
- Attempts an LDAP bind with the StackStorm service credentials
bind_dnandbind_password. - Searches the LDAP server for the username provided by the StackStorm user, and saves the user's
bind_dnattribute asuser_dn. - Fetches the user's LDAP groups and compares them against the groups in the
group_dnsconfiguration using the logic fromgroup_dns_check, theuser_dnfrom step 2, and theusernamesupplied by the StackStorm user. - Attempts to re-bind to the LDAP server with the StackStorm user's supplied username and password.
If all of the steps succeed, then the user is authenticated. If any of those steps fail, then the authentication fails.
Please refer to the standalone mode in the configuration section for authentication for basic setup concept. The following is an example of the auth section in the StackStorm configuration file for the LDAP backend.
[auth]
mode = standalone
backend = ldap
backend_kwargs = {"bind_dn": "CN=st2admin,ou=users,dc=example,dc=com", "bind_password": "foobar123", "base_ou": "dc=example,dc=com", "group_dns": ["CN=st2users,ou=groups,dc=example,dc=com", "CN=st2developers,ou=groups,dc=example,dc=com"], "host": "identity.example.com", "port": 636, "use_ssl": true, "cacert": "/path/to/cacert.pem"}
enable = True
debug = False
use_ssl = True
cert = /path/to/mycert.crt
key = /path/to/mycert.key
logging = /path/to/st2auth.logging.conf
api_url = http://myhost.example.com:9101/Note: Key in the client_options dictionary must be an integer representing a LDAP constant option value.
For example:
backend_kwargs = {..., "client_options": {"20482": 9}}In this case, "20482" represents ldap.OPT_TIMEOUT option.
To retrieve a integer value of a particular client option constant, you can run the following code:
import ldap
print(ldap.OPT_TIMEOUT)Additionally, this simple example uses the default values for the id_attr, account_pattern, and group_pattern configuration options. This means that the user's account will be queried with the default LDAP search pattern uid={username}, and the groups will be queried with the default LDAP search pattern (|(&(objectClass=*)(|(member={user_dn})(uniqueMember={user_dn})(memberUid={username})))).
If your LDAP server uses a different name for the user ID attribute, you can simply specify the id_attr configuration option.
[auth]
mode = standalone
backend = ldap
backend_kwargs = {"bind_dn": "CN=st2admin,ou=users,dc=example,dc=com", "bind_password": "foobar123", "base_ou": "dc=example,dc=com", "id_attr": "username", "group_dns": ["CN=st2users,ou=groups,dc=example,dc=com", "CN=st2developers,ou=groups,dc=example,dc=com"], "host": "identity.example.com", "port": 636, "use_ssl": true, "cacert": "/path/to/cacert.pem"}
enable = True
debug = False
use_ssl = True
cert = /path/to/mycert.crt
key = /path/to/mycert.key
logging = /path/to/st2auth.logging.conf
api_url = http://myhost.example.com:9101/In this example, the user's account will be queried with the LDAP search pattern username={username}, and the user's groups will be queried with the default LDAP search pattern from above.
[auth]
mode = standalone
backend = ldap
backend_kwargs = {"bind_dn": "CN=st2admin,ou=users,dc=example,dc=com", "bind_password": "foobar123", "base_ou": "dc=example,dc=com", "account_pattern": "(&(objectClass=person)(|(accountName={username})(mail={username})))", "group_pattern": "(&(objectClass=userGroup)(|(member={user_dn})(uniqueMember={user_dn})(memberUsername={username})))", "group_dns": ["CN=st2users,ou=groups,dc=example,dc=com", "CN=st2developers,ou=groups,dc=example,dc=com"], "host": "identity.example.com", "port": 636, "use_ssl": true, "cacert": "/path/to/cacert.pem"}
enable = True
debug = False
use_ssl = True
cert = /path/to/mycert.crt
key = /path/to/mycert.key
logging = /path/to/st2auth.logging.conf
api_url = http://myhost.example.com:9101/Note: You do not have to override both account_pattern and group_pattern together, you may only need to override one of them.
It's easier to read the account_pattern string (&(objectClass=person)(|(accountName={username})(mail={username}))) if it is reformatted to show the nesting:
(&
(objectClass=person)
(|
(accountName={username})
(mail={username})
)
)
The Python string format patterns {username} will be interpolated with the LDAP username of users who are authenticating with StackStorm. This means that the account_pattern search string will query the LDAP server for a user who has the LDAP objectClass attribute equal to person and whose LDAP accountName or LDAP mail attributes are equal to the username they submit to StackStorm.
For group_pattern, the usage is similar, but it has an additional user_dn variable that will be interpolated into the string:
(&
(objectClass=userGroup)
(|
(member={user_dn})
(uniqueMember={user_dn})
(memberUsername={username})
)
)
This search string will query for LDAP objects that have objectClass attributes equal to userGroup and whose LDAP member or uniqueMember attributes are equal to the user's LDAP user_dn value or whose LDAP memberUsername attributes are equal to the username they submit to StackStorm.
The user_dn value is the user's bind_dn attribute returned by the LDAP server in step 2.
The group_dns option takes a list of fully-qualified DNs (Distinguished Names), or RDNs (Relative Distinguished Names).
[auth]
mode = standalone
backend = ldap
backend_kwargs = {"bind_dn": "CN=st2admin,ou=users,dc=example,dc=com", "bind_password": "foobar123", "base_ou": "dc=example,dc=com", "group_dns": ["CN=st2users", "CN=st2developers"], "host": "identity.example.com", "port": 636, "use_ssl": true, "cacert": "/path/to/cacert.pem"}
enable = True
debug = False
use_ssl = True
cert = /path/to/mycert.crt
key = /path/to/mycert.key
logging = /path/to/st2auth.logging.conf
api_url = http://myhost.example.com:9101/Note: groups, like users, are objects in LDAP. Groups are not the same as the OU (Organizational Unit) "folders" that you can see in DNs.
To login, users must be members of all groups--identified by their DN or RDN--in the base_dns option (assuming group_dns_check is and).
If group_dns_check is or, then the user only needs to be in one of the required groups, not all of them.
In this example, the group_dns option has the RDNs CN=st2users and CN=st2developers instead of the fully qualified DNs CN=st2users,ou=groups,dc=example,dc=com and CN=st2developers,ou=groups,dc=example,dc=com.
Using RDNs can be especially helpful when using Active Directory as group names (the CN group attributes) are globally unique, no matter how they are organized (no matter which OU is the parent).
Unit tests:
make unit-testsCopyright 2015-2020 Extreme Networks, Inc.
Copyright 2020 StackStorm, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
By contributing you agree that these contributions are your own (or approved by your employer) and you grant a full, complete, irrevocable copyright license to all users and developers of the project, present and future, pursuant to the license of the project.
