Apache Cassandra is often deployed in clustered environments where network trust assumptions are incorrect by default. This page focuses on practical hardening for Debian 11+ and RHEL 8+ with emphasis on Cassandra 5.0+ security features.
Common ports to control:
7000/7001 internode communication (TCP)7199 JMX monitoring9042 CQL native transport9160 Thrift (legacy, disable if unused)# Allow only internal network access
sudo ufw allow from 10.0.0.0/8 to any port 7000,7001,7199,9042 proto tcp
sudo ufw reload
# Create a zone for Cassandra
sudo firewall-cmd --permanent --new-zone=cassandra
sudo firewall-cmd --permanent --zone=cassandra --add-source=10.0.0.0/8
sudo firewall-cmd --permanent --zone=cassandra --add-port=7000/tcp
sudo firewall-cmd --permanent --zone=cassandra --add-port=7001/tcp
sudo firewall-cmd --permanent --zone=cassandra --add-port=7199/tcp
sudo firewall-cmd --permanent --zone=cassandra --add-port=9042/tcp
sudo firewall-cmd --reload
In cassandra.yaml:
authenticator: PasswordAuthenticator
authorizer: CassandraAuthorizer
role_manager: CassandraRoleManager
After enabling auth:
cassandra/cassandra credentials immediately.GRANT only for required keyspaces/tables.-- Create superuser (replace with strong password)
CREATE ROLE admin_user WITH PASSWORD = 'strong-admin-password' AND SUPERUSER = true AND LOGIN = true;
-- Create application user
CREATE ROLE app_user WITH PASSWORD = 'strong-app-password' AND LOGIN = true;
-- Grant minimal required permissions
GRANT ALL PERMISSIONS ON KEYSPACE myapp TO app_user;
Enable encryption for both client traffic and internode traffic.
client_encryption_options:
enabled: true
optional: false # Require encryption for all clients
keystore: /etc/cassandra/ssl/node.keystore
keystore_password: change-me
require_client_auth: false # Set to true if mutual TLS is required
# truststore: /etc/cassandra/ssl/node.truststore # Required if require_client_auth is true
# truststore_password: change-me
server_encryption_options:
internode_encryption: all # Encrypt all internode communication
keystore: /etc/cassandra/ssl/node.keystore
keystore_password: change-me
# require_client_auth: false
# truststore: /etc/cassandra/ssl/node.truststore
# truststore_password: change-me
# Generate keystore
keytool -genkey -keyalg RSA -alias cassandra -keystore node.keystore -storepass change-me -keypass change-me -validity 365 -dname "CN=cassandra.example.com, OU=IT, O=Organization, L=City, S=State, C=US"
# Export certificate
keytool -export -alias cassandra -keystore node.keystore -rfc -file cassandra.crt
# Import to truststore (for mutual TLS)
keytool -import -v -trustcacerts -alias cassandra -file cassandra.crt -keystore node.truststore -keypass change-me -storepass change-me
Operational notes:
JMX is a common attack surface.
/etc/cassandra/jvm.options:# JMX settings
-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=7199
-Dcom.sun.management.jmxremote.rmi.port=7199
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/etc/cassandra/jmxremote.access
-Djava.rmi.server.hostname=127.0.0.1 # Bind to localhost only
# /etc/cassandra/jmxremote.access
monitorRole readonly
controlRole readwrite
# /etc/cassandra/jmxremote.password
monitorRole monitor123
controlRole control456
Secure the password file:
sudo chown cassandra:cassandra /etc/cassandra/jmxremote.password
sudo chmod 400 /etc/cassandra/jmxremote.password
/etc/cassandra and key material.# /etc/systemd/system/cassandra.service.d/hardening.conf
[Service]
# Disable core dumps
DisableCoreDumps=true
# Restrict system calls
SystemCallFilter=@system-service
# Restrict capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID
# Restrict file system access
ReadOnlyPaths=/usr
ReadWritePaths=/var/lib/cassandra /tmp /var/tmp /dev/shm
NoNewPrivileges=true
# Network isolation
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
</file>
Apply the changes:
```bash
sudo systemctl daemon-reload
sudo systemctl restart cassandra
New in Cassandra 5.0, Dynamic Data Masking (DDM) provides an additional layer of security for sensitive data:
# In cassandra.yaml
dynamic_data_masking_enabled: true
-- Create table with masked columns
CREATE TABLE customer_sensitive (
id UUID PRIMARY KEY,
name TEXT MASKED WITH mask_inner(1, '*'),
email TEXT MASKED WITH mask_default(),
phone TEXT MASKED WITH mask_replace('XXX-XXX-XXXX')
);
-- Grant UNMASK permission to authorized users only
GRANT UNMASK ON COLUMN customer_sensitive.name TO admin_user;
cassandra.yaml:audit_logging_options:
enabled: true
logger: SLF4JLogger
included_keyspaces: "mykeyspace"
excluded_keyspaces: ""
included_categories: "AUTH,DDL,DML,ADMIN"
excluded_categories: "QUERY_TRUNCATED,MISCELLANEOUS"
included_users: ""
excluded_users: "cassandra"
roll_cycle: "TESTHOURLY" # For production use: "DAILY" or "HOURLY"
max_queue_weight: 26214400 # 25MB
max_log_files_size: 1073741824 # 1GB
archive_command: ""
max_archive_retries: 10