Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/instaclustr/cassandra-ldap

LDAP Authenticator for Apache Cassandra
https://github.com/instaclustr/cassandra-ldap

apache-cassandra auth authentication authenticator cassandra cassandra-ldap ldap ldap-authentication ldap-server netapp-public

Last synced: about 1 month ago
JSON representation

LDAP Authenticator for Apache Cassandra

Awesome Lists containing this project

README

        

# Instaclustr LDAP Authenticator

_LDAP Authenticator for Apache Cassandra_

image:https://circleci.com/gh/instaclustr/cassandra-ldap.svg?style=svg["Instaclustr",link="https://circleci.com/gh/instaclustr/cassandra-ldap"]

- 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.

## 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
- See blog by Stefan Miklosovic about https://www.instaclustr.com/the-instaclustr-ldap-plugin-for-cassandra/[Apache Cassandra LDAP Authentication]
- Please see https://www.instaclustr.com/support/documentation/announcements/instaclustr-open-source-project-status/[Instaclustr support status] of this project