Introducing an open source Apache Cassandra LDAP Authenticator
No need to reinvent the wheel – Apache Cassandra has just released its own LDAP authenticator for integration into this popular NoSQL database. Kurt Greaves explains how developers can implement this open source plug-in for their own projects.
With more and more developers realizing a need for an LDAP integration into Apache Cassandra, we’ve heard countless stories of devs taking matters into their own hands and writing LDAP authenticators for the popular NoSQL database. Unfortunately, these existing implementations are by and large not publicly available – leaving each new development team to face this rather challenging task on their own.
To help make developers’ lives a little easier and spare them from the process of reinventing the proverbial wheel, we’ve introduced a freely available and open source LDAP authenticator plug-in for Cassandra, which works in tandem with the established CassandraAuthorizer implementation. This plug-in currently supports basic LDAP usage, and should be effective for most use cases. As an open source project, modifications and improvements are certainly welcome and encouraged, and all interested developers are invited to submit pull requests to the project on GitHub.
LDAP authenticator implementation
Implementation of the LDAP authenticator uses JNDI, with Cassandra making authentication requests to the LDAP server using a client-provided username and password. The authenticator currently supports only plain text authentication.
When a service LDAP user is configured in the ldap.properties file, Cassandra will authenticate the user on startup and create a corresponding role in the system_auth.roles table (where it will be used to verify clients’ authentication requests in the future). An alternative – and decidedly not recommended – method is enabling anonymous access to the LDAP server, which would allow access with no service user configured. The service user can then be configured as a Cassandra superuser, and can set permissions for other authenticated users.
A user’s Distinguished Name is the only credential that Cassandra stores. The user’s password is not stored in the roles table, and credentials are passed directly to the configured LDAP server. The password, or hashed password, is stored in the cache if it’s enabled (on the nodes in memory only). To begin, a user role won’t have access permissions for any keyspaces or tables, making it necessary to issue grants so that the user can perform queries. Importantly, with the LDAP authenticator, all role authentication is handled by LDAP. Users in LDAP can be removed or disabled to bar future connections, but the user will remain in system_auth.roles unless removed manually, which is advised if changing to a different authentication solution.
Because the authenticator supports only plain text, using client encryption in Cassandra is essential from a security standpoint. It’s also required to use LDAPS (LDAP over SSL) in order to secure credentials as they’re transmitted between Cassandra and the LDAP server. Because SSL configuration is done entirely through JNDI, LDAPS can be enabled simply by specifying it as the protocol for the LDAP server (first making sure that LDAPS is enabled on your server).
Version 3.11 and later have also implemented a cache to protect the LDAP server from thrashing. The cache will hold the username and either the associated password or a hash of it based on the cache_hashed_password property in ldap.properties. Because hashed passwords must be calculated for each authentication, they do impact performance. Also, since the password or hash is stored only in memory on Cassandra nodes, it’s important to make sure the Casandra nodes are well secured if not hashing passwords.
To set LDAP JNDI properties, just specify the correct properties in the ldap.properties file. For instance, the LDAP read timeout can be set by including:
Documentation on these properties is available here.
Making the transition to the LDAP authenticator
You can transition seamlessly to using the LDAP authenticator – with no downtime – either by using AllowAllAuthenticator, or by handling authentication failures from LDAP and the old password authenticator in the client and then setting it to try the LDAP authenticator on the next request. And while it’s possible to prepare LDAP users ahead of time in system.roles, this technique is not recommended because it would require storing LDAP user passwords in Cassandra.
Another option, if you’re OK with a little downtime in your particular use case, is making the transition while turning off all nodes at once. To avoid any errors on startup due to creating the service roles, it’s advisable to start one node and see that it’s running before starting the rest.
The LDAP authenticator’s source code and setup/usage instructions are available on GitHub. The authenticator current supports Cassandra versions 2.2, 3.0, and 3.11.