Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions conf/cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,26 @@ role_manager:
# invalid_role_disconnect_task_period: 4h
# invalid_role_disconnect_task_max_jitter: 1h

# Creates the initial role on a cluster which has no roles yet, implementing IDefaultRoleInitializer.
Comment thread
aparna0522 marked this conversation as resolved.
# Most functions of the IRoleManager require an authenticated login, so a cluster with no roles has no way
# to create the first one; this option controls how that role is bootstrapped.
#
# Defaults to PasswordDefaultRoleInitializer, which creates a 'cassandra' superuser whose password is also
# 'cassandra'. That password is a published constant, so deployments using it must rotate or drop the role
# before the native transport is reachable.
#
# MutualTlsDefaultRoleInitializer instead creates the role with no password at all and maps a client
# certificate identity onto it, so there is no credential to guess. It requires an authenticator supporting
# mutual TLS, such as MutualTlsAuthenticator or MutualTlsWithPasswordFallbackAuthenticator.
#
# default_role_initializer:
# class_name: PasswordDefaultRoleInitializer # or: MutualTlsDefaultRoleInitializer
# parameters:
# role: cassandra
# password: cassandra # PasswordDefaultRoleInitializer: plaintext password
# # password_hash: "$2a$04$wsvzFamDJPDrTwMjgfcgpO.mKc.CMEuHBFZSjhGz2Ts6.v8PUO2rC" # ...or a bcrypt hash instead
# # identity: "spiffe1" # MutualTlsDefaultRoleInitializer: cert identity to map

# Network authorization backend, implementing INetworkAuthorizer; used to restrict user
# access to certain DCs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer,
Expand Down
20 changes: 20 additions & 0 deletions conf/cassandra_latest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ role_manager:
invalid_role_disconnect_task_period: 4h
invalid_role_disconnect_task_max_jitter: 1h

# Creates the initial role on a cluster which has no roles yet, implementing IDefaultRoleInitializer.
# Most functions of the IRoleManager require an authenticated login, so a cluster with no roles has no way
# to create the first one; this option controls how that role is bootstrapped.
#
# Defaults to PasswordDefaultRoleInitializer, which creates a 'cassandra' superuser whose password is also
# 'cassandra'. That password is a published constant, so deployments using it must rotate or drop the role
# before the native transport is reachable.
#
# MutualTlsDefaultRoleInitializer instead creates the role with no password at all and maps a client
# certificate identity onto it, so there is no credential to guess. It requires an authenticator supporting
# mutual TLS, such as MutualTlsAuthenticator or MutualTlsWithPasswordFallbackAuthenticator.
#
# default_role_initializer:
# class_name: PasswordDefaultRoleInitializer # or: MutualTlsDefaultRoleInitializer
# parameters:
# role: cassandra
# password: cassandra # PasswordDefaultRoleInitializer: plaintext password
# # password_hash: "$2a$04$wsvzFamDJPDrTwMjgfcgpO.mKc.CMEuHBFZSjhGz2Ts6.v8PUO2rC" # ...or a bcrypt hash instead
# # identity: "spiffe1" # MutualTlsDefaultRoleInitializer: cert identity to map

# Network authorization backend, implementing INetworkAuthorizer; used to restrict user
# access to certain DCs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.cassandra.auth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.tcm.ClusterMetadata;

import static org.apache.cassandra.auth.AuthUtils.escape;

public abstract class AbstractDefaultRoleInitializer implements IDefaultRoleInitializer
{
private static final Logger logger = LoggerFactory.getLogger(AbstractDefaultRoleInitializer.class);

@Override
public final void initializeDefaultRoleIfNeeded()
{
if (ClusterMetadata.current().tokenMap.tokens().isEmpty())
throw new IllegalStateException(getClass().getSimpleName() + " skipped role setup: no known tokens in the ring");

try {
if (!hasExistingRoles())
createDefaultRole();

} catch (RequestExecutionException e)
{
logger.warn(getClass().getSimpleName() + " skipped default role setup: some nodes were not ready");
throw e;
}
}

@Override
public final boolean hasExistingRoles()
{
// Try looking up the configured default role first, to avoid the range query if possible.
String defaultRoleQuery = String.format("SELECT * FROM %s.%s WHERE role = '%s'", SchemaConstants.AUTH_KEYSPACE_NAME, AuthKeyspace.ROLES, escape(defaultRoleName()));
String allUsersQuery = String.format("SELECT * FROM %s.%s LIMIT 1", SchemaConstants.AUTH_KEYSPACE_NAME, AuthKeyspace.ROLES);
return !QueryProcessor.process(defaultRoleQuery, ConsistencyLevel.ONE).isEmpty()
|| !QueryProcessor.process(defaultRoleQuery, ConsistencyLevel.QUORUM).isEmpty()
|| !QueryProcessor.process(allUsersQuery, ConsistencyLevel.QUORUM).isEmpty();
}
}
28 changes: 27 additions & 1 deletion src/java/org/apache/cassandra/auth/AuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,24 @@ public static void applyAuth()

DatabaseDescriptor.setAuthorizer(authorizer);

// default role initializer: bootstraps the first role on a cluster which has none yet. Instantiated
// before the role manager because the role manager depends on it (see IRoleManager#defaultRoleInitializer).

IDefaultRoleInitializer defaultRoleInitializer = authInstantiate(conf.default_role_initializer,
IDefaultRoleInitializer.class,
PasswordDefaultRoleInitializer.instance);
DatabaseDescriptor.setDefaultRoleInitializer(defaultRoleInitializer);

// role manager

IRoleManager roleManager = authInstantiate(conf.role_manager, IRoleManager.class, CassandraRoleManager.class);

if (authenticator instanceof PasswordAuthenticator && !(roleManager instanceof CassandraRoleManager))
throw new ConfigurationException(authenticator.getClass().getName() + " requires " + CassandraRoleManager.class.getName(), false);

if (!defaultRoleInitializer.supportsRoleManager(roleManager))
throw new ConfigurationException(defaultRoleInitializer.getClass().getName() + " does not support " + roleManager.getClass().getName(), false);

DatabaseDescriptor.setRoleManager(roleManager);

// authenticator
Expand Down Expand Up @@ -140,18 +151,23 @@ public static void applyAuth()
authenticator.validateConfiguration();
authorizer.validateConfiguration();
roleManager.validateConfiguration();
defaultRoleInitializer.validateConfiguration();
networkAuthorizer.validateConfiguration();
cidrAuthorizer.validateConfiguration();
DatabaseDescriptor.getInternodeAuthenticator().validateConfiguration();
}

private static <T> T authInstantiate(ParameterizedClass authCls, Class<T> expectedType, Class<? extends T> defaultCls) {
private static <T> T authInstantiate(ParameterizedClass authCls, Class<T> expectedType, Class<? extends T> defaultCls)
{
if (authCls != null && authCls.class_name != null)
{
String authPackage = AuthConfig.class.getPackage().getName();
return ParameterizedClass.newInstance(authCls, List.of("", authPackage), expectedType);
}

if (defaultCls == null)
Comment thread
aparna0522 marked this conversation as resolved.
return null;

// for now, this has to stay and can not be replaced by ParameterizedClass.newInstance as above
// due to that failing for simulator dtests. See CASSANDRA-20450 for more information.
try
Expand All @@ -163,4 +179,14 @@ private static <T> T authInstantiate(ParameterizedClass authCls, Class<T> expect
throw new ConfigurationException("Failed to instantiate " + defaultCls.getName(), e);
}
}

private static <T> T authInstantiate(ParameterizedClass authCls, Class<T> expectedType, T defaultInstance)
{
if (authCls != null && authCls.class_name != null)
{
String authPackage = AuthConfig.class.getPackage().getName();
return ParameterizedClass.newInstance(authCls, List.of("", authPackage), expectedType);
}
return defaultInstance;
}
}
53 changes: 53 additions & 0 deletions src/java/org/apache/cassandra/auth/AuthUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.cassandra.auth;

import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;

import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ConsistencyLevel;

public class AuthUtils
{
static final ConsistencyLevel DEFAULT_SUPERUSER_CONSISTENCY_LEVEL = ConsistencyLevel.QUORUM;

private AuthUtils() {}

public static String hashpw(String password)
{
return BCrypt.hashpw(password, PasswordSaltSupplier.get());
}

public static String escape(String name)
{
return StringUtils.replace(name, "", "''");
}

/** Allows selective overriding of the consistency level for specific roles. */
public static ConsistencyLevel consistencyForRoleWrite(String role)
{
return role.equals(DatabaseDescriptor.getRoleManager().defaultRoleInitializer().defaultRoleName()) ? DEFAULT_SUPERUSER_CONSISTENCY_LEVEL : CassandraAuthorizer.authWriteConsistencyLevel();
}

public static ConsistencyLevel consistencyForRoleRead(String role)
{
return role.equals(DatabaseDescriptor.getRoleManager().defaultRoleInitializer().defaultRoleName()) ? DEFAULT_SUPERUSER_CONSISTENCY_LEVEL : CassandraAuthorizer.authReadConsistencyLevel();
}
}
Loading