CREATE ROLE [ IF NOT EXISTS ]
WITH [ PASSWORD =
[ AND LOGIN =
<true|false>
[ AND SUPERUSER =
<true|false>
[ AND OPTIONS =
] ] ] ] ALTER ROLE
WITH [ PASSWORD =
[ AND LOGIN =
<true|false>
[ AND SUPERUSER =
<true|false>
[ AND OPTIONS =
] ] ] ] DROP ROLE [ IF EXISTS ]
LIST ROLES [ OF
] [ NORECURSIVE ]
</true|false>
</true|false>
</true|false>
</true|false>
Note that when a ROLE is created, it by default does not have "LOGIN" privileges and "SUPERUSER" status.
CREATE USER [ IF NOT EXISTS ]
[ WITH PASSWORD
] [ SUPERUSER | NOSUPERUSER ] ALTER USER
[ WITH PASSWORD
] [ SUPERUSER | NOSUPERUSER ] DROP USER [ IF EXISTS ]
LIST USERS
authenticator: PasswordAuthenticator
Once enabled, a ROLE with "LOGIN" privilege is needed. By default, a ROLE with "LOGIN" privilege and "SUPERUSER" status, called "cassandra" (with password "cassandra"), can be used to connect to the authentication-enabled node to create other ROLEs, as below:
Once the new ROLE is created, you can use it to access Cassandra with the defined password. Please note that for safety purpose, it is suggested to create your own ROLE with "SUPERUSER" privilege and "LOGIN" status and drop the default "cassandra" ROLE (or at least changes its password).
First, the configuration change is made on each node. In my test, I enabled internal authentication on node1 and node2, but NOT on node3. This leads to the situation that I can log in CQLSH on node3 without any constraint, which creates a security hole in the system. In order to avoid this problem, when enabling internal authentication in a Cassandra cluster, the change has to be made on every single node in the cluster. Second, with the default, out-of-the-box CassandraRoleManager implementation, the access control related information is stored in Cassandra's system_auth keyspace. By default, the replication setting of this keyspace is "SimpleStrategy" with replication factor 1. In this case, if we lose the node on which the ROLE was created, other nodes that have internal authentication enabled will have trouble connecting to Cassandra. To test this out, I shutdown node1 and try to connect to CQLSH on node2 using the newly created ROLE, john. The connection is refused:
In order to solve this issue, we should change the replicate factor of system_auth keyspace to multiple nodes (I would recommend to change to ALL nodes in the cluster). After changing the replication factor, it is recommended to run "nodetool repair system_auth" command to bring all nodes in sync right away. Once these changes are made, node2 can connect to CQLSH successfully using ROLE john, even if node1 is down.
roles_validity_in_ms: * validity period for role cache * default value 2000 (0 to disable) * disabled automatically with "AllowAllAuthenticator" roles_update_interval_in_ms: * refresh interval for role cache * default to the value of "roles_validity_in_ms" credentials_validity_in_ms: * validity period of a stored credential in cache (in encrypted form) * default value 2000 (0 to disable) credentials_update_interval_in_ms: * refresh interval for credential cache * default to the value of "credentials_validity_in_ms"
CREATE ALTER DROP SELECT MODIFY AUTHORIZE DESCRIBE EXECUTEThe resources are organized in hierarchies of the following categories:
ALL KEYSPACES -> KEYSPACE
-> TABLE
ALL FUNCTIONS -> ALL FUNCTIONS IN KEYSPACE
-> FUNCTION
ALL ROLES -> ROLE
ALL MBEANS -> MBEAN
(Cassandra 3.6 and later)
One ROLE with enough privilege (with "SUPERUSER" status or "GRANT/REVOKE" privilege) can grant/revoke certain permission to/from a specified ROLE on a particular resource. The CQL statements of doing so is as below:
GRANT ALL |
ON
TO
REVOKE ALL |
ON
FROM
**
and
are from the 2 lists above.
Please note that:
SyntaxException: Resource type DataResource does not support any of the requested permissions
authenticator: CassandraAuthorizerOnce enabled, a ROLE with SUPERUSER status or 'AUTHORIZE' permission can be used to grant permissions on resources to other ROLEs.
permissions_validity_in_ms: * validity period for permission cache * default value 2000 (0 to disable) * disabled automatically with "AllowAllAuthorizer" permissions_update_interval_in_ms: * refresh interval for role cache * default to the value of "permissions_validity_in_ms"
transparent_data_encryption_options: enabled: false chunk_length_kb: 64 cipher: AES/CBC/PKCS5Padding key_alias: testing:1 # CBC IV length for AES needs to be 16 bytes # (which is also the default size) # iv_length: 16 key_provider: - class_name: org.apache.cassandra.security.JKSKeyProvider parameters: - keystore: conf/.keystore keystore_password: cassandra store_type: JCEKS key_password: cassandraBy default, TDE encryption is disabled (" enabled" setting is false). When enabled, Cassandra's out-of-the-box key provider ( JKSKeyProvider) reads the key from a Java Cryptography Extension (JCE)- style keystore that you can specify the following properties in cassandra.yaml:
Algorithm | Max. Keysize -------------|----------------- DES | 64 DESede | 64 RC2 | 128 RC4 | 128 RC5 | 128 RSA | 128 all others | 128If stronger encryption, such as Advanced Encryption Standard (AES) with maximum key size greater than 128-bit(e.g. 256), JCE Unlimited Strength Policy is needed. This is also highly recommended by Cassandra. The installation of JCE Unlimited Strength Policy is quite simple: 1). Download the policy file (in zip format) for your java version (e.g. for Java 8, the file can be downloaded from here) 2). Unzip the zipped file 3). Copy the unzipped local_policy.jar and US_export_policy.jar files to location: $JAVA_HOME/jre/lib/security (e.g. /usr/lib/jvm/java-8-oracle/jre/lib/security). Please note that in that location, the 2 files should have already existed as the default policy file. You can make a copy of them before overwriting them with the new policy file.
keytool -genseckey -alias cass_node1 -keyalg AES -keystore .keystore -keysize 256 -storetype JCEKS -storepass john123 -keypass john123Using the above information, we can enable Cassandra TDE encryption (AES-256) like this:
transparent_data_encryption_options: chunk_length_kb: 64 cipher: AES/CBC/PKCS5Padding enabled: true key_alias: cass_node1 iv_length: 16 key_provider: - class_name: org.apache.cassandra.security.JKSKeyProvider parameters: - key_password: john123 keystore: /.keystore keystore_password: john123 store_type: JCEKSAfter restarting Cassandra service, the commitlog and hints files on this node are AES-256 encrypted.
Compared with 1-Way SSL Authentication, 2-Way SSL Authentication provides better security and identification flexibility.
Approach 1: Copy the public part of each node's certificate to all other nodes. On each node, import all copied parts (from all nodes, including the node itself) into the Truststore of that node. Approach 2: Use a Certificate Authority (CA)'s root certificate to sign each node's certificate. Import the root certificate and the signed certificate into the Keystore of each node. Use the root certificate to build a common Truststore and copy the common Truststore to each node.When the number of nodes in the cluster increase, the operation overhead of approach 1 quickly becomes unmanageable. So unless for a very small cluster and/or for development purpose, approach 2 is always preferred. DataStax's document for Cassandra 3.x provides a good description of how to prepare SSL Certificates, Keystore, and Truststore for both approaches. You can find the detail instructions for approach 1 here and for approach 2 here. Once this step is done, it becomes easy to configure Cassandra node-to-node and client-to-node encryption.
server_encryption_options:
internode_encryption: [none|all|dc]
keystore:
keystore_password:
truststore:
truststore_password:
# More advanced defaults below: protocol: TLS algorithm: SunX509 store_type: JKS cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] require_client_auth: true require_endpoint_verification: true
Most of these settings are straightforward to understand except 2 points: 1). If 2-Way SSL Authenticate is needed, "require_client_auth" needs to be set as true. 2). If a CA is used to build the trust chain, A type of Man in the Middle (MITM) attack can happen even with TLS/SSL communication (see here) for more background info. If the trust chain is built through simply importing all node certificates into each Truststore, this problem does not exist. In order to address this issue, Hostname Verification is needed. Cassandra version 3.6 introduces the support for it through CASSANDRA-9220. The "require_endpoint_verification" setting in node-to-node encryption options is used for this purpose.
# enable client/server encryption.
client_encryption_options:
enabled: true
# If enabled and optional is set to true encrypted and unencrypted connections are handled.
optional: false
keystore:
keystore_password:
require_client_auth: true
# Set trustore and truststore_password if require_client_auth is true
truststore:
truststore_password:
# More advanced defaults below: protocol: TLS algorithm: SunX509 store_type: JKS cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
Similarly, there are several points worthy a few more words: 1). When "require_client_auth" is set to true, 2-Way SSL Authentication is enabled and "truststoe" and "trustore_password" settings have to be set with the proper values. 2). When client-to-node encryption is enabled ("enabled" is set to true), "optional" setting determines whether or not unecrypted traffic is allowed along with encrypted traffic. "optional" being set to true allows both uncrypted and encrypted traffic. 3). Starting from Cassandra version 3.0, an additional dedicated port ("native_transport_port_ssl", default to 9142) is added in "cassandra.yaml" file for encrypted traffic. If it is not set, the original native transport port ("native_transport_port", default to 9042) is used for encrypted traffic. When both unecrypted and encrypted traffic are allowed ("optional" setting to true), "native_transport_port_ssl" will be used for encrypted traffic and "native_transport_port" will be used for unencrypted traffic. 4). The MITM problem for client-to-node communication is addressed in client drivers. For example, JAVA-841 is the solution in Java driver.
if [ "x$LOCAL_JMX" = "x" ]; then LOCAL_JMX=yes fi JMX_PORT="7100" if [ "$LOCAL_JMX" = "yes" ]; then .... .... else ... ... fiBy default, "LOCAL_JMX" system variable is "yes" and Cassandra JMX authentication is disabled via the following setting:
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false"In order to enable JMX Authentication/Authorization for Cassandra, the system variable "LOCAL_JMX" needs to change to "no" (or any value not equal to "yes"). This change makes sure JMX authentication is enabled:
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"After doing this, there are two different ways to achieve secure JMX access depending on the version of Cassandra:
// Specify password file JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.password.file= " // Specify access control file. // If this line is not included, the default JRE access control file is then verified JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.access.file= "The password file is used to define different roles and their passwords and the access control file is used to define the permitted access for each role. By default, JRE implementation includes a default password template file and a default access control file under folder "<$JRE_HOME>/lib/management/": jmxremote.password.template and jmxremote.access. In order to enable file-based Password Authentication for Cassandra, we can follow the steps below:
cp
/lib/management/jmxremote.password.template /etc/cassandra/jmxremote.password
cassandra
chown cassandra:cassandra /etc/cassandra/jmxremote.password chmod 400 /etc/cassandra/jmxremote.password
cassandra readwrite
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.password.file=
"
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.access.file=
"
JVM_OPTS="$JVM_OPTS -Dcassandra.jmx.remote.login.config=CassandraLogin" JVM_OPTS="$JVM_OPTS -Djava.security.auth.login.config=$CASSANDRA_HOME/conf/cassandra-jaas.config"By default, Cassandra (version 3.6 and later) ships with a JAAS login module, CassandraLoginModule, as specified in the JASS login module configuration file, "cassandra-jass.config". This module delegates the JMX authentication request to the authenticator as defined in "cassandra.yaml" file (e.g. PasswordAuthenticator)
// Delegates authentication to Cassandra configured IAuthenticator
CassandraLogin {
org.apache.cassandra.auth.CassandraLoginModule REQUIRED;
};
JVM_OPTS="$JVM_OPTS -Dcassandra.jmx.authorizer=org.apache.cassandra.auth.jmx.AuthorizationProxy"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=true" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.need.client.auth=true" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.enabled.protocols=" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.enabled.cipher.suites=" JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.keyStore=" JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.keyStorePassword=" JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.trustStore=" JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.trustStorePassword="
In this blog post, I went through the security features that are available in the latest Cassandra 3.x release (3.9 as the time of the writing). Compared with earlier releases (2.1 and before), Cassandra 3.x has gradually introduced many security improvements and new features, such as Role-based Access Control, At-rest Data Encryption, JMX Authentication Delegation, and etc. This blog post also aims to provide hands-on guidance on how these security features are configured in Cassandra 3.9, while providing enough underlying background information at the same time.
Ready to optimize your Cassandra Database for the future?