Cassandra Security Guide¶
A fresh Cassandra install has no security. Anyone who can reach port 9042 can read and write any data. There is no authentication, no authorization, no encryption. This is convenient for development but dangerous for anything else.
Cassandra has the expected security features—password, role-based access control, TLS encryption for client and internode traffic, audit logging—but they must be enabled. Each feature requires configuration changes and, for some, a rolling restart.
This guide covers enabling authentication, setting up roles and permissions, configuring encryption, and turning on audit logging.
Security Overview¶
Security Layers¶
| Layer | Scope |
|---|---|
| Application | Input validation, credential management, audit logging |
| Cassandra | Authentication, authorization, encryption |
| Network | Firewalls, VPNs, network isolation |
| Infrastructure | OS hardening, access controls, patching |
Security Components¶
| Component | Purpose | Default State |
|---|---|---|
| Authentication | Verify identity | Disabled |
| Authorization | Control access | Disabled |
| Client Encryption | Encrypt client traffic | Disabled |
| Internode Encryption | Encrypt cluster traffic | Disabled |
| Audit Logging | Track operations | Disabled |
Authentication¶
Enabling Authentication¶
# cassandra.yaml
# Enable password authentication
authenticator: PasswordAuthenticator
# Alternative authenticators:
# authenticator: AllowAllAuthenticator # No authentication (default)
# authenticator: com.example.LdapAuthenticator # Custom LDAP
Default Superuser¶
After enabling authentication, use the default superuser:
-- Default credentials (CHANGE IMMEDIATELY)
-- Username: cassandra
-- Password: cassandra
cqlsh -u cassandra -p cassandra
-- Create new superuser
CREATE ROLE admin WITH PASSWORD = 'strong_password_here'
AND SUPERUSER = true
AND LOGIN = true;
-- Disable default superuser
ALTER ROLE cassandra WITH SUPERUSER = false AND LOGIN = false;
Creating Users¶
-- Create application user
CREATE ROLE app_user WITH PASSWORD = 'app_password'
AND LOGIN = true;
-- Create read-only user
CREATE ROLE readonly_user WITH PASSWORD = 'readonly_pass'
AND LOGIN = true;
-- Create user with role inheritance
CREATE ROLE analyst WITH LOGIN = true AND PASSWORD = 'analyst_pass';
GRANT readonly_role TO analyst;
-- List all roles
LIST ROLES;
Authentication Cache¶
# cassandra.yaml
credentials_validity_in_ms: 2000
credentials_update_interval_in_ms: 2000
credentials_cache_max_entries: 1000
Authorization¶
Enabling Authorization¶
# cassandra.yaml
# Enable role-based authorization
authorizer: CassandraAuthorizer
# Role management
role_manager: CassandraRoleManager
Permission Types¶
| Permission | Applies To | Description |
|---|---|---|
ALL |
All | All permissions |
ALTER |
Keyspace, Table, Function | Modify schema |
AUTHORIZE |
All | Grant/revoke permissions |
CREATE |
Keyspace, Table, Function, Role | Create objects |
DESCRIBE |
All | DESCRIBE operations |
DROP |
Keyspace, Table, Function, Role | Delete objects |
EXECUTE |
Function | Execute functions |
MODIFY |
Keyspace, Table | INSERT, UPDATE, DELETE |
SELECT |
Keyspace, Table, MBean | Read data |
Granting Permissions¶
-- Grant full access to keyspace
GRANT ALL PERMISSIONS ON KEYSPACE my_keyspace TO app_user;
-- Grant read-only access
GRANT SELECT ON KEYSPACE my_keyspace TO readonly_user;
-- Grant access to specific table
GRANT SELECT, MODIFY ON TABLE my_keyspace.users TO app_user;
-- Grant permission to create keyspaces
GRANT CREATE ON ALL KEYSPACES TO admin_user;
-- List permissions
LIST ALL PERMISSIONS OF app_user;
LIST ALL PERMISSIONS ON KEYSPACE my_keyspace;
Role Hierarchy¶
-- Create role hierarchy
CREATE ROLE base_role;
CREATE ROLE extended_role;
-- Grant permissions to base role
GRANT SELECT ON KEYSPACE analytics TO base_role;
-- Grant additional permissions to extended role
GRANT MODIFY ON KEYSPACE analytics TO extended_role;
-- Extended role inherits from base
GRANT base_role TO extended_role;
-- Users can be assigned roles
GRANT extended_role TO analyst_user;
Resource Permissions¶
-- All keyspaces
GRANT CREATE ON ALL KEYSPACES TO developer;
-- Specific keyspace
GRANT ALL ON KEYSPACE production TO admin;
-- All tables in keyspace
GRANT SELECT ON ALL TABLES IN KEYSPACE analytics TO analyst;
-- Specific table
GRANT SELECT, MODIFY ON TABLE production.orders TO app_user;
-- Functions
GRANT EXECUTE ON FUNCTION my_keyspace.my_function(int, text) TO app_user;
GRANT EXECUTE ON ALL FUNCTIONS IN KEYSPACE my_keyspace TO app_user;
-- JMX MBeans (for management)
GRANT SELECT ON MBEAN 'org.apache.cassandra.db:*' TO monitoring_user;
Revoking Permissions¶
-- Revoke specific permission
REVOKE MODIFY ON KEYSPACE my_keyspace FROM app_user;
-- Revoke all permissions
REVOKE ALL PERMISSIONS ON KEYSPACE my_keyspace FROM app_user;
-- Remove role assignment
REVOKE admin_role FROM user;
Client-to-Node Encryption¶
Generate Certificates¶
#!/bin/bash
# generate_certs.sh
# Create CA key and certificate
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -days 3650 \
-out ca-cert.pem -subj "/CN=CassandraCA"
# For each node, create key and certificate
for NODE in node1 node2 node3; do
# Generate private key
openssl genrsa -out ${NODE}-key.pem 4096
# Generate certificate signing request
openssl req -new -key ${NODE}-key.pem -out ${NODE}.csr \
-subj "/CN=${NODE}"
# Sign certificate with CA
openssl x509 -req -in ${NODE}.csr -CA ca-cert.pem \
-CAkey ca-key.pem -CAcreateserial -out ${NODE}-cert.pem -days 365
# Create keystore (PKCS12)
openssl pkcs12 -export -in ${NODE}-cert.pem -inkey ${NODE}-key.pem \
-out ${NODE}-keystore.p12 -name ${NODE} -password pass:keystorepass
# Convert to JKS (if needed)
keytool -importkeystore -srckeystore ${NODE}-keystore.p12 \
-srcstoretype PKCS12 -srcstorepass keystorepass \
-destkeystore ${NODE}-keystore.jks -deststorepass keystorepass
done
# Create truststore with CA certificate
keytool -import -file ca-cert.pem -keystore truststore.jks \
-storepass truststorepass -noprompt -alias ca
Configure Client Encryption¶
# cassandra.yaml
client_encryption_options:
# Enable encryption
enabled: true
# Optional: Allow unencrypted connections
optional: false
# Keystore containing server certificate
keystore: /etc/cassandra/certs/node1-keystore.jks
keystore_password: keystorepass
# Truststore for client certificate validation
# Required if require_client_auth is true
truststore: /etc/cassandra/certs/truststore.jks
truststore_password: truststorepass
# Require client certificates (mutual TLS)
require_client_auth: false
# TLS protocol versions
protocol: TLS
accepted_protocols:
- TLSv1.2
- TLSv1.3
# Cipher suites (strong ciphers only)
cipher_suites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_AES_128_GCM_SHA256
Client Connection with SSL¶
# cqlsh with SSL
cqlsh --ssl node1.example.com
# With explicit certificate
cqlsh --ssl --ssl-certfile=/path/to/ca-cert.pem node1.example.com
# cqlshrc configuration
# ~/.cassandra/cqlshrc
[ssl]
certfile = /path/to/ca-cert.pem
validate = true
userkey = /path/to/client-key.pem
usercert = /path/to/client-cert.pem
// Java driver with SSL
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream("/path/to/truststore.jks"), "truststorepass".toCharArray());
tmf.init(ts);
sslContext.init(null, tmf.getTrustManagers(), null);
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node1.example.com", 9042))
.withSslContext(sslContext)
.build();
Internode Encryption¶
Configure Node-to-Node Encryption¶
# cassandra.yaml
server_encryption_options:
# Encryption mode:
# none: No encryption
# dc: Encrypt only cross-datacenter traffic
# rack: Encrypt only cross-rack traffic
# all: Encrypt all internode traffic
internode_encryption: all
# Keystore with node certificate
keystore: /etc/cassandra/certs/node1-keystore.jks
keystore_password: keystorepass
# Truststore with CA certificate
truststore: /etc/cassandra/certs/truststore.jks
truststore_password: truststorepass
# Require peer certificates
require_client_auth: true
# TLS settings
protocol: TLS
accepted_protocols:
- TLSv1.2
- TLSv1.3
cipher_suites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# Enable for legacy SSL port (optional)
enable_legacy_ssl_storage_port: false
Encryption Modes¶
| Mode | Same Rack | Cross-Rack | Cross-DC |
|---|---|---|---|
none |
Plain | Plain | Plain |
dc |
Plain | Plain | Encrypted |
rack |
Plain | Encrypted | Encrypted |
all |
Encrypted | Encrypted | Encrypted |
Network Security¶
Firewall Rules¶
# Required ports for Cassandra
# Client CQL port
iptables -A INPUT -p tcp --dport 9042 -s 10.0.0.0/8 -j ACCEPT
# Internode communication
iptables -A INPUT -p tcp --dport 7000 -s 10.0.0.0/8 -j ACCEPT
# SSL internode (if legacy enabled)
iptables -A INPUT -p tcp --dport 7001 -s 10.0.0.0/8 -j ACCEPT
# JMX (restrict to management network)
iptables -A INPUT -p tcp --dport 7199 -s 10.0.1.0/24 -j ACCEPT
# Drop all other Cassandra ports
iptables -A INPUT -p tcp --dport 9042 -j DROP
iptables -A INPUT -p tcp --dport 7000 -j DROP
iptables -A INPUT -p tcp --dport 7001 -j DROP
iptables -A INPUT -p tcp --dport 7199 -j DROP
Binding Interfaces¶
# cassandra.yaml
# Listen only on specific interface
listen_address: 10.0.0.1
# Or listen on all interfaces
# listen_address: 0.0.0.0
# listen_interface: eth0
# RPC (client) address
rpc_address: 10.0.0.1
# Broadcast addresses (for NAT/cloud)
broadcast_address: 10.0.0.1
broadcast_rpc_address: 10.0.0.1
JMX Security¶
# cassandra-env.sh
# Enable JMX authentication
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
# JMX access file
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.access.file=/etc/cassandra/jmxremote.access"
# Enable JMX SSL
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=true"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.registry.ssl=true"
JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.keyStore=/etc/cassandra/certs/node1-keystore.jks"
JVM_OPTS="$JVM_OPTS -Djavax.net.ssl.keyStorePassword=keystorepass"
# /etc/cassandra/jmxremote.access
admin readwrite
monitor readonly
# /etc/cassandra/jmxremote.password (chmod 400)
admin admin_password
monitor monitor_password
Audit Logging (Cassandra 4.0+)¶
Enable Audit Logging¶
# cassandra.yaml
audit_logging_options:
enabled: true
# Logger implementation
logger:
- class_name: BinAuditLogger
# Audit categories
included_categories: QUERY, DML, DDL, AUTH, ERROR
# Excluded categories
# excluded_categories:
# Include specific keyspaces (empty = all)
included_keyspaces:
- production
- sensitive_data
# Exclude system keyspaces
excluded_keyspaces:
- system
- system_schema
- system_auth
- system_distributed
# Include specific users (empty = all)
# included_users:
# Exclude service accounts
excluded_users:
- cassandra
- monitoring
Audit Categories¶
| Category | Description |
|---|---|
QUERY |
SELECT statements |
DML |
INSERT, UPDATE, DELETE |
DDL |
CREATE, ALTER, DROP |
DCL |
GRANT, REVOKE |
AUTH |
Login attempts |
ADMIN |
Administrative operations |
ERROR |
Failed operations |
PREPARE |
Prepared statements |
View Audit Logs¶
# Binary logs require auditlogviewer
auditlogviewer /var/log/cassandra/audit/
# Or configure file-based logger
# class_name: FileAuditLogger
# parameters:
# log_dir: /var/log/cassandra/audit
Security Best Practices¶
Production Checklist¶
Authentication & Authorization: - [ ] Enable PasswordAuthenticator - [ ] Enable CassandraAuthorizer - [ ] Create dedicated superuser - [ ] Disable default cassandra user - [ ] Create application-specific roles - [ ] Apply least-privilege permissions - [ ] Enable password complexity requirements
Encryption: - [ ] Enable client-to-node encryption - [ ] Enable internode encryption - [ ] Use TLS 1.2 or higher - [ ] Use strong cipher suites - [ ] Implement certificate rotation plan - [ ] Use mutual TLS where appropriate
Network: - [ ] Restrict access with firewalls - [ ] Bind to specific interfaces - [ ] Secure JMX access - [ ] Use VPN for cross-DC traffic - [ ] Disable unused ports
Monitoring & Audit: - [ ] Enable audit logging - [ ] Monitor authentication failures - [ ] Alert on permission changes - [ ] Regular security reviews
Common Mistakes¶
| Mistake | Risk | Solution |
|---|---|---|
| Default cassandra user | Full access for attackers | Disable after creating new superuser |
| No encryption | Data interception | Enable TLS for all connections |
| GRANT ALL to apps | Over-privileged access | Apply least-privilege |
| JMX open to network | Remote management access | Restrict with firewall + auth |
| Weak passwords | Credential attacks | Enforce complexity policies |
| No audit logging | No visibility into access | Enable audit logging |
Next Steps¶
- Configuration Reference - Cassandra configuration