Skip to content

Commit

Permalink
Expose auth mode in system_views.clients, nodetool clientstats, metrics
Browse files Browse the repository at this point in the history
Adds 'authenticationMode' and 'metadata' fields to AuthenticatedUser to add context
about how the user was authenticated and updates system_views.clients,
nodetool clientstats (behind --verbose flag) to include this information.

Also adds new metrics to ClientMetrics to help operators identify which
authentication modes are being used.

patch by Andy Tolbert; reviewed by Francisco Guerrero, Stefan Miklosovic for CASSANDRA-19366
  • Loading branch information
tolbertam authored and smiklosovic committed Feb 13, 2024
1 parent bec6bfd commit 4120b8c
Show file tree
Hide file tree
Showing 39 changed files with 1,564 additions and 250 deletions.
1 change: 1 addition & 0 deletions CHANGES.txt
@@ -1,4 +1,5 @@
5.1
* Expose auth mode in system_views.clients, nodetool clientstats, metrics (CASSANDRA-19366)
* Remove sealed_periods and last_sealed_period tables (CASSANDRA-19189)
* Improve setup and initialisation of LocalLog/LogSpec (CASSANDRA-19271)
* Refactor structure of caching metrics and expose auth cache metrics via JMX (CASSANDRA-17062)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.txt
Expand Up @@ -81,6 +81,10 @@ New features
- Whether bulk loading of SSTables is allowed.
- nodetool tpstats can display core pool size, max pool size and max tasks queued if --verbose / -v flag is specified.
system_views.thread_pools adds core_pool_size, max_pool_size and max_tasks_queued columns.
- Authentication mode is exposed in system_views.clients table, nodetool clientstats and ClientMetrics
to help operators identify which authentication modes are being used. nodetool clientstats introduces --verbose flag
behind which this information is visible.


Upgrading
---------
Expand Down
81 changes: 77 additions & 4 deletions doc/modules/cassandra/pages/managing/operating/metrics.adoc
Expand Up @@ -969,14 +969,87 @@ Reported name format:
[cols=",,",options="header",]
|===
|Name |Type |Description
|connectedNativeClients |Gauge<Integer> |Number of clients connected to
|AuthFailure |Meter | Rate of failed authentications

|AuthSuccess |Meter | Rate of successful authentications

|ClientsByProtocolVersion |Gauge<List<Map<String, Integer>> | List of
all connections' protocol version and ip address

|ConnectedNativeClients |Gauge<Integer> |Number of clients connected to
this nodes native protocol server

|connections |Gauge<List<Map<String, String>> |List of all connections
|ConnectedNativeClientsByUser |Gauge<Map<String, Integer>> |Number of
connnective native clients by username

|Connections |Gauge<List<Map<String, String>> |List of all connections
and their state information

|connectedNativeClientsByUser |Gauge<Map<String, Int> |Number of
connnective native clients by username
|PausedConnections|Gauge<Integer>|Number of connections currently
paused by rate limiter

|ProtocolException|Meter|Rate of requests resulting in a protocol
exception

|UnknownException|Meter|Rate of requests resulting in an unknown
exception

|RequestDiscarded|Meter|Rate of requests discarded by rate limiter

|RequestDispatched|Meter|Rate of requests dispatched (not discarded)

|RequestsSizeByIpDistribution|Histogram|Histogram of distribution of
requests coming from unique IPs

|===

== Client Encryption Metrics

Metrics specific to Client encryption

*Metric Name*::
`org.apache.cassandra.metrics.Client.<Encrypted|Unencrypted>.<MetricName>`
*JMX MBean*::
`org.apache.cassandra.metrics:type=Client scope=<Encrypted|Unencrypted> name=<MetricName>`

[cols=",,",options="header",]
|===
|Name |Type |Description
|ConnectedNativeClients |Gauge<Integer> |Number of clients connected in
this way to this nodes native protocol server

|===

== Client Authentication Mode-Specific Metrics

Metrics specific to connectivity for a given Authentication 'mode'.

*Metric Name*::
`org.apache.cassandra.metrics.Client.<Mode>.<MetricName>`
*JMX MBean*::
`org.apache.cassandra.metrics:type=Client scope=<Mode> name=<MetricName>`

An authentication mode is a supported method of authentication. The following
authentication modes exist for the given supported `IAuthenticators`:

* `PasswordAuthenticator` - Password
* `MutualTlsAuthenticator` - MutualTls
* `MutualTlsWithPasswordFallbackAuthenticator` - MutualTls, Password
* `AllowAllAuthenticator` - Unauthenticated

A custom implementation of `IAuthenticator` may expose metrics on their own
custom mode by implementing `IAuthenticator.getSupportedAuthenticationModes()`.

[cols=",,",options="header",]
|===
|Name |Type |Description
|ConnectedNativeClients |Gauge<Int> |Number of clients connected and
authenticated to this nodes native protocol server using this mode

|AuthFailure |Meter | Rate of failed authentications using this mode

|AuthSuccess |Meter | Rate of successful authentications using this mode

|===

== Batch Metrics
Expand Down
8 changes: 8 additions & 0 deletions src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java
Expand Up @@ -29,6 +29,8 @@ public class AllowAllAuthenticator implements IAuthenticator
{
private static final SaslNegotiator AUTHENTICATOR_INSTANCE = new Negotiator();

private static final Set<AuthenticationMode> AUTHENTICATION_MODES = Collections.singleton(AuthenticationMode.UNAUTHENTICATED);

public boolean requireAuthentication()
{
return false;
Expand All @@ -52,6 +54,12 @@ public SaslNegotiator newSaslNegotiator(InetAddress clientAddress)
return AUTHENTICATOR_INSTANCE;
}

@Override
public Set<AuthenticationMode> getSupportedAuthenticationModes()
{
return AUTHENTICATION_MODES;
}

public AuthenticatedUser legacyAuthenticate(Map<String, String> credentialsData)
{
return AuthenticatedUser.ANONYMOUS_USER;
Expand Down
59 changes: 58 additions & 1 deletion src/java/org/apache/cassandra/auth/AuthenticatedUser.java
Expand Up @@ -18,12 +18,17 @@
package org.apache.cassandra.auth;

import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Objects;

import org.apache.cassandra.auth.IAuthenticator.AuthenticationMode;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Datacenters;

import static org.apache.cassandra.auth.IAuthenticator.AuthenticationMode.UNAUTHENTICATED;

/**
* Returned from IAuthenticator#authenticate(), represents an authenticated user everywhere internally.
*
Expand Down Expand Up @@ -55,13 +60,39 @@ public static void init()

private final String name;

// Primary Role of the logged in user
private final AuthenticationMode authenticationMode;

private final Map<String, Object> metadata;

// Primary Role of the logged-in user
private final RoleResource role;

public AuthenticatedUser(String name)
{
this(name, UNAUTHENTICATED);
}

public AuthenticatedUser(String name, AuthenticationMode authenticationMode)
{
this(name, authenticationMode, Collections.emptyMap());
}

/**
* Defines authenticated user context established within a client connection.
*
* @param name The user's role name
* @param authenticationMode How the user was authenticated
* @param metadata contextual metadata about how the user was authenticated. Note that this data is exposed
* through the <code>system_views.clients table</code>, <code>nodetool clientstats</code> and
* {@link org.apache.cassandra.metrics.ClientMetrics}-based JMX Beans. Implementors should
* take care not to store anything sensitive here.
*/
public AuthenticatedUser(String name, AuthenticationMode authenticationMode, Map<String, Object> metadata)
{
this.name = name;
this.role = RoleResource.role(name);
this.authenticationMode = authenticationMode;
this.metadata = metadata;
}

public String getName()
Expand All @@ -74,6 +105,26 @@ public RoleResource getPrimaryRole()
return role;
}

/**
* @returns the mode of authentication used to authenticate this user
*/
public AuthenticationMode getAuthenticationMode()
{
return authenticationMode;
}

/**
* @returns {@link IAuthenticator}-contextual metadata about how the user was authenticated.
* <p>
* Note that this data is exposed through the <code>system_views.clients table</code>,
* <code>nodetool clientstats</code> and {@link org.apache.cassandra.metrics.ClientMetrics}-based JMX Beans.
* Implementors should take care not to store anything sensitive here.
*/
public Map<String, Object> getMetadata()
{
return metadata;
}

/**
* Checks the user's superuser status.
* Only a superuser is allowed to perform CREATE USER and DROP USER queries.
Expand Down Expand Up @@ -179,6 +230,12 @@ public boolean equals(Object o)
@Override
public int hashCode()
{
// Note: for reasons of maintaining the invariant that an object that equals maintains the same hashCode,
// we do not include mode and metadata in the hashCode calculation.
// This is particularly salient as there are cases where AuthenticatedUser is used as a key in
// Role/Permissions cache. In effect, we would like to treat all connections sharing the same name as the same
// user, where mode and metadata are just additional context about how the user authenticated that
// should not factor into 'equivalence' of users.
return Objects.hashCode(name);
}
}

0 comments on commit 4120b8c

Please sign in to comment.