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
5 changes: 3 additions & 2 deletions conf/cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2019,8 +2019,9 @@ trace_type_repair_ttl: 7d

# If unset, all GC Pauses greater than gc_log_threshold will log at
# INFO level
# UDFs (user defined functions) are disabled by default.
# As of Cassandra 3.0 there is a sandbox in place that should prevent execution of evil code.
# Cassandra disables user-defined functions (UDFs) by default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this in cassandra_latest.yaml too

# Cassandra restricts UDF bytecode and class loading within its JVM.
# These restrictions do not provide complete process isolation.
user_defined_functions_enabled: false

# Triggers are enabled by default.
Expand Down
9 changes: 9 additions & 0 deletions conf/jvm-server.options
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@
# Set the default location for the trigger JARs. (Default: conf/triggers)
#-Dcassandra.triggers_dir=directory

# Select the sandbox for user-defined functions (UDFs).
# Valid values are auto (default), sandbox, and securitymanager.
# The auto setting uses the Java security manager based sandbox before Java Development Kit (JDK) 24.
# It uses the bytecode sandbox on JDK 24 and later.
# The sandbox setting uses the bytecode sandbox on every supported JDK.
# The securitymanager setting requires the Java security manager based sandbox.
# Startup fails if the runtime cannot install the security manager.
#-Dcassandra.udf.security_mechanism=auto

# For testing new compaction and compression strategies. It allows you to experiment with different
# strategies and benchmark write performance differences without affecting the production workload.
#-Dcassandra.write_survey=true
Expand Down
55 changes: 55 additions & 0 deletions doc/modules/cassandra/pages/developing/cql/functions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,61 @@ include::cassandra:example$JAVA/udf_imports.java[]

Please note, that these convenience imports are not available for script UDFs.

==== UDF restrictions

UDFs run inside the Cassandra Java Virtual Machine (JVM).
Cassandra checks UDF bytecode.
It restricts the classes that a UDF can load.
These controls do not provide complete process isolation.
A UDF can consume processor time or heap memory.
Grant permission to create functions only to trusted roles.

Set the JVM system property `cassandra.udf.security_mechanism` before node startup.
The default value is `auto`.

[cols="1,3",options="header"]
|===
|Value |Behavior
|`auto` |Cassandra uses the Java security manager based sandbox before Java Development Kit (JDK) 24.
It uses the bytecode sandbox on JDK 24 and later.
|`sandbox` |Cassandra uses the bytecode sandbox on every supported JDK.
It skips security manager and security policy installation.
|`securitymanager` |Cassandra requires the Java security manager based sandbox.
Startup fails if the runtime cannot install the security manager.
|===

An invalid value causes a configuration error.
For example, add `-Dcassandra.udf.security_mechanism=sandbox` to the JVM options to select the sandbox.
Use a JDK version that Cassandra supports.

The bytecode sandbox rejects restricted calls when Cassandra creates a function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section feels like AI stuff. I can imagine the prompt "be concise as possible, no fluff" and this is the result :)

It blocks system property access and environment access.
It blocks process exit and native library loading.
It blocks changes to standard input and output.
It also blocks changes to the default process locale and time zone.
It blocks indirect property access through `Integer.getInteger`, `Long.getLong`, and `Boolean.getBoolean`.
The class loader blocks file access and process control.
It also blocks reflection and method handles.
The verifier rejects `ClassLoader` calls.
It also rejects calls through `Module` and `ModuleLayer`.
It rejects `Class.getModule`.
A function cannot declare additional classes.

UDF threads can execute calls to `System.nanoTime`, `System.currentTimeMillis`, and `System.arraycopy`.
Cassandra applies warning and failure timeouts when `user_defined_functions_threads_enabled=true`.
The `user_function_timeout_policy` setting controls the response to a failure timeout.

To permit restricted `System` access, use these settings:

. Set `allow_insecure_udfs=true` in `cassandra.yaml`.
. Set `user_defined_functions_threads_enabled=false` in `cassandra.yaml`.
. Set `allow_extra_insecure_udfs=true` in `cassandra.yaml`.

This configuration permits restricted `System` method calls and indirect property access.
With UDF threads enabled, the selected sandbox still enforces its restrictions.
The class loader restrictions and the base bytecode checks still apply.
Cassandra applies execution timeouts only when UDF threads are enabled.

[[create-function-statement]]
=== CREATE FUNCTION statement

Expand Down
6 changes: 2 additions & 4 deletions src/java/org/apache/cassandra/audit/AuditLogManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -57,6 +55,7 @@
import org.apache.cassandra.exceptions.PreparedQueryNotFoundException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.security.JMXSubjects;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.messages.ResultMessage;
Expand Down Expand Up @@ -493,8 +492,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
return null;
}

AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
Subject subject = JMXSubjects.current();

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.util.Collections;
import java.util.Set;
Expand Down Expand Up @@ -53,6 +51,7 @@
import org.apache.cassandra.auth.RoleResource;
import org.apache.cassandra.auth.Roles;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.security.JMXSubjects;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.JmxInvocationListener;
import org.apache.cassandra.utils.MBeanWrapper;
Expand Down Expand Up @@ -161,9 +160,8 @@ public Object invoke(Object proxy, Method method, Object[] args)
{
String methodName = method.getName();

// Retrieve Subject from current AccessControlContext
AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
// Retrieve the Subject for the current JMX invocation.
Subject subject = JMXSubjects.current();

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,17 @@ public enum CassandraRelevantProperties
UCS_SURVIVAL_FACTOR("unified_compaction.survival_factor", "1"),
UCS_TARGET_SSTABLE_SIZE("unified_compaction.target_sstable_size", "1GiB"),
UDF_EXECUTOR_THREAD_KEEPALIVE_MS("cassandra.udf_executor_thread_keepalive_ms", "30000"),
/**
* Selects the user-defined function (UDF) sandbox mechanism.
* <ul>
* <li>{@code auto} uses a {@link java.lang.SecurityManager} before Java Development Kit (JDK) 24.
* It uses the bytecode sandbox on JDK 24 and later.</li>
* <li>{@code securitymanager} requires a SecurityManager.
* Startup fails if the runtime cannot install it.</li>
* <li>{@code sandbox} uses the bytecode sandbox on every supported JDK.</li>
* </ul>
*/
UDF_SECURITY_MECHANISM("cassandra.udf.security_mechanism", "auto"),
UNSAFE_SYSTEM("cassandra.unsafesystem"),
/** User's home directory. */
USER_HOME("user.home"),
Expand Down
10 changes: 6 additions & 4 deletions src/java/org/apache/cassandra/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,8 @@ public static class SSTableConfig
public volatile boolean use_statements_enabled = true;

/**
* Optionally disable asynchronous UDF execution.
* Disabling asynchronous UDF execution also implicitly disables the security-manager!
* Controls asynchronous execution of user-defined functions (UDFs).
* If Cassandra uses the security manager, disabling asynchronous execution disables its UDF execution checks.
* By default, async UDF execution is enabled to be able to detect UDFs that run too long / forever and be
* able to fail fast - i.e. stop the Cassandra daemon, which is currently the only appropriate approach to
* "tell" a user that there's something really wrong with the UDF.
Expand All @@ -745,7 +745,9 @@ public static class SSTableConfig
public boolean allow_insecure_udfs = false;

/**
* Set this to allow UDFs accessing java.lang.System.* methods, which basically allows UDFs to execute any arbitrary code on the system.
* Permits restricted System method calls and indirect property access only when UDF threads are disabled.
* With UDF threads enabled, the selected sandbox still enforces its restrictions.
* Class loader restrictions and base bytecode checks still apply.
*/
public boolean allow_extra_insecure_udfs = false;

Expand Down Expand Up @@ -1650,4 +1652,4 @@ public enum CQLStartTime
* 6.0 and later.
*/
public volatile boolean gossip_quarantine_disabled = false;
}
}
Loading