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.
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.
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.
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 |
filter_template |
template for searching in LDAP, explanation further in this readme, defaults to |
auth_cache_enabled |
relevant for Cassandra 3.11 and 4.0 plugins, defaults to |
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 |
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 |
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:
authenticator: Cassandra22LDAPAuthenticator
role_manager: LDAPCassandraRoleManager
authorizer: CassandraAuthorizer
authenticator: Cassandra30LDAPAuthenticator
role_manager: LDAPCassandraRoleManager
authorizer: CassandraAuthorizer
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.
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.
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.
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.
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.
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.
-
See blog by Stefan Miklosovic about Apache Cassandra LDAP Authentication
-
Please see Instaclustr support status of this project