• Apache Cassandra
  • Technical
Apache Cassandra LDAP Authentication

We’ve seen an increasing need for LDAP integration into Apache Cassandra, and continually hearing of cases where people have written their own LDAP authenticators for Cassandra.

However, if you search around you’ll have a hard time finding one of these implementations, and you’ll likely have to write one yourself, which is no easy feat.

So, to solve this issue we’ve created an open source LDAP authenticator plug-in for Apache Cassandra that goes hand in hand with the existing CassandraAuthorizer implementation. At the moment it supports a basic usage of LDAP which should suffice for most cases, however, improvements are welcome if you need to modify it to suit your needs and encouraged to submit pull requests for any enhancements.

This plug-in authenticator is freely available for anyone for use and is also be included in the support scope for customers with Apache Cassandra Enterprise Support from Instaclustr.

Implementation

The LDAPAuthenticator is implemented using JNDI, and authentication requests will be made by Cassandra to the LDAP server using the username and password provided by the client. At this time only plain text authentication is supported.

If you configure a service LDAP user in the ldap.properties file, on startup Cassandra, will authenticate the service user and create a corresponding role in the system_auth.roles table. This service user will then be used for future authentication requests received from clients. Alternatively (not recommended), if you have anonymous access enabled for your LDAP server, the authenticator allows authentication without a service user configured. The service user will be configured as a superuser role in Cassandra, and you will need to log in as the service user to define permissions for other users once they have authenticated.

On successful authentication of a client, a corresponding role will be created in the system_auth.roles table. The password for the role is not stored in the roles table, and credentials will always be passed through directly to the configured LDAP server. The only credentials stored in Cassandra is the Distinguished Name of the user. However, if caching is enabled the password/hashed password will be stored in the cache, in memory only, on the nodes. Permissions-wise, this role will have no access to any keyspaces/tables, so GRANT’s will need to be issued before the user can perform any useful queries.

Once created, the role will never be deleted, and all authentication of the role will be handled through LDAP while the LDAPAuthenticator is in place. Removing or disabling the user in LDAP will disallow future connections as that user, but not clean up the user from system_auth.roles. This can be done manually if so desired and should be done if you wish to switch to a different authentication mechanism.
Regarding security, as the authenticator only supports plain text from clients you should ensure you have enabled and are using client encryption in Cassandra. On the LDAP side, you must use LDAPS otherwise credentials will be sent in the clear between Cassandra and the LDAP server. As all SSL configuration is performed through JNDI, simply specifying LDAPS as your protocol for the LDAP server (assuming it’s enabled on your server) will enable LDAPS.

On 3.11 and later versions, a cache has been implemented to avoid thrashing your LDAP server. This cache will be populated with the username and either the provided password or a hash of the password based on the cache_hashed_password property in ldap.properties. Note that hashing the password will incur a performance hit as the hash needs to be calculated on each auth. The password/hash is only stored in memory on the Cassandra nodes, so if you don’t enable hashing ensure appropriate security controls are in place for your Cassandra nodes.

LDAP JNDI properties can be set via the ldap.properties file. Simply specify the desired property and it will be set as part of the servers context. For example, you can set the LDAP read timeout like so:

   com.sun.jndi.ldap.read.timeout: 2000

These properties and their documentation can be found here.

Transitioning

To transition to  LDAPAuthenticator you can do so in a rolling fashion with no downtime as long as you are using AllowAllAuthenticator to start with, or you handle auth failures from both LDAP and your old password authenticator in your client and try the alternate auth on the next request. Pre-creating the LDAP users in system.roles is also possible however not recommended as you will need to store the LDAP user passwords in Cassandra for it to work.

Alternatively, you can do the switch with downtime with no issues however this requires turning off all the nodes simultaneously. To ensure no errors on startup due to the creation of service roles you should start one node first and wait until it’s running before starting the rest of the nodes.

You can find the LDAP authenticator source code on GitHub here, with instructions on setup and usage in the README. Currently, the authenticator is supported for 2.2, 3.0, and 3.11 versions. Use the corresponding branch in the repo for your desired version.