diff --git a/CHANGES.txt b/CHANGES.txt index 13ac8905f4..14b7b37818 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,7 @@ * ReadCommandController should close fast to avoid deadlock when building secondary index (CASSANDRA-19564) * Redact security-sensitive information in system_views.settings (CASSANDRA-20856) Merged from 4.0: + * Rate limit password changes (CASSANDRA-21202) * Node does not send multiple inflight echos (CASSANDRA-18866) * Obsolete expired SSTables before compaction starts (CASSANDRA-19776) * Switch lz4-java to at.yawk.lz4 version due to CVE (CASSANDRA-21052) diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java index 21cb6f7a3a..d0c46ef3e2 100644 --- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java +++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java @@ -26,6 +26,8 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; @@ -34,6 +36,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.concurrent.ScheduledExecutors; +import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.SchemaConstants; @@ -47,6 +50,7 @@ import org.apache.cassandra.service.StorageService; import org.apache.cassandra.transport.Dispatcher; import org.apache.cassandra.transport.messages.ResultMessage; import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.Clock; import org.mindrot.jbcrypt.BCrypt; import static org.apache.cassandra.service.QueryState.forInternalCalls; @@ -117,6 +121,19 @@ public class CassandraRoleManager implements IRoleManager public static final String GENSALT_LOG2_ROUNDS_PROPERTY = Config.PROPERTY_PREFIX + "auth_bcrypt_gensalt_log2_rounds"; private static final int GENSALT_LOG2_ROUNDS = getGensaltLogRounds(); + private static int PASSWORD_UPDATE_MIN_INTERVAL_MS = CassandraRelevantProperties.ROLE_PASSWORD_UPDATE_MIN_INTERVAL_MS.getInt(); + // in-memory protection against excessive loadRoleWithWritetimeStatement queries + private static Cache recentPasswordUpdates = Caffeine.newBuilder() + .expireAfterWrite(PASSWORD_UPDATE_MIN_INTERVAL_MS, TimeUnit.MILLISECONDS) + .build(); + + @VisibleForTesting + public static synchronized void updatePasswordUpdateMinInterval(int newInterval) + { + recentPasswordUpdates = Caffeine.newBuilder().expireAfterWrite(newInterval, TimeUnit.MILLISECONDS).build(); + PASSWORD_UPDATE_MIN_INTERVAL_MS = newInterval; + } + static int getGensaltLogRounds() { int rounds = Integer.getInteger(GENSALT_LOG2_ROUNDS_PROPERTY, 10); @@ -128,6 +145,7 @@ public class CassandraRoleManager implements IRoleManager } private SelectStatement loadRoleStatement; + private SelectStatement loadRoleWithWritetimeStatement; private final Set