Instaclustr LDAP Authenticator
LDAP Authenticator for Apache Cassandra
-
Website: https://www.instaclustr.com/
-
Documentation: https://www.instaclustr.com/support/documentation/
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.
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 four implementation modules:
-
cassandra-2.2 - builds against version 2.2.19
-
cassandra-3.0 - builds against version 3.0.23
-
cassandra-3.11 - builds against version 3.11.9
-
cassandra-4.0 - builds against version 4.0-beta3
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: |
context_factory |
defaults to |
service_dn |
Service user distinguished name. This user will be a SUPERUSER and be used for looking up user details on authentication, example: |
service_password |
Service user password |
ldap_naming_attribute |
Use this setting if you need to change your naming attribute from the usual cn= |
auth_cache_enabled |
relevant for Cassandra 3.11 and 4.0 plugins, defaults to |
auth_bcrypt_gensalt_log2_rounds |
number of rounds to hash passwords |
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
authenticator: LDAPAuthenticator
authorizer: CassandraAuthorizer
role_manager: LDAPCassandraRoleManager
Cassandra 4.0
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.
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 super user 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 login and the disruption of LDAP communication will not affect their ability to do so as they live in Cassandra natively.
In case there are two logins of same name (e.g. admin
in LDAP and admin
in C*), in order to distinguish them, if you want to login with LDAP user, you have to specify its full account name, e.g.
cqlsh -u cn=admin,dn=example,dn=org
In case a user specifies just admin
as login name (or any other name, for that matter), it will try to authenticate against database first and if not successful against LDAP, adding all details (cn= etc. …) to username automatically.
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.
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 extending DefaultLDAPServer
and overriding what is necessary. You might as well extend and implement LDAPPasswordRetriever
class. DefaultLDAPServer
just extends it.
To tell LDAP plugin to use your implementation, you need to create a file in src/main/resources/META-INF/services
called com.instaclustr.cassandra.ldap.auth.LDAPPasswordRetriever
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.
Further Information
-
See blog by Kurt Greaves Apache Cassandra LDAP Authentication
-
Please see https://www.instaclustr.com/support/documentation/announcements/instaclustr-open-source-project-status/ for Instaclustr support status of this project