Skip to content

instaclustr/cassandra-ldap

Repository files navigation

Instaclustr LDAP Authenticator

LDAP Authenticator for Apache Cassandra

Instaclustr

This is a pluggable authentication implementation for Apache Cassandra, providing a way to authenticate and create users based on a configured LDAP server. This implementation provides authentication only. Role management must be performed through the usual Cassandra role management— CassandraAuthorizer. See How it works for more details.

Deprecation note

By releasing of Cassandra 5.0, the Cassandra team has stopped to actively develop / support Cassandra versions 3.0 and 3.11. We keep the source code of respective 3.0 and 3.11 modules in the repository, but they are commented out from main pom.xml as they require Java 8 but Cassandra 5.0 requires Java 11 and the testing framework we use for running embedded Cassandra can not deal with Java 11 while testing Cassandra 3.x even we set source and target for Maven to 1.8. If you want to build 3.x modules, please be sure you run Java 8 and you build only the respective module you want.

Project Structure and Building

This project consists of 5 modules; the base module is the module on which all other implementation modules depend. It contains core implementation which is necessary as all concrete modules are reusing it.

The base module depends on Cassandra dependency—version 3.0.18 but its scope is provided as these classes will be present when such plugin as a whole is put on a class path of Cassandra in runtime.

There are five implementation modules:

  • cassandra-2.2 - builds against version 2.2.19

  • cassandra-3.0 - builds against version 3.0.28

  • cassandra-3.11 - builds against version 3.11.14

  • cassandra-4.0 - builds against version 4.0.7

  • cassandra-4.1 - builds aganist version 4.1.0

  • cassandra-5.0 - builds aganist version 5.0.0

Project is built as:

$ mvn clean install

This does not invoke integration tests. For integration tests to run, please specify it profile:

$ mvn clean install -Pit

Integration tests will expect a Docker installation to be present (or a way to connect to one). There is a Docker container started with the LDAP server running against which an integration test, per module, is started.

Configuration of Plugins

After build, the respective JAR to place to Cassandra CLASSPATH (e.g. by placing it to libs directory of Cassandra installation) is located in the target directory of each build as casandra-ldap-{c* version}.jar. This JAR already contains artifacts from base so you do not need to take care of it-one JAR is enough. You may at most probably use a plugin built against a respective version for other Cassandra versions of the same minor release, so you might use 3.11.8 plugin for 3.11.4, for example.

The configuration is ridden by a configuration file and system properties which you need to start Cassandra with to point that plugin to a configuration file to read properties from.

The system property is -Dcassandra.ldap.properties.file=/path/to/configiration.properties. If not set, it will try to resolve $CASSANDRA_CONF/ldap.properties.

The content of the configuration file is as follows:

property name explanation

ldap_uri

Ldap server URI. Specify ldaps when using a secure LDAP port (strongly recommended), example: ldap://127.0.0.1:389/dc=example,dc=org

context_factory

defaults to com.sun.jndi.ldap.LdapCtxFactory

service_dn

Service user distinguished name. This user will be a SUPERUSER and be used for looking up user details on authentication, example: cn=admin,dc=example,dc=org

service_password

Service user password

filter_template

template for searching in LDAP, explanation further in this readme, defaults to (cn=%s)

auth_cache_enabled

relevant for Cassandra 3.11 and 4.0 plugins, defaults to false

consistency_for_role

consistency level to use for retrieval of a role to check if it can log in - defaults to LOCAL_ONE

auth_bcrypt_gensalt_log2_rounds

number of rounds to hash passwords

load_ldap_service

defaults to false, if it is true, SPI mechanism will look on class path to load custom implementation of LDAPUserRetriever.

default_role_membership

A role to add new LDAP users to by default. Defaults to empty (users will not be added to any role).

cassandra_ldap_admin_user

name of a user/role which will be considered a default superuser, instead of cassandra. Please consult "How it Works" section to know more about the usage.

Configuration of Cassandra

If is strongly recommended to use NetworkTopologyStrategy for your system_auth keyspace.

Please be sure that system_auth keyspace uses NetworkTopologyStrategy with number of replicas equal to number of nodes in DC. If it is not the case, you can alter your keyspace as follows:

ALTER KEYSPACE system_auth WITH replication = {'class': 'NetworkTopologyStrategy', 'dc1': '3'}  AND durable_writes = true;

After this, repair system_auth keyspace so it all propagates to other nodes.

You need to restart your cluster in a rolling fashion. For each node, you need to add one of these configurations into cassandra.yaml for each node:

Cassandra 2.2

authenticator: Cassandra22LDAPAuthenticator
role_manager: LDAPCassandraRoleManager
authorizer: CassandraAuthorizer

Cassandra 3.0

authenticator: Cassandra30LDAPAuthenticator
role_manager: LDAPCassandraRoleManager
authorizer: CassandraAuthorizer

Cassandra 3.11 - 4.x

authenticator: LDAPAuthenticator
authorizer: CassandraAuthorizer
role_manager: LDAPCassandraRoleManager

For 3.11 and 4, configure credential caching parameters in cassandra.yaml if necessary and if you want that cache to be enabled (as per configuration parameters). [Re]start Cassandra.

Example

For fast testing there is Debian OpenLDAP Docker container

docker run -e LDAP_ADMIN_PASSWORD=admin --rm -d -p 389:389 --name ldap1 osixia/openldap

The ldap.configuration file in the conf directory does not need to be changed, and with the above docker run it will work out of the box. You just have to put it in $CASSANDRA_CONF or set respective configuration property as described above.

Explanation of filter_template property

filter_template property is by default (cn=%s) where %s will be replaced by name you want to log in with. For example if you do cqlsh -u myuserinldap, a search filter for LDAP will be (cn=myuserinldap). You may have a different search filter based on your need, a lot of people use e.g. SAM or something similar. If you try to log in with cqlsh -u cn=myuserinldap, there will be no replacement done and this will be used as a search filter instead.

How it Works

LDAPAuthenticator currently supports plain text authorization requests only in the form of a username and password. This request is made to the LDAP server over plain text, so you should be using client encryption on the Cassandra side and secure ldap (ldaps) on the LDAP side.

Credentials are sent from your client to the Cassandra server and then tested against the LDAP server for authentication using a specified service account. This service account should be configured in the ldap.properties file using the service_dn and service_password properties. If service_dn is set, such a role will be created in database, when not already present, upon node’s start.

service_dn account, which will be automatically created, will be superuser in Cassandra.

All "normal" roles are not affected - they behave exactly as you are used to.

If the LDAP server connection is lost or there is another communication error while talking to LDAP server, the operator still has a possibility of logging in via cassandra user as usual, and until the LDAP server is not back again; Users meant to be authenticated against the LDAP server will not be able to log in but all "normal" users will be able to log in and the disruption of LDAP communication will not affect their ability to do so as they live in Cassandra natively.

In case a user specifies just test as login name (or any other name, for that matter), it will try to authenticate against database first and if not successful against LDAP using filter filter_template which defaults to (cn=%s)

It is possible to delete administration role (e.g. role cassandra) but if one does that, all administration operations are only able to be done via LDAP account. In case LDAP is down, the operator would not have any control over DB as cassandra is not present anymore. In such case, it is recommended to create another admin-like user with a strong password before the cassandra role is deleted. A plugin is internally creating new roles when somebody from LDAP logs in and it is not in DB yet. For this functionality, there needs to be some admin-like user which writes them system_auth.roles table. If you delete cassandra user, there is suddenly not such user. You have to restart node and specify this property:

-Dcassandra.ldap.admin.user=dba

Where dba is new superuser which is able to write to system_auth.roles and acts as Cassandra admin.

Upon login via LDAP user, this plugin will create a dummy role just to be able to play as a normal Cassandra role with all its permissions and so on. Passwords for LDAP users are not stored in Cassandra, obviously.

Credentials are cached for implementations for Cassandra 3.11 and 4.0 so that way we are not hitting LDAP server all the time when there is a lot of login attempts with same login name. An administrator can increase relevant validity settings in cassandra.yaml to increase these periods even more.

SPI for LDAP server implementations (advanced)

In order to talk to a LDAP server, there is DefaultLDAPServer class in base module which all modules are using. However, it might not be enough - there is a lot of LDAP servers out there and their internals and configuration might render the default implementation incompatible. If you have special requirements, you might provide your own implementation by implementing LDAPUserRetriever. You have to have load_ldap_service set to true as well.

To tell LDAP plugin to use your implementation, you need to create a file in src/main/resources/META-INF/services called LDAPUserRetriever and the content of that file needs to be just one line - the fully qualified class name (with package) of your custom implementation.

After you build such plugin, the SPI mechanism upon plugin’s initialisation during Cassandra node startup will pick up your custom LDAP server connection / authentication logic.

Default Role Membership

It is possible to automatically add new LDAP users to an existing Cassandra role when they are created by setting the default_role_membership configuration option. When this is set, any LDAP users logging in to Cassandra for the first time will be added to the role specified. Users who already exist in Cassandra will not be added to the group. If the default role specified does not exist, the role will not be created and new users will not receive the default membership. Only one role can be specified.

Further Information