You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ma...@apache.org on 2022/02/07 12:09:32 UTC

[cassandra] branch cassandra-3.11 updated (5d0aeb1 -> b022aec)

This is an automated email from the ASF dual-hosted git repository.

marcuse pushed a change to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git.


    from 5d0aeb1  Merge branch 'cassandra-3.0' into cassandra-3.11
     new 5c9ba06  Extend operator control over the UDF threading model
     new b022aec  Merge branch 'cassandra-3.0' into cassandra-3.11

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/java/org/apache/cassandra/config/Config.java      | 13 +++++++++++++
 .../apache/cassandra/config/DatabaseDescriptor.java   | 16 ++++++++++++++++
 .../apache/cassandra/cql3/functions/UDFunction.java   | 19 ++++++++++++++++++-
 .../security/ThreadAwareSecurityManager.java          |  5 +++++
 4 files changed, 52 insertions(+), 1 deletion(-)

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[cassandra] 01/01: Merge branch 'cassandra-3.0' into cassandra-3.11

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

marcuse pushed a commit to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit b022aecfa665c021db4fb39267a7cf83f97ec135
Merge: 5d0aeb1 5c9ba06
Author: Marcus Eriksson <ma...@apache.org>
AuthorDate: Mon Feb 7 12:59:52 2022 +0100

    Merge branch 'cassandra-3.0' into cassandra-3.11

 src/java/org/apache/cassandra/config/Config.java      | 13 +++++++++++++
 .../apache/cassandra/config/DatabaseDescriptor.java   | 16 ++++++++++++++++
 .../apache/cassandra/cql3/functions/UDFunction.java   | 19 ++++++++++++++++++-
 .../security/ThreadAwareSecurityManager.java          |  5 +++++
 4 files changed, 52 insertions(+), 1 deletion(-)

diff --cc src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index a5a02ce,ca946ce..d64d054
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@@ -704,227 -737,6 +704,233 @@@ public class DatabaseDescripto
  
          conf.server_encryption_options.validate();
  
 +        if (conf.user_defined_function_fail_timeout < 0)
 +            throw new ConfigurationException("user_defined_function_fail_timeout must not be negative", false);
 +        if (conf.user_defined_function_warn_timeout < 0)
 +            throw new ConfigurationException("user_defined_function_warn_timeout must not be negative", false);
 +
 +        if (conf.user_defined_function_fail_timeout < conf.user_defined_function_warn_timeout)
 +            throw new ConfigurationException("user_defined_function_warn_timeout must less than user_defined_function_fail_timeout", false);
 +
++        if (!conf.allow_insecure_udfs && !conf.enable_user_defined_functions_threads)
++            throw new ConfigurationException("To be able to set enable_user_defined_functions_threads: false you need to set allow_insecure_udfs: true - this is an unsafe configuration and is not recommended.");
++
++        if (conf.allow_extra_insecure_udfs)
++            logger.warn("Allowing java.lang.System.* access in UDFs is dangerous and not recommended. Set allow_extra_insecure_udfs: false to disable.");
++
 +        if (conf.commitlog_segment_size_in_mb <= 0)
 +            throw new ConfigurationException("commitlog_segment_size_in_mb must be positive, but was "
 +                    + conf.commitlog_segment_size_in_mb, false);
 +        else if (conf.commitlog_segment_size_in_mb >= 2048)
 +            throw new ConfigurationException("commitlog_segment_size_in_mb must be smaller than 2048, but was "
 +                    + conf.commitlog_segment_size_in_mb, false);
 +
 +        if (conf.max_mutation_size_in_kb == null)
 +            conf.max_mutation_size_in_kb = conf.commitlog_segment_size_in_mb * 1024 / 2;
 +        else if (conf.commitlog_segment_size_in_mb * 1024 < 2 * conf.max_mutation_size_in_kb)
 +            throw new ConfigurationException("commitlog_segment_size_in_mb must be at least twice the size of max_mutation_size_in_kb / 1024", false);
 +
 +        // native transport encryption options
 +        if (conf.native_transport_port_ssl != null
 +            && conf.native_transport_port_ssl != conf.native_transport_port
 +            && !conf.client_encryption_options.enabled)
 +        {
 +            throw new ConfigurationException("Encryption must be enabled in client_encryption_options for native_transport_port_ssl", false);
 +        }
 +
 +        // If max protocol version has been set, just validate it's within an acceptable range
 +        if (conf.native_transport_max_negotiable_protocol_version != Integer.MIN_VALUE)
 +        {
 +            try
 +            {
 +                ProtocolVersion.decode(conf.native_transport_max_negotiable_protocol_version, ProtocolVersionLimit.SERVER_DEFAULT);
 +                logger.info("Native transport max negotiable version statically limited to {}", conf.native_transport_max_negotiable_protocol_version);
 +            }
 +            catch (Exception e)
 +            {
 +                throw new ConfigurationException("Invalid setting for native_transport_max_negotiable_protocol_version; " +
 +                                                 ProtocolVersion.invalidVersionMessage(conf.native_transport_max_negotiable_protocol_version));
 +            }
 +        }
 +
 +        if (conf.max_value_size_in_mb <= 0)
 +            throw new ConfigurationException("max_value_size_in_mb must be positive", false);
 +        else if (conf.max_value_size_in_mb >= 2048)
 +            throw new ConfigurationException("max_value_size_in_mb must be smaller than 2048, but was "
 +                    + conf.max_value_size_in_mb, false);
 +
 +        switch (conf.disk_optimization_strategy)
 +        {
 +            case ssd:
 +                diskOptimizationStrategy = new SsdDiskOptimizationStrategy(conf.disk_optimization_page_cross_chance);
 +                break;
 +            case spinning:
 +                diskOptimizationStrategy = new SpinningDiskOptimizationStrategy();
 +                break;
 +        }
 +
 +        try
 +        {
 +            ParameterizedClass strategy = conf.back_pressure_strategy != null ? conf.back_pressure_strategy : RateBasedBackPressure.withDefaultParams();
 +            Class<?> clazz = Class.forName(strategy.class_name);
 +            if (!BackPressureStrategy.class.isAssignableFrom(clazz))
 +                throw new ConfigurationException(strategy + " is not an instance of " + BackPressureStrategy.class.getCanonicalName(), false);
 +
 +            Constructor<?> ctor = clazz.getConstructor(Map.class);
 +            BackPressureStrategy instance = (BackPressureStrategy) ctor.newInstance(strategy.parameters);
 +            logger.info("Back-pressure is {} with strategy {}.", backPressureEnabled() ? "enabled" : "disabled", conf.back_pressure_strategy);
 +            backPressureStrategy = instance;
 +        }
 +        catch (ConfigurationException ex)
 +        {
 +            throw ex;
 +        }
 +        catch (Exception ex)
 +        {
 +            throw new ConfigurationException("Error configuring back-pressure strategy: " + conf.back_pressure_strategy, ex);
 +        }
 +
 +        if (conf.otc_coalescing_enough_coalesced_messages > 128)
 +            throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be smaller than 128", false);
 +
 +        if (conf.otc_coalescing_enough_coalesced_messages <= 0)
 +            throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be positive", false);
 +    }
 +
 +    private static String storagedirFor(String type)
 +    {
 +        return storagedir(type + "_directory") + File.separator + type;
 +    }
 +
 +    private static String storagedir(String errMsgType)
 +    {
 +        String storagedir = System.getProperty(Config.PROPERTY_PREFIX + "storagedir", null);
 +        if (storagedir == null)
 +            throw new ConfigurationException(errMsgType + " is missing and -Dcassandra.storagedir is not set", false);
 +        return storagedir;
 +    }
 +
 +    public static void applyAddressConfig() throws ConfigurationException
 +    {
 +        applyAddressConfig(conf);
 +    }
 +
 +    public static void applyAddressConfig(Config config) throws ConfigurationException
 +    {
 +        listenAddress = null;
 +        rpcAddress = null;
 +        broadcastAddress = null;
 +        broadcastRpcAddress = null;
 +
 +        /* Local IP, hostname or interface to bind services to */
 +        if (config.listen_address != null && config.listen_interface != null)
 +        {
 +            throw new ConfigurationException("Set listen_address OR listen_interface, not both", false);
 +        }
 +        else if (config.listen_address != null)
 +        {
 +            try
 +            {
 +                listenAddress = InetAddress.getByName(config.listen_address);
 +            }
 +            catch (UnknownHostException e)
 +            {
 +                throw new ConfigurationException("Unknown listen_address '" + config.listen_address + '\'', false);
 +            }
 +
 +            if (listenAddress.isAnyLocalAddress())
 +                throw new ConfigurationException("listen_address cannot be a wildcard address (" + config.listen_address + ")!", false);
 +        }
 +        else if (config.listen_interface != null)
 +        {
 +            listenAddress = getNetworkInterfaceAddress(config.listen_interface, "listen_interface", config.listen_interface_prefer_ipv6);
 +        }
 +
 +        /* Gossip Address to broadcast */
 +        if (config.broadcast_address != null)
 +        {
 +            try
 +            {
 +                broadcastAddress = InetAddress.getByName(config.broadcast_address);
 +            }
 +            catch (UnknownHostException e)
 +            {
 +                throw new ConfigurationException("Unknown broadcast_address '" + config.broadcast_address + '\'', false);
 +            }
 +
 +            if (broadcastAddress.isAnyLocalAddress())
 +                throw new ConfigurationException("broadcast_address cannot be a wildcard address (" + config.broadcast_address + ")!", false);
 +        }
 +
 +        /* Local IP, hostname or interface to bind RPC server to */
 +        if (config.rpc_address != null && config.rpc_interface != null)
 +        {
 +            throw new ConfigurationException("Set rpc_address OR rpc_interface, not both", false);
 +        }
 +        else if (config.rpc_address != null)
 +        {
 +            try
 +            {
 +                rpcAddress = InetAddress.getByName(config.rpc_address);
 +            }
 +            catch (UnknownHostException e)
 +            {
 +                throw new ConfigurationException("Unknown host in rpc_address " + config.rpc_address, false);
 +            }
 +        }
 +        else if (config.rpc_interface != null)
 +        {
 +            rpcAddress = getNetworkInterfaceAddress(config.rpc_interface, "rpc_interface", config.rpc_interface_prefer_ipv6);
 +        }
 +        else
 +        {
 +            rpcAddress = FBUtilities.getLocalAddress();
 +        }
 +
 +        /* RPC address to broadcast */
 +        if (config.broadcast_rpc_address != null)
 +        {
 +            try
 +            {
 +                broadcastRpcAddress = InetAddress.getByName(config.broadcast_rpc_address);
 +            }
 +            catch (UnknownHostException e)
 +            {
 +                throw new ConfigurationException("Unknown broadcast_rpc_address '" + config.broadcast_rpc_address + '\'', false);
 +            }
 +
 +            if (broadcastRpcAddress.isAnyLocalAddress())
 +                throw new ConfigurationException("broadcast_rpc_address cannot be a wildcard address (" + config.broadcast_rpc_address + ")!", false);
 +        }
 +        else
 +        {
 +            if (rpcAddress.isAnyLocalAddress())
 +                throw new ConfigurationException("If rpc_address is set to a wildcard address (" + config.rpc_address + "), then " +
 +                                                 "you must set broadcast_rpc_address to a value other than " + config.rpc_address, false);
 +        }
 +    }
 +
 +    public static void applyThriftHSHA()
 +    {
 +        // fail early instead of OOMing (see CASSANDRA-8116)
 +        if (ThriftServerType.HSHA.equals(conf.rpc_server_type) && conf.rpc_max_threads == Integer.MAX_VALUE)
 +            throw new ConfigurationException("The hsha rpc_server_type is not compatible with an rpc_max_threads " +
 +                                             "setting of 'unlimited'.  Please see the comments in cassandra.yaml " +
 +                                             "for rpc_server_type and rpc_max_threads.",
 +                                             false);
 +        if (ThriftServerType.HSHA.equals(conf.rpc_server_type) && conf.rpc_max_threads > (FBUtilities.getAvailableProcessors() * 2 + 1024))
 +            logger.warn("rpc_max_threads setting of {} may be too high for the hsha server and cause unnecessary thread contention, reducing performance", conf.rpc_max_threads);
 +    }
 +
 +    public static void applyEncryptionContext()
 +    {
 +        // always attempt to load the cipher factory, as we could be in the situation where the user has disabled encryption,
 +        // but has existing commitlogs and sstables on disk that are still encrypted (and still need to be read)
 +        encryptionContext = new EncryptionContext(conf.transparent_data_encryption_options);
 +    }
 +
 +    public static void applySeedProvider()
 +    {
          // load the seeds for node contact points
          if (conf.seed_provider == null)
          {
@@@ -2513,7 -2181,17 +2519,17 @@@
          conf.user_defined_function_warn_timeout = userDefinedFunctionWarnTimeout;
      }
  
+     public static boolean allowInsecureUDFs()
+     {
+         return conf.allow_insecure_udfs;
+     }
+ 
+     public static boolean allowExtraInsecureUDFs()
+     {
+         return conf.allow_extra_insecure_udfs;
+     }
+ 
 -    public static boolean enableMaterializedViews()
 +    public static boolean getEnableMaterializedViews()
      {
          return conf.enable_materialized_views;
      }
diff --cc src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
index c9402f1,0000000..3d72559
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
+++ b/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
@@@ -1,214 -1,0 +1,219 @@@
 +/*
 + * 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.security;
 +
 +import java.security.AccessControlException;
 +import java.security.AllPermission;
 +import java.security.CodeSource;
 +import java.security.Permission;
 +import java.security.PermissionCollection;
 +import java.security.Permissions;
 +import java.security.Policy;
 +import java.security.ProtectionDomain;
 +import java.util.Collections;
 +import java.util.Enumeration;
 +
 +import io.netty.util.concurrent.FastThreadLocal;
 +
 +import org.apache.cassandra.utils.logging.LoggingSupportFactory;
++import org.apache.cassandra.config.DatabaseDescriptor;
 +
 +/**
 + * Custom {@link SecurityManager} and {@link Policy} implementation that only performs access checks
 + * if explicitly enabled.
 + * <p>
 + * This implementation gives no measurable performance penalty
 + * (see <a href="http://cstar.datastax.com/tests/id/1d461628-12ba-11e5-918f-42010af0688f">see cstar test</a>).
 + * This is better than the penalty of 1 to 3 percent using a standard {@code SecurityManager} with an <i>allow all</i> policy.
 + * </p>
 + */
 +public final class ThreadAwareSecurityManager extends SecurityManager
 +{
 +    public static final PermissionCollection noPermissions = new PermissionCollection()
 +    {
 +        public void add(Permission permission)
 +        {
 +            throw new UnsupportedOperationException();
 +        }
 +
 +        public boolean implies(Permission permission)
 +        {
 +            return false;
 +        }
 +
 +        public Enumeration<Permission> elements()
 +        {
 +            return Collections.emptyEnumeration();
 +        }
 +    };
 +
 +    private static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = new RuntimePermission("accessDeclaredMembers");
 +    private static final RuntimePermission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
 +    private static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup");
++    private static final RuntimePermission SET_SECURITY_MANAGER_PERMISSION = new RuntimePermission("setSecurityManager");
 +
 +    private static volatile boolean installed;
 +
 +    public static void install()
 +    {
 +        if (installed)
 +            return;
 +        System.setSecurityManager(new ThreadAwareSecurityManager());
 +        LoggingSupportFactory.getLoggingSupport().onStartup();
 +        installed = true;
 +    }
 +
 +    static
 +    {
 +        //
 +        // Use own security policy to be easier (and faster) since the C* has no fine grained permissions.
 +        // Either code has access to everything or code has access to nothing (UDFs).
 +        // This also removes the burden to maintain and configure policy files for production, unit tests etc.
 +        //
 +        // Note: a permission is only granted, if there is no objector. This means that
 +        // AccessController/AccessControlContext collect all applicable ProtectionDomains - only if none of these
 +        // applicable ProtectionDomains denies access, the permission is granted.
 +        // A ProtectionDomain can have its origin at an oridinary code-source or provided via a
 +        // AccessController.doPrivileded() call.
 +        //
 +        Policy.setPolicy(new Policy()
 +        {
 +            public PermissionCollection getPermissions(CodeSource codesource)
 +            {
 +                // contract of getPermissions() methods is to return a _mutable_ PermissionCollection
 +
 +                Permissions perms = new Permissions();
 +
 +                if (codesource == null || codesource.getLocation() == null)
 +                    return perms;
 +
 +                switch (codesource.getLocation().getProtocol())
 +                {
 +                    case "file":
 +                        // All JARs and class files reside on the file system - we can safely
 +                        // assume that these classes are "good".
 +                        perms.add(new AllPermission());
 +                        return perms;
 +                }
 +
 +                return perms;
 +            }
 +
 +            public PermissionCollection getPermissions(ProtectionDomain domain)
 +            {
 +                return getPermissions(domain.getCodeSource());
 +            }
 +
 +            public boolean implies(ProtectionDomain domain, Permission permission)
 +            {
 +                CodeSource codesource = domain.getCodeSource();
 +                if (codesource == null || codesource.getLocation() == null)
 +                    return false;
 +
 +                switch (codesource.getLocation().getProtocol())
 +                {
 +                    case "file":
 +                        // All JARs and class files reside on the file system - we can safely
 +                        // assume that these classes are "good".
 +                        return true;
 +                }
 +
 +                return false;
 +            }
 +        });
 +    }
 +
 +    private static final FastThreadLocal<Boolean> initializedThread = new FastThreadLocal<>();
 +
 +    private ThreadAwareSecurityManager()
 +    {
 +    }
 +
 +    public static boolean isSecuredThread()
 +    {
 +        ThreadGroup tg = Thread.currentThread().getThreadGroup();
 +        if (!(tg instanceof SecurityThreadGroup))
 +            return false;
 +        Boolean threadInitialized = initializedThread.get();
 +        if (threadInitialized == null)
 +        {
 +            initializedThread.set(false);
 +            ((SecurityThreadGroup) tg).initializeThread();
 +            initializedThread.set(true);
 +            threadInitialized = true;
 +        }
 +        return threadInitialized;
 +    }
 +
 +    public void checkAccess(Thread t)
 +    {
 +        // need to override since the default implementation only checks the permission if the current thread's
 +        // in the root-thread-group
 +
 +        if (isSecuredThread())
 +            throw new AccessControlException("access denied: " + MODIFY_THREAD_PERMISSION, MODIFY_THREAD_PERMISSION);
 +        super.checkAccess(t);
 +    }
 +
 +    public void checkAccess(ThreadGroup g)
 +    {
 +        // need to override since the default implementation only checks the permission if the current thread's
 +        // in the root-thread-group
 +
 +        if (isSecuredThread())
 +            throw new AccessControlException("access denied: " + MODIFY_THREADGROUP_PERMISSION, MODIFY_THREADGROUP_PERMISSION);
 +        super.checkAccess(g);
 +    }
 +
 +    public void checkPermission(Permission perm)
 +    {
++        if (!DatabaseDescriptor.enableUserDefinedFunctionsThreads() && !DatabaseDescriptor.allowExtraInsecureUDFs() && SET_SECURITY_MANAGER_PERMISSION.equals(perm))
++            throw new AccessControlException("Access denied");
++
 +        if (!isSecuredThread())
 +            return;
 +
 +        // required by JavaDriver 2.2.0-rc3 and 3.0.0-a2 or newer
 +        // code in com.datastax.driver.core.CodecUtils uses Guava stuff, which in turns requires this permission
 +        if (CHECK_MEMBER_ACCESS_PERMISSION.equals(perm))
 +            return;
 +
 +        super.checkPermission(perm);
 +    }
 +
 +    public void checkPermission(Permission perm, Object context)
 +    {
 +        if (isSecuredThread())
 +            super.checkPermission(perm, context);
 +    }
 +
 +    public void checkPackageAccess(String pkg)
 +    {
 +        if (!isSecuredThread())
 +            return;
 +
 +        if (!((SecurityThreadGroup) Thread.currentThread().getThreadGroup()).isPackageAllowed(pkg))
 +        {
 +            RuntimePermission perm = new RuntimePermission("accessClassInPackage." + pkg);
 +            throw new AccessControlException("access denied: " + perm, perm);
 +        }
 +
 +        super.checkPackageAccess(pkg);
 +    }
 +}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org