Sensu creator and Developer Advocate Todd Campbell recently wrote about using LDAP authentication for single-sign on (SSO) with Sensu Go. That post provided a great overview of Sensu authentication and included some useful LDAP troubleshooting tips. In this post, we'll focus on the Sensu LDAP implementation and explore how SSO/LDAP users are linked to RBAC "profiles" (i.e. Roles and ClusterRoles). We'll also demonstrate how Sensu supports multiple LDAP providers thanks to its groups_prefix
feature.
Diagnosing Sensu LDAP authentication providers
Both the LDAP and AD authentication provider types in Sensu use the same fundamental mechanism: they bind to an LDAP server implementation and do searches. Bind
, in this context, is the LDAP bind process that lets you authenticate that an LDAP user/password exists in the LDAP server matching some conditions. The only real difference between the LDAP and AD providers are some of the defaults used for the LDAP search settings.
LDAP is really flexible in terms of what attributes can be used to store information, so the default user and group search settings Sensu provides might not work for your organization's LDAP schema. If as a Sensu admin, you need to integrate with an LDAP server implemented by another part of your internal organization, it might not be clear what search settings to use, especially if the default settings Sensu provides aren't working. I've found that the best way to figure this out is to use ldapsearch
and ldapwhoami
on the system where sensu-backend
is expected to operate.
If you can construct ldapsearch
and ldapwhoami
command line operations that work, then you have all the information you need to translate into a working LDAP or AD authentication provider configuration in Sensu.
When using LDAP authentication provider, Sensu does four steps to authenticate with the LDAP server:
- Binds with privileged user
- Performs a user search using privileged user to make sure the desired login user exists
- Performs a group search using a privileged user to pull group info to be optionally used in Sensu RBAC
- Binds with login user to authenticate provided user/password credentials
The privileged LDAP user
The privileged LDAP user has permissions to perform LDAP searches for users and groups.
You may need to specifically ask the LDAP/AD admin for an LDAP account that will allow you to perform ldapsearch
commands. Specifically, you'll need the Distinguished Name (aka dn) and password for the privileged user.
For the following examples, I'm going to assume a privileged user with a Distinguished Name of
“cn=sensuldap,dc=example,dc=com”
Security Settings
You'll have to discuss with your LDAP/AD admin as to whether or not they are using TLS-secured LDAP and if they require a custom CA or client certificate. Both ldapsearch
and sensu-backend
support these options. For brevity, I'm not going to go into the details of cert signing or the installation of a custom CA. For the purposes of the following ldapseach
examples, I'm going to assume the LDAP server is using secure LDAP and that the TLS_CACERT TLS_CERT
and TLS_KEY
environment variables are set appropriately if needed. If the LDAP server you are connecting to requires client certificates, you'll definitely need to discuss the configuration with your LDAP admin.
The user search command
Once you have the privileged user credentials and have your TLS environment set up, you can use ldapsearch
to mimic how Sensu will search for users and groups. Let's assume that you want to login into Sensu web-ui using an LDAP user with a uid attribute of sensu-admin
. We want to be able to login to the Sensu web-ui by entering the LDAP uid attribute and LDAP password. We can construct the search Sensu will perform using an ldapsearch
command similar to this:
ldapsearch -x -H "ldaps://ldap.example.com" -D “cn=sensuldap,dc=example,dc=com” -b "dc=example,dc=com" "(&(objectClass=person)(uid=sensu-admin))"
If this is a valid unique search, you should get the result indicating numEntries: 1
. If you are getting more than one match, you'll need to review and see if you can adjust the search.
Note that it's possible to search non-unique fields in LDAP — such as human names. If your user search test returns more than a single result, you may need to change the base search by including one or more Organizational Units(ou)
attributes to narrow the search further. Once you have the search base tailored to provide a unique match, you should see the details of the matching LDAP record including the Distinguished Name attribute for the user:
dn: uid=sensu-admin,dc=example,dc=com
The value of the dn
attribute is what Sensu uses when authenticating the user trying to login by binding to the LDAP server after the privileged user search is complete.
This search command binds to the LDAP server as the privileged user and asks for the privileged user's password. The -D option holds privileged user's Distinguished Name and corresponds to the Sensu LDAP authentication provider binding.user_dn
setting.
The user search is restricted by the -b option which represents a search base. It's a subset of the available distinguished name LDAP attribute (dn
) that must match. You'll want to confirm the correct search base with your LDAP admin. This search base corresponds to the Sensu LDAP auth provider user_Search.base_dn
setting.
Lastly come the search filters. Sensu uses two LDAP search filters: a filter on ObjectClass
, where the value is configurable as part of the Sensu authentication provider configuration, and then a second filter using a configurable LDAP attribute name corresponding to the login name you want to use in Sensu.
The needed configuration attributes for Sensu LDAP authentication provider can be taken from the ldapsearch example above. Here's what the final Sensu LDAP authentication provider looks like:
---
type: ldap
api_version: authentication/v2
metadata:
name: openldap
spec:
servers:
- binding:
password: <<privileged_user_password>>
user_dn: cn=sensuldap,dc=example,dc=com
host: ldaps.example.com
insecure: false
port: 636
security: tls
user_search:
attribute: uid
base_dn: dc=example,dc=com
name_attribute: uid
object_class: person
The Sensu LDAP authentication provider binding section needs to have the user_dn
attribute that matches the privileged LDAP user authorized to perform user and group searches, and the -D argument in the ldapsearch
call above.
The user_search
section takes information from the ldapsearch
-b argument in the command we did above. The base_dn
attribute matches the common elements of the search for all users. The attribute
is the LDAP attribute that matches the unique name you want to use when you login to Sensu. It may be a username or an email address or something else your organization uses for unique login names. The object_class
is the type of record you want to search. The default value is person — which is the most common LDAP configuration usage — but you may need to change the object class to match your LDAP configuration details.
Once you are more familiar with the contents of your LDAP records you'll probably end up using a different value for name_attribute
. The name_attribute
is used to display the user info inside Sensu, and is not part of the LDAP query. When starting out, I find it's easier to set this to the same value as attribute
setting, but once you have everything working, you may change this to the LDAP attribute that holds the person's full name or something else. For example, name search could be based on email username only (e.g. "lizy" instead of "lizy@sensu.io") and name_attribute
could be the full email address, or vice versa. Unlike attribute
, the name_attribute
doesn't need to be unique because it's not part of a user search.
The group search command
Once Sensu does the user search, it goes on to perform a search for all groups that a particular user is a member of. The search Sensu will perform will look similar to this ldapsearch command:
ldapsearch -x -H "ldaps://ldap.example.com" -D “cn=sensuldap,dc=example,dc=com” -b "ou=Groups,dc=example,dc=com" "(&(objectClass=groupOfNames)(member=sensu-admin))"
Similar to the user search before, this group search uses the privileged user Distinguished Name and search base options. What's different here are the search filters. ObjectClass
filter is still being used, but now we are only matching for groupOfNames
. The second filter is the group attribute filter, where we are choosing to match the attribute named member, and the value is the same as the uid from the user search, since we are trying to find which groups the sensu-admin
user belongs to.
Note the search base for groups may be different than the search base most appropriate for user searches due to the use of Organization Unit_(ou)_ attributes in the dn.
Translating the group search into the Sensu LDAP authentication configuration settings:
group_search:
attribute: member
base_dn: ou=Groups,dc=example,dc=com
name_attribute: cn
object_class: groupOfNames
For the group_search.attribute
, you'll want to make sure you are selecting the attribute that corresponds to the user's login name you are trying to use. The group will have multiple versions of the attribute, indicating membership to the group for multiple users. Because of the flexibility of LDAP, you may need to talk to your LDAP admin as to what group search attribute and object class to use. Or you can do some exploratory searches with different filters to explore your LDAP records.
Group search pairs well with a Sensu RBAC feature called groups_prefix
. When defining any Sensu authentication provider (LDAP, AD and OIDC), you can identify a groups_prefix
string that you can use in your Sensu role-based access control rules to distinguish LDAP groups from Sensu native groups, so you can give different access based on authentication provider.
For example, you could set groups_prefix: “ldap”
and then write Sensu RBAC role binding rules with a group subject "ldap:operators"
. If this groups_prefix
is configured in the LDAP authentication provider, Sensu would interpret the "ldap:"
prefix in the group name in any RBAC rules to only match groups from the LDAP provider.
Similarly, Sensu also has a user_prefix
feature that lets you distinguish Sensu native users from users authenticated from an external provider like LDAP, if you need to write RBAC rules that target specific LDAP users instead of groups.
Moreover, the groups and user prefix let you write granular Sensu RBAC rules for multiple authentication providers.
The ldapwhoami command
Once you have established the user and group search settings for your Sensu LDAP/AD authentication provider configuration, you'll also want to make sure that the login user that needs to authenticate is able to bind to the LDAP server. You may have to work with your LDAP admin to correctly set permission for the users you want to use LDAP authentication with. Part of this is making sure the user has an LDAP password defined (hint: ldappasswd
command) but also that the user has permissions to bind to the LDAP server. The easiest way for you to test that the target user has the necessary credentials and permissions is to use the ldapwhoami
command and successfully bind to the LDAP server.
Let’s take the Distinguished Name from the user search example from above and use it in the ldapwhoami
command:
ldapwhoami -x -H "ldaps://ldap.example.com" -D “uid=sensu-admin,dc=example,dc=com”
If the entered password is correct, the output should look like this:
dn:uid=sensu-admin,dc=example,dc=local
If you run into an error, you may need to talk to your LDAP/AD admin to ensure your user has simple bind access with user/password instead of relying on an external Kerberos server to provide user authentication.
NOTE: LDAP simple bind is a critical requirement for Sensu's LDAP and AD authentication provider implementations.
As an aside, if you have time to read up on the history of why Kerberos exists, the history is sort of fascinating. I'd wager that if we had mutual TLS as a network security standard earlier, something like Kerberos wouldn't have been widely adopted.
We'd love to hear about your experience with the Sensu LDAP implementation! Let us know in discourse: