Complete Security Guide

Security Best Practices

Lock down your Cassandra cluster - Authentication, Encryption, Hardening!

📖 The Story: Jake's $2M Data Breach

Jake deployed Cassandra to production with DEFAULT SETTINGS. No password, no encryption, port 9042 open to the internet. Three weeks later: 50 million customer records stolen, $2M fine from regulators, company reputation destroyed. All because he skipped security.

😱 What Went Wrong

Jake's "Setup":

-- Default Cassandra installation: authenticator: AllowAllAuthenticator ← NO PASSWORD! ❌ authorizer: AllowAllAuthorizer ← NO PERMISSIONS! ❌ encryption: none ← NO ENCRYPTION! ❌ -- Ports exposed to internet: 9042 CQL port ← WORLD ACCESSIBLE! ❌ 7199 JMX port ← NO AUTH! ❌ -- Result: $ cqlsh 54.123.45.67 ← No password needed! Connected! cqlsh> SELECT * FROM customers.personal_data; -- 50 million records exposed! 💥

The Attack:

  1. 🌐 Discovery: Shodan scan found open port 9042
  2. 🔓 Access: Connected with cqlsh (no password!)
  3. 📊 Enumeration: DESCRIBE KEYSPACES found customer data
  4. 💾 Exfiltration: Exported 50M records over 3 days
  5. 💰 Ransom: $500K Bitcoin demand
  6. 📰 Public: Media reported the breach
  7. ⚖️ Fine: $2M GDPR violation

The Damage:

  • 💰 Direct Cost: $2M regulatory fine
  • 💸 Response Cost: $500K incident response
  • 📉 Stock Price: 40% drop
  • 😡 Customers: 60% churn rate
  • 💼 Jake: Fired, career damaged

✅ What Should Have Been Done

Proper Security Setup (30 minutes of work!):

-- 1. Enable authentication authenticator: PasswordAuthenticator ← Require login ✅ -- 2. Enable authorization authorizer: CassandraAuthorizer ← Role-based access ✅ -- 3. Enable encryption client_encryption_options: enabled: true ← SSL/TLS ✅ -- 4. Firewall rules # Only allow specific IPs iptables -A INPUT -p tcp --dport 9042 -s 10.0.0.0/8 -j ACCEPT iptables -A INPUT -p tcp --dport 9042 -j DROP ← Block internet ✅ -- 5. Change default superuser password ALTER USER cassandra WITH PASSWORD 'Complex!Pass@2024'; -- Result: SECURE! ✅

Cost of Security:

  • ⏱️ Time: 30 minutes to configure
  • 💰 Money: $0 (free!)
  • 🛡️ Protection: Priceless
  • 😊 Sleep: Peaceful nights

30 minutes of security = Avoided $3M disaster! 🎉

🛡️ Defense in Depth - Security Layers

Multiple layers of protection!

🌐

Layer 1: Network

  • Firewall rules
  • VPC/Private networks
  • IP whitelisting
  • Port restrictions
  • VPN access only

First line of defense!

🔐

Layer 2: Authentication

  • PasswordAuthenticator
  • Strong passwords
  • Change defaults
  • Rotate credentials
  • LDAP integration

Who are you?

👮

Layer 3: Authorization

  • CassandraAuthorizer
  • Role-based access
  • Least privilege
  • Table-level permissions
  • Audit trails

What can you do?

🔒

Layer 4: Encryption

  • Client-to-node SSL
  • Node-to-node SSL
  • Encryption at rest
  • TLS 1.2+
  • Strong ciphers

Protect data in transit!

⚔️

Layer 5: Hardening

  • Disable JMX remote
  • Remove defaults
  • Minimal services
  • OS hardening
  • Security patches

Reduce attack surface!

📊

Layer 6: Monitoring

  • Audit logging
  • Failed login tracking
  • Suspicious queries
  • Alert on anomalies
  • SIEM integration

Detect attacks!

🔐 Authentication Setup

Require login - NO anonymous access!

1

Enable PasswordAuthenticator

Switch from AllowAll to Password-based

-- Edit cassandra.yaml: authenticator: PasswordAuthenticator -- Before (INSECURE!): # authenticator: AllowAllAuthenticator ← Anyone can connect! -- After (SECURE!): authenticator: PasswordAuthenticator ← Requires password ✅ -- Restart Cassandra: sudo systemctl restart cassandra
2

Change Default Superuser Password

CRITICAL: Default is cassandra/cassandra!

-- Login with default credentials (TEMPORARILY!): cqlsh -u cassandra -p cassandra -- Change password IMMEDIATELY: ALTER USER cassandra WITH PASSWORD 'YourComplex!Pass@2024'; -- Verify: exit cqlsh -u cassandra -p 'YourComplex!Pass@2024' -- Should connect ✅ -- Try old password: cqlsh -u cassandra -p cassandra -- Should FAIL ✅
3

Create Application Users

Never use superuser in applications!

-- Create read-only user: CREATE ROLE readonly_user WITH PASSWORD = 'ReadOnly!2024' AND LOGIN = true; -- Create application user: CREATE ROLE app_user WITH PASSWORD = 'AppUser!2024' AND LOGIN = true; -- Create admin user (not superuser): CREATE ROLE db_admin WITH PASSWORD = 'Admin!2024' AND LOGIN = true; -- List users: LIST ROLES; /* role | super | login | options ---------------+-------+-------+--------- cassandra | True | True | {} readonly_user| False | True | {} app_user | False | True | {} db_admin | False | True | {} */

Password Best Practices

  • 🔒 Minimum 12 characters
  • 🔤 Mix: Upper, lower, numbers, symbols
  • ❌ No dictionary words
  • 🔄 Rotate every 90 days
  • 🚫 Never commit to git!
  • 🗝️ Use password manager
  • 🎯 Different per environment

👮 Authorization & Role-Based Access

Control WHO can do WHAT!

1

Enable CassandraAuthorizer

-- Edit cassandra.yaml: authorizer: CassandraAuthorizer -- Before (INSECURE!): # authorizer: AllowAllAuthorizer ← Everyone can do everything! -- After (SECURE!): authorizer: CassandraAuthorizer ← Role-based permissions ✅ -- Restart Cassandra: sudo systemctl restart cassandra
2

Grant Permissions (Least Privilege)

Only give what's needed!

-- Read-only user (SELECT only): GRANT SELECT ON KEYSPACE my_keyspace TO readonly_user; -- Application user (SELECT + MODIFY): GRANT SELECT ON KEYSPACE my_keyspace TO app_user; GRANT MODIFY ON KEYSPACE my_keyspace TO app_user; -- Admin user (all except superuser): GRANT ALL PERMISSIONS ON KEYSPACE my_keyspace TO db_admin; GRANT CREATE ON ALL KEYSPACES TO db_admin; -- Table-specific permissions: GRANT SELECT ON my_keyspace.sensitive_table TO readonly_user; -- Only this table, not whole keyspace! -- Verify permissions: LIST ALL PERMISSIONS OF app_user; /* role | resource | permission ----------+-------------------+------------ app_user | | SELECT app_user | | MODIFY */

❌ BAD: Everyone is Superuser

-- Application connects as superuser: cqlsh -u cassandra -p cassandra -- Can do ANYTHING: DROP KEYSPACE production; ← Oops! 💥

✅ GOOD: Least Privilege

-- Application connects as app_user: cqlsh -u app_user -p 'AppUser!2024' -- Can only SELECT + INSERT/UPDATE/DELETE: SELECT * FROM my_keyspace.users; ← Works ✅ INSERT INTO my_keyspace.users... ← Works ✅ DROP KEYSPACE production; ← DENIED! ✅ /* Unauthorized: Error from server: User app_user has no DROP permission on */

Permission Types

Permission Allows Use Case
SELECT Read data Read-only users, reporting
MODIFY INSERT/UPDATE/DELETE Application users
CREATE Create tables/keyspaces DBAs, migrations
ALTER Modify schema DBAs
DROP Delete tables/keyspaces Senior DBAs only!
AUTHORIZE Grant/revoke permissions Security admins

🔒 Encryption - Protect Data in Transit

SSL/TLS for client and inter-node!

Why Encrypt?

Without Encryption

Anyone on the network can see your data!

-- Attacker runs Wireshark on same network: $ sudo tcpdump -i eth0 port 9042 -A -- Sees plaintext CQL queries: SELECT * FROM customers WHERE ssn='123-45-6789' -- Sees results in plaintext: John Doe, 123-45-6789, john@example.com -- COMPLETE DATA BREACH! 💥
1

Generate SSL Certificates

-- Create keystore directory: sudo mkdir -p /etc/cassandra/ssl cd /etc/cassandra/ssl -- Generate keystore (private key): keytool -genkeypair \ -alias cassandra \ -keyalg RSA \ -keysize 2048 \ -validity 365 \ -keystore cassandra.keystore \ -dname "CN=cassandra, OU=IT, O=MyCompany, L=City, ST=State, C=US" \ -storepass "keystorepass" \ -keypass "keypass" -- Export certificate: keytool -exportcert \ -alias cassandra \ -file cassandra.cer \ -keystore cassandra.keystore \ -storepass "keystorepass" -- Create truststore (trusted certificates): keytool -importcert \ -alias cassandra \ -file cassandra.cer \ -keystore cassandra.truststore \ -storepass "truststorepass" \ -noprompt -- Set permissions: sudo chown cassandra:cassandra cassandra.* sudo chmod 400 cassandra.*
2

Enable Client-to-Node Encryption

Encrypt traffic between apps and Cassandra

-- Edit cassandra.yaml: client_encryption_options: enabled: true optional: false ← Require SSL (no fallback to plaintext) keystore: /etc/cassandra/ssl/cassandra.keystore keystore_password: keystorepass require_client_auth: false ← Change to true for mutual TLS truststore: /etc/cassandra/ssl/cassandra.truststore truststore_password: truststorepass protocol: TLS algorithm: SunX509 store_type: JKS cipher_suites: [TLS_RSA_WITH_AES_256_CBC_SHA] -- Restart Cassandra: sudo systemctl restart cassandra -- Connect with SSL: cqlsh 127.0.0.1 9042 \ --ssl \ -u app_user \ -p 'AppUser!2024'
3

Enable Node-to-Node Encryption

Encrypt traffic between Cassandra nodes

-- Edit cassandra.yaml: server_encryption_options: internode_encryption: all ← Encrypt all inter-node traffic keystore: /etc/cassandra/ssl/cassandra.keystore keystore_password: keystorepass truststore: /etc/cassandra/ssl/cassandra.truststore truststore_password: truststorepass protocol: TLS algorithm: SunX509 store_type: JKS cipher_suites: [TLS_RSA_WITH_AES_256_CBC_SHA] require_client_auth: true ← Mutual authentication require_endpoint_verification: false -- Options for internode_encryption: # none: No encryption (INSECURE!) # dc: Encrypt only cross-datacenter # rack: Encrypt cross-rack (within DC) # all: Encrypt everything (RECOMMENDED!) ✅ -- Restart ALL nodes (rolling restart): # Node 1: sudo systemctl restart cassandra # Wait for UN # Node 2: sudo systemctl restart cassandra # Wait for UN # Node 3: sudo systemctl restart cassandra

Now Encrypted!

Attacker runs Wireshark again:

$ sudo tcpdump -i eth0 port 9042 -A -- All they see is gibberish: �3t5��y��2��o�8h��... -- Encrypted! Can't read anything! ✅

🌐 Network Security

Lock down network access!

❌

INSECURE

# All ports open to internet: 0.0.0.0/0 → 9042 ← CQL 0.0.0.0/0 → 7199 ← JMX 0.0.0.0/0 → 7000 ← Inter-node Anyone can connect! 💥
✅

SECURE

# Restricted to private network: 10.0.0.0/8 → 9042 ← CQL (apps only) 10.0.0.0/8 → 7000 ← Inter-node 127.0.0.1 → 7199 ← JMX (localhost) Internet blocked! ✅

Firewall Rules (iptables)

#################################### # CASSANDRA FIREWALL RULES #################################### # 1. Default policy: DROP everything iptables -P INPUT DROP iptables -P FORWARD DROP # 2. Allow localhost iptables -A INPUT -i lo -j ACCEPT # 3. Allow established connections iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 4. CQL port (9042) - Only from app servers iptables -A INPUT -p tcp --dport 9042 \ -s 10.0.1.0/24 \ ← App subnet -j ACCEPT # 5. Inter-node communication (7000) - Only from Cassandra nodes iptables -A INPUT -p tcp --dport 7000 \ -s 10.0.2.0/24 \ ← Cassandra subnet -j ACCEPT # 6. Inter-node SSL (7001) - Only from Cassandra nodes iptables -A INPUT -p tcp --dport 7001 \ -s 10.0.2.0/24 \ -j ACCEPT # 7. JMX (7199) - localhost ONLY iptables -A INPUT -p tcp --dport 7199 \ -s 127.0.0.1 \ -j ACCEPT # 8. SSH (22) - From jump host only iptables -A INPUT -p tcp --dport 22 \ -s 10.0.0.10 \ ← Jump host -j ACCEPT # 9. BLOCK everything else iptables -A INPUT -j DROP # 10. Save rules sudo iptables-save > /etc/iptables/rules.v4

Port Reference

Port Purpose Access
9042 CQL native transport App servers only
7000 Inter-node communication Cassandra nodes only
7001 Inter-node SSL Cassandra nodes only
7199 JMX monitoring Localhost only (or monitoring subnet)
9160 Thrift (deprecated) DISABLE if not using

⚔️ System Hardening

Reduce attack surface!

1. Disable Remote JMX

-- Edit cassandra-env.sh: # DISABLE remote JMX (use localhost only): JVM_OPTS="$JVM_OPTS -Djava.rmi.server.hostname=127.0.0.1" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.host=127.0.0.1" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.local.only=true" # If you MUST use remote JMX, enable authentication: JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true" JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password"

2. Remove/Disable Default Accounts

-- After creating your admin accounts: # Option 1: Change password to something unknown ALTER USER cassandra WITH PASSWORD '$(openssl rand -base64 32)'; # Option 2: Revoke superuser and disable ALTER ROLE cassandra WITH SUPERUSER = false AND LOGIN = false; # Create your own superuser: CREATE ROLE my_superuser WITH PASSWORD = 'SuperSecure!2024' AND SUPERUSER = true AND LOGIN = true;

3. Disable Thrift (if not using)

-- Edit cassandra.yaml: start_rpc: false ← Disable Thrift (legacy, port 9160) -- Thrift is DEPRECATED - only enable if legacy apps require it!

4. OS-Level Hardening

# 1. Keep system updated sudo apt update && sudo apt upgrade -y # 2. Disable unnecessary services sudo systemctl disable bluetooth sudo systemctl disable cups # 3. Configure sysctl for security # /etc/sysctl.conf: net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.icmp_echo_ignore_broadcasts = 1 # 4. File permissions sudo chmod 600 /etc/cassandra/cassandra.yaml sudo chmod 400 /etc/cassandra/ssl/* sudo chown cassandra:cassandra /var/lib/cassandra # 5. Disable password login for SSH (use keys) # /etc/ssh/sshd_config: PasswordAuthentication no PermitRootLogin no

5. Limit cassandra User Privileges

# Run Cassandra as non-root user # Create dedicated user: sudo useradd -r -s /bin/false cassandra # Set ownership: sudo chown -R cassandra:cassandra /var/lib/cassandra sudo chown -R cassandra:cassandra /var/log/cassandra sudo chown -R cassandra:cassandra /etc/cassandra # Start as cassandra user: sudo -u cassandra cassandra

📊 Security Monitoring & Audit Logging

Detect and respond to attacks!

Enable Audit Logging

-- Edit cassandra.yaml: audit_logging_options: enabled: true logger: - class_name: BinAuditLogger audit_logs_dir: /var/log/cassandra/audit roll_cycle: HOURLY max_queue_weight: 268435456 # 256 MiB max_log_size: 17179869184 # 16 GiB # Log all CQL operations: included_categories: QUERY,DML,DDL,DCL,AUTH # Exclude system queries (reduce noise): excluded_keyspaces: system,system_schema # Included/excluded users: # excluded_users: monitoring_user -- Restart: sudo systemctl restart cassandra -- View audit logs: tail -f /var/log/cassandra/audit/BinLog-*.log

Monitor Failed Logins

-- Watch for authentication failures: tail -f /var/log/cassandra/system.log | grep "AuthenticationException" -- Example failed login: WARN [Native-Transport-Requests-1] 2024-01-15 10:23:45,123 Message.java:623 - Unexpected exception during request org.apache.cassandra.exceptions.AuthenticationException: Username and/or password are incorrect -- Alert on multiple failures: # Create monitoring script: #!/bin/bash FAILURES=$(grep "AuthenticationException" /var/log/cassandra/system.log | \ grep "`date +%Y-%m-%d`" | wc -l) if [ $FAILURES -gt 10 ]; then echo "ALERT: $FAILURES failed login attempts today!" # Send to SIEM/alerting system fi

Monitor Suspicious Queries

-- Alert on dangerous operations: # 1. Full table scans (SELECT without WHERE) grep "SELECT.*FROM.*users" /var/log/cassandra/audit/*.log | \ grep -v "WHERE" # 2. DROP statements grep "DROP" /var/log/cassandra/audit/*.log # 3. Bulk exports (large result sets) grep "LIMIT 100000" /var/log/cassandra/audit/*.log # 4. Schema changes in production grep -E "ALTER|CREATE|DROP" /var/log/cassandra/audit/*.log | \ grep -v "system" # 5. Access to sensitive tables grep "credit_cards\|ssn\|passwords" /var/log/cassandra/audit/*.log

Set Up Alerts

Create alerts for:

  • 🚨 More than 5 failed logins from same IP
  • 🚨 DROP/ALTER statements from non-admin users
  • 🚨 Access from unexpected IP addresses
  • 🚨 Large data exports (millions of rows)
  • 🚨 Access during off-hours
  • 🚨 JMX connections (if should be disabled)

✅ Complete Security Checklist

Production readiness checklist!

🔐

Authentication

  • ☐ PasswordAuthenticator enabled
  • ☐ Default cassandra password changed
  • ☐ Application users created
  • ☐ Strong passwords (12+ chars)
  • ☐ Password rotation policy
  • ☐ LDAP/AD integration (if applicable)
👮

Authorization

  • ☐ CassandraAuthorizer enabled
  • ☐ Least privilege roles
  • ☐ Table-level permissions
  • ☐ No applications use superuser
  • ☐ Regular permission audits
  • ☐ Default cassandra role disabled
🔒

Encryption

  • ☐ Client-to-node SSL enabled
  • ☐ Node-to-node SSL enabled
  • ☐ TLS 1.2+ only
  • ☐ Strong cipher suites
  • ☐ Certificate rotation plan
  • ☐ Encryption at rest (if required)
🌐

Network

  • ☐ Firewall rules configured
  • ☐ Private network/VPC
  • ☐ No internet exposure
  • ☐ Port 9042 restricted
  • ☐ JMX localhost only
  • ☐ SSH from jump host only
⚔️

Hardening

  • ☐ JMX remote disabled
  • ☐ Thrift disabled
  • ☐ File permissions set
  • ☐ Run as non-root
  • ☐ OS security patches
  • ☐ Unnecessary services disabled
📊

Monitoring

  • ☐ Audit logging enabled
  • ☐ Failed login alerts
  • ☐ Suspicious query detection
  • ☐ SIEM integration
  • ☐ Regular log review
  • ☐ Incident response plan

Pre-Production Security Verification

Before going to production, verify:

# 1. Authentication required cqlsh your-server ← Should FAIL without password ✅ # 2. Encryption enabled openssl s_client -connect your-server:9042 ← Should show SSL ✅ # 3. Ports blocked from internet nmap -p 9042,7199 your-public-ip ← Should show filtered ✅ # 4. No default passwords cqlsh -u cassandra -p cassandra ← Should FAIL ✅ # 5. JMX not remotely accessible telnet your-server 7199 ← Should timeout ✅ # 6. Audit logging working ls -la /var/log/cassandra/audit/ ← Should have recent logs ✅ # 7. Permissions enforced # Login as app_user: DROP KEYSPACE system; ← Should be DENIED ✅

🎉 Your Cassandra Cluster is Now SECURE!

You've learned comprehensive Cassandra security!

🎓 What You Accomplished:

  • 🔐 Authentication: Password required, strong passwords
  • 👮 Authorization: Role-based access, least privilege
  • 🔒 Encryption: SSL/TLS for client and inter-node
  • 🌐 Network: Firewall rules, private network
  • ⚔️ Hardening: JMX disabled, OS hardened
  • 📊 Monitoring: Audit logs, alert on attacks

💡 Key Takeaways:

  1. Security is NOT optional - One breach = millions in fines
  2. Defense in depth - Multiple layers catch what one misses
  3. Default is insecure - ALWAYS configure before production
  4. 30 minutes of setup - Can save $2M+ in damages
  5. Monitor everything - Detect attacks before damage
  6. Least privilege - Only grant what's needed

📋 Quick Security Setup (30 Minutes):

# 1. Enable authentication (cassandra.yaml) authenticator: PasswordAuthenticator # 2. Enable authorization (cassandra.yaml) authorizer: CassandraAuthorizer # 3. Change default password ALTER USER cassandra WITH PASSWORD 'Complex!Pass@2024'; # 4. Enable encryption (cassandra.yaml) client_encryption_options: enabled: true # 5. Firewall rules iptables -A INPUT -p tcp --dport 9042 -s 10.0.0.0/8 -j ACCEPT iptables -A INPUT -p tcp --dport 9042 -j DROP # 6. Enable audit logging (cassandra.yaml) audit_logging_options: enabled: true # DONE! Cluster is secure! 🎉

🚨 Red Flags to Watch:

  • ❌ "We'll add security later" → NO! Add it NOW!
  • ❌ "It's only dev environment" → Dev gets hacked too!
  • ❌ "No one knows about it" → Security by obscurity fails!
  • ❌ "Too complicated" → 30 minutes vs $2M fine!
  • ❌ "Performance impact" → Negligible vs data breach!

✅ Security Verification Commands:

# Test 1: Can't connect without password $ cqlsh 192.168.1.10 # Should fail: "Provided username cassandra and/or password are incorrect" ✅ # Test 2: Encryption enabled $ openssl s_client -connect 192.168.1.10:9042 # Should show SSL certificate ✅ # Test 3: Ports not exposed to internet $ nmap -p 9042,7199 your-public-ip # Should show: "filtered" or "closed" ✅ # Test 4: Authorization works $ cqlsh -u app_user -p 'AppUser!2024' 192.168.1.10 cqlsh> DROP KEYSPACE production; # Should fail: "Unauthorized" ✅ # Test 5: Audit logging active $ ls -lh /var/log/cassandra/audit/ # Should show recent log files ✅

🏆 Production Security Score

Calculate your score (out of 100):

  • 🔐 Authentication enabled: +20 points
  • 👮 Authorization enabled: +20 points
  • 🔒 Client encryption: +15 points
  • 🔒 Node-to-node encryption: +10 points
  • 🌐 Firewall configured: +15 points
  • ⚔️ System hardened: +10 points
  • 📊 Audit logging: +10 points

Your target: 100/100 before production! ✅

80-100: Excellent! Production-ready 🎉
60-79: Good, but add more layers ⚠️
40-59: Vulnerable, fix critical gaps ❌
0-39: DANGER! Do not go to production! 💥

🛡️ Remember Jake's $2M breach - Don't be Jake!
30 minutes of security = Years of peace! 🎯

Advertisement

📱 Responsive Ad 📱