Use ParameterizedClass for all auth-related implementations

Uses ParameterizedClass for IAuthorizer, INetworkAuthorizer, and
IRoleManager implementations enabling those to be configurable from
parameters specified directly in cassandra.yaml.

Opportunistically makes consistent the initialization and setting of
all auth-related implementations and removes code duplication.

Notes:
 * IInternodeAuthenticator implementations are expected to be set as
   default directly in DatabaseDescriptor instead of done via
   AuthConfig.applyAuth(). This is assumed in tests and client logic.
   For instance ReconnectableSnitchHelperTest fetches this authenticator
   before calling daemonInitialize(). Also, BulkLoader fetches this
   directly when creating an outbound connection.
 * Changing this behavior in BulkLoader causes a cascade of changes.
   First, one would need to add AuthConfig.applyAuth() directly in
   clientInitialize() and then would need to enable all the additional
   classes that this change causes. Long term this might be justified
   as if we're depending on the auth logic to be properly set. However,
   in the context of this change it was decided to postpone further
   changes.
 * Standardize auth-related configurations by using a short-form with
   the class name instead of the full qualified class name containing
   the package name.

 patch by Tiago Alves; reviewed by Mick Semb Wever, Stefan Miklosovic for CASSANDRA-19946
This commit is contained in:
Tiago Alves 2024-09-24 10:27:40 +01:00 committed by mck
parent 9dfcfaee65
commit 2dea5c7588
No known key found for this signature in database
GPG Key ID: E91335D77E3E87CB
11 changed files with 108 additions and 99 deletions

View File

@ -1,4 +1,5 @@
5.0.2
* Use ParameterizedClass for all auth-related implementations (CASSANDRA-19946)
* Correct out-of-date metrics and configuration documentation for SAI (CASSANDRA-19898)
Merged from 4.1:
* Fix race condition in DecayingEstimatedHistogramReservoir during rescale (CASSANDRA-19365)

View File

@ -159,14 +159,17 @@ batchlog_replay_throttle: 1024KiB
# Authentication backend, implementing IAuthenticator; used to identify users
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
# PasswordAuthenticator}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllAuthenticator performs no checks - set it to disable authentication.
# - PasswordAuthenticator relies on username/password pairs to authenticate
# users. It keeps usernames and hashed passwords in system_auth.roles table.
# Please increase system_auth keyspace replication factor if you use this authenticator.
# If using PasswordAuthenticator, CassandraRoleManager must also be used (see below)
authenticator:
class_name : org.apache.cassandra.auth.AllowAllAuthenticator
authenticator: AllowAllAuthenticator
# MutualTlsAuthenticator can be configured using the following configuration. One can add their own validator
# which implements MutualTlsCertificateValidator class and provide logic for extracting identity out of certificates
# and validating certificates.
@ -177,6 +180,10 @@ authenticator:
# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
# CassandraAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
# - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please
@ -189,6 +196,10 @@ authorizer: AllowAllAuthorizer
# which stores role information in the system_auth keyspace. Most functions of the
# IRoleManager require an authenticated login, so unless the configured IAuthenticator
# actually implements authentication, most of this functionality will be unavailable.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - CassandraRoleManager stores role data in the system_auth keyspace. Please
# increase system_auth keyspace replication factor if you use this role manager.
@ -198,6 +209,10 @@ role_manager: CassandraRoleManager
# access to certain DCs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer,
# CassandraNetworkAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllNetworkAuthorizer allows access to any DC to any user - set it to disable authorization.
# - CassandraNetworkAuthorizer stores permissions in system_auth.network_permissions table. Please
@ -208,6 +223,11 @@ network_authorizer: AllowAllNetworkAuthorizer
# access from certain CIDRs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllCIDRAuthorizer,
# CassandraCIDRAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllCIDRAuthorizer allows access from any CIDR to any user - set it to disable CIDR authorization.
# - CassandraCIDRAuthorizer stores user's CIDR permissions in system_auth.cidr_permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer, otherwise any changes to

View File

@ -162,6 +162,10 @@ batchlog_replay_throttle: 1024KiB
# Authentication backend, implementing IAuthenticator; used to identify users
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
# PasswordAuthenticator}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllAuthenticator performs no checks - set it to disable authentication.
# - PasswordAuthenticator relies on username/password pairs to authenticate
@ -169,7 +173,7 @@ batchlog_replay_throttle: 1024KiB
# Please increase system_auth keyspace replication factor if you use this authenticator.
# If using PasswordAuthenticator, CassandraRoleManager must also be used (see below)
authenticator:
class_name : org.apache.cassandra.auth.AllowAllAuthenticator
class_name : AllowAllAuthenticator
# MutualTlsAuthenticator can be configured using the following configuration. One can add their own validator
# which implements MutualTlsCertificateValidator class and provide logic for extracting identity out of certificates
# and validating certificates.
@ -180,11 +184,16 @@ authenticator:
# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
# CassandraAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
# - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer.
authorizer: AllowAllAuthorizer
authorizer:
class_name: AllowAllAuthorizer
# Part of the Authentication & Authorization backend, implementing IRoleManager; used
# to maintain grants and memberships between roles.
@ -192,25 +201,40 @@ authorizer: AllowAllAuthorizer
# which stores role information in the system_auth keyspace. Most functions of the
# IRoleManager require an authenticated login, so unless the configured IAuthenticator
# actually implements authentication, most of this functionality will be unavailable.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - CassandraRoleManager stores role data in the system_auth keyspace. Please
# increase system_auth keyspace replication factor if you use this role manager.
role_manager: CassandraRoleManager
role_manager:
class_name: CassandraRoleManager
# Network authorization backend, implementing INetworkAuthorizer; used to restrict user
# access to certain DCs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer,
# CassandraNetworkAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllNetworkAuthorizer allows access to any DC to any user - set it to disable authorization.
# - CassandraNetworkAuthorizer stores permissions in system_auth.network_permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer.
network_authorizer: AllowAllNetworkAuthorizer
network_authorizer:
class_name: AllowAllNetworkAuthorizer
# CIDR authorization backend, implementing ICIDRAuthorizer; used to restrict user
# access from certain CIDRs
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllCIDRAuthorizer,
# CassandraCIDRAuthorizer}.
# Optional parameters can be specified in the form of:
# parameters:
# param_key1: param_value1
# ...
#
# - AllowAllCIDRAuthorizer allows access from any CIDR to any user - set it to disable CIDR authorization.
# - CassandraCIDRAuthorizer stores user's CIDR permissions in system_auth.cidr_permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer, otherwise any changes to

View File

@ -18,7 +18,7 @@
package org.apache.cassandra.auth;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,7 +27,6 @@ import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
/**
* Only purpose is to Initialize authentication/authorization via {@link #applyAuth()}.
@ -49,14 +48,10 @@ public final class AuthConfig
Config conf = DatabaseDescriptor.getRawConfig();
IAuthenticator authenticator = new AllowAllAuthenticator();
/* Authentication, authorization and role management backend, implementing IAuthenticator, IAuthorizer & IRoleMapper*/
if (conf.authenticator != null)
{
authenticator = ParameterizedClass.newInstance(conf.authenticator,
Arrays.asList("", AuthConfig.class.getPackage().getName()));
}
/* Authentication, authorization and role management backend, implementing IAuthenticator, I*Authorizer & IRoleManager */
IAuthenticator authenticator = authInstantiate(conf.authenticator, AllowAllAuthenticator.class);
// the configuration options regarding credentials caching are only guaranteed to
// work with PasswordAuthenticator, so log a message if some other authenticator
@ -75,10 +70,7 @@ public final class AuthConfig
// authorizer
IAuthorizer authorizer = new AllowAllAuthorizer();
if (conf.authorizer != null)
authorizer = FBUtilities.newAuthorizer(conf.authorizer);
IAuthorizer authorizer = authInstantiate(conf.authorizer, AllowAllAuthorizer.class);
if (!authenticator.requireAuthentication() && authorizer.requireAuthorization())
throw new ConfigurationException(conf.authenticator.class_name + " can't be used with " + conf.authorizer, false);
@ -87,11 +79,7 @@ public final class AuthConfig
// role manager
IRoleManager roleManager;
if (conf.role_manager != null)
roleManager = FBUtilities.newRoleManager(conf.role_manager);
else
roleManager = new CassandraRoleManager();
IRoleManager roleManager = authInstantiate(conf.role_manager, CassandraRoleManager.class);
if (authenticator instanceof PasswordAuthenticator && !(roleManager instanceof CassandraRoleManager))
throw new ConfigurationException("CassandraRoleManager must be used with PasswordAuthenticator", false);
@ -100,29 +88,32 @@ public final class AuthConfig
// authenticator
if (conf.internode_authenticator != null)
{
DatabaseDescriptor.setInternodeAuthenticator(ParameterizedClass.newInstance(conf.internode_authenticator,
Arrays.asList("", AuthConfig.class.getPackage().getName())));
}
IInternodeAuthenticator internodeAuthenticator = authInstantiate(conf.internode_authenticator,
AllowAllInternodeAuthenticator.class);
DatabaseDescriptor.setInternodeAuthenticator(internodeAuthenticator);
// network authorizer
INetworkAuthorizer networkAuthorizer = FBUtilities.newNetworkAuthorizer(conf.network_authorizer);
DatabaseDescriptor.setNetworkAuthorizer(networkAuthorizer);
INetworkAuthorizer networkAuthorizer = authInstantiate(conf.network_authorizer, AllowAllNetworkAuthorizer.class);
if (networkAuthorizer.requireAuthorization() && !authenticator.requireAuthentication())
{
throw new ConfigurationException(conf.network_authorizer + " can't be used with " + conf.authenticator.class_name, false);
}
DatabaseDescriptor.setNetworkAuthorizer(networkAuthorizer);
// cidr authorizer
ICIDRAuthorizer cidrAuthorizer = ICIDRAuthorizer.newCIDRAuthorizer(conf.cidr_authorizer);
DatabaseDescriptor.setCIDRAuthorizer(cidrAuthorizer);
ICIDRAuthorizer cidrAuthorizer = authInstantiate(conf.cidr_authorizer, AllowAllCIDRAuthorizer.class);
if (cidrAuthorizer.requireAuthorization() && !authenticator.requireAuthentication())
{
throw new ConfigurationException(conf.cidr_authorizer + " can't be used with " + conf.authenticator, false);
}
DatabaseDescriptor.setCIDRAuthorizer(cidrAuthorizer);
// Validate at last to have authenticator, authorizer, role-manager and internode-auth setup
// in case these rely on each other.
@ -133,4 +124,21 @@ public final class AuthConfig
cidrAuthorizer.validateConfiguration();
DatabaseDescriptor.getInternodeAuthenticator().validateConfiguration();
}
private static <T> T authInstantiate(ParameterizedClass authCls, Class<T> defaultCls) {
if (authCls != null && authCls.class_name != null)
{
String authPackage = AuthConfig.class.getPackage().getName();
return ParameterizedClass.newInstance(authCls, List.of("", authPackage));
}
try
{
return defaultCls.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
throw new ConfigurationException("Failed to instantiate " + defaultCls.getName(), e);
}
}
}

View File

@ -21,10 +21,8 @@ package org.apache.cassandra.auth;
import java.net.InetAddress;
import java.util.Set;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.metrics.CIDRAuthorizerMetrics;
import org.apache.cassandra.utils.FBUtilities;
/**
* Backend for CIDR authorization feature
@ -40,21 +38,6 @@ public interface ICIDRAuthorizer
ENFORCE
}
public static ICIDRAuthorizer newCIDRAuthorizer(ParameterizedClass cidrAuthorizer)
{
if (cidrAuthorizer == null || cidrAuthorizer.class_name == null)
{
return new AllowAllCIDRAuthorizer();
}
String className = cidrAuthorizer.class_name;
if (!className.contains("."))
{
className = "org.apache.cassandra.auth." + className;
}
return FBUtilities.construct(className, "cidr authorizer");
}
public void setup();
/**

View File

@ -79,10 +79,10 @@ public class Config
public String cluster_name = "Test Cluster";
public ParameterizedClass authenticator;
public String authorizer;
public String role_manager;
public ParameterizedClass authorizer;
public ParameterizedClass role_manager;
public ParameterizedClass crypto_provider;
public String network_authorizer;
public ParameterizedClass network_authorizer;
public ParameterizedClass cidr_authorizer;
@Replaces(oldName = "permissions_validity_in_ms", converter = Converters.MILLIS_DURATION_INT, deprecated = true)

View File

@ -73,11 +73,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.audit.IAuditLogger;
import org.apache.cassandra.auth.AllowAllNetworkAuthorizer;
import org.apache.cassandra.auth.IAuthenticator;
import org.apache.cassandra.auth.IAuthorizer;
import org.apache.cassandra.auth.INetworkAuthorizer;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.marshal.AbstractType;
@ -650,40 +645,6 @@ public class FBUtilities
return FBUtilities.instanceOrConstruct(partitionerClassName, "partitioner");
}
public static IAuthorizer newAuthorizer(String className) throws ConfigurationException
{
if (!className.contains("."))
className = "org.apache.cassandra.auth." + className;
return FBUtilities.construct(className, "authorizer");
}
public static IAuthenticator newAuthenticator(String className) throws ConfigurationException
{
if (!className.contains("."))
className = "org.apache.cassandra.auth." + className;
return FBUtilities.construct(className, "authenticator");
}
public static IRoleManager newRoleManager(String className) throws ConfigurationException
{
if (!className.contains("."))
className = "org.apache.cassandra.auth." + className;
return FBUtilities.construct(className, "role manager");
}
public static INetworkAuthorizer newNetworkAuthorizer(String className)
{
if (className == null)
{
return new AllowAllNetworkAuthorizer();
}
if (!className.contains("."))
{
className = "org.apache.cassandra.auth." + className;
}
return FBUtilities.construct(className, "network authorizer");
}
public static IAuditLogger newAuditLogger(String className, Map<String, String> parameters) throws ConfigurationException
{
if (!className.contains("."))

View File

@ -126,7 +126,9 @@ tables_fail_threshold: "java.lang.Integer"
trace_type_repair_ttl: "org.apache.cassandra.config.DurationSpec.IntSecondsBound"
minimum_replication_factor_warn_threshold: "java.lang.Integer"
page_size_warn_threshold: "java.lang.Integer"
role_manager: "java.lang.String"
role_manager:
class_name: "java.lang.String"
parameters: "java.util.Map"
counter_write_request_timeout: "org.apache.cassandra.config.DurationSpec.LongMillisecondsBound"
denylist_writes_enabled: "java.lang.Boolean"
block_for_peers_in_remote_dcs: "java.lang.Boolean"
@ -158,7 +160,9 @@ internode_max_message_size: "org.apache.cassandra.config.DataStorageSpec.IntByte
items_per_collection_warn_threshold: "java.lang.Integer"
key_cache_migrate_during_compaction: "java.lang.Boolean"
sstable_read_rate_persistence_enabled: "java.lang.Boolean"
network_authorizer: "java.lang.String"
network_authorizer:
class_name: "java.lang.String"
parameters: "java.util.Map"
data_disk_usage_max_disk_size: "org.apache.cassandra.config.DataStorageSpec.LongBytesBound"
memtable_offheap_space: "org.apache.cassandra.config.DataStorageSpec.IntMebibytesBound"
prepared_statements_cache_size: "org.apache.cassandra.config.DataStorageSpec.LongMebibytesBound"
@ -281,7 +285,9 @@ commitlog_directory: "java.lang.String"
unlogged_batch_across_partitions_warn_threshold: "java.lang.Integer"
write_consistency_levels_disallowed: "java.util.Set"
auto_bootstrap: "java.lang.Boolean"
authorizer: "java.lang.String"
authorizer:
class_name: "java.lang.String"
parameters: "java.util.Map"
auth_write_consistency_level: "java.lang.String"
counter_cache_size: "org.apache.cassandra.config.DataStorageSpec.LongMebibytesBound"
denylist_range_reads_enabled: "java.lang.Boolean"

View File

@ -70,8 +70,8 @@ public class AuditLoggerAuthTest
{
OverrideConfigurationLoader.override((config) -> {
config.authenticator = new ParameterizedClass("PasswordAuthenticator");
config.role_manager = "CassandraRoleManager";
config.authorizer = "CassandraAuthorizer";
config.role_manager = new ParameterizedClass("CassandraRoleManager");
config.authorizer = new ParameterizedClass("CassandraAuthorizer");
config.audit_logging_options.enabled = true;
config.audit_logging_options.logger = new ParameterizedClass("InMemoryAuditLogger", null);
});

View File

@ -102,8 +102,14 @@ public class ConfigCompatibilityTest
// Switched to a parameterized class that can construct from a bare string
.add("internode_authenticator types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("authenticator types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("authorizer types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("network_authorizer types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("role_manager types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("Property internode_authenticator used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass")
.add("Property authenticator used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass")
.add("Property authorizer used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass")
.add("Property role_manager used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass")
.add("Property network_authorizer used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass")
.build();
/**

View File

@ -65,7 +65,7 @@ public class CQLUserAuditTest
{
OverrideConfigurationLoader.override((config) -> {
config.authenticator = new ParameterizedClass("PasswordAuthenticator");
config.role_manager = "CassandraRoleManager";
config.role_manager = new ParameterizedClass("CassandraRoleManager");
config.diagnostic_events_enabled = true;
config.audit_logging_options.enabled = true;
config.audit_logging_options.logger = new ParameterizedClass("DiagnosticEventAuditLogger", null);