Cassandra Encryption Configuration
This section covers the complete encryption configuration options in cassandra.yaml for both internode and client-to-node encryption.
Configuration Overview
Section titled “Configuration Overview”Cassandra provides two separate encryption configurations:
| Section | Purpose | Port |
|---|---|---|
server_encryption_options | Node-to-node communication | 7000 (7001 legacy SSL) |
client_encryption_options | Client-to-node communication | 9042 |
Internode Encryption
Section titled “Internode Encryption”Full Configuration Reference
Section titled “Full Configuration Reference”server_encryption_options: # Encryption scope # Options: none, dc, rack, all internode_encryption: all
# Enable legacy SSL storage port (7001) # Set to true during migration from unencrypted # Named enable_legacy_ssl_storage_port in Cassandra 4.0legacy_ssl_storage_port_enabled: false
# Keystore containing node's private key and certificate keystore: /etc/cassandra/certs/node-keystore.jks keystore_password: changeit
# Truststore containing CA certificates truststore: /etc/cassandra/certs/truststore.jks truststore_password: changeit
# Require client certificate for internode connections require_client_auth: true
# Hostname verification (Cassandra 4.0+) require_endpoint_verification: true
# TLS protocol version protocol: TLS
# Accepted protocol versions accepted_protocols: - TLSv1.2 - TLSv1.3
# Cipher suites (in preference order) 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
# Algorithm for secure random number generation # algorithm: SunX509
# Keystore type (JKS, PKCS12, or PEM for Cassandra 4.0+) # store_type: JKS
# SSL context factory (advanced) # ssl_context_factory: # class_name: org.apache.cassandra.security.DefaultSslContextFactoryEncryption Modes
Section titled “Encryption Modes”| Mode | Description | Use Case |
|---|---|---|
none | No encryption | Development only |
dc | Encrypt cross-datacenter only | WAN traffic encryption |
rack | Encrypt cross-rack and cross-datacenter | Physical rack isolation |
all | Encrypt all internode traffic | Recommended for production |
Migration to Encrypted Internode
Section titled “Migration to Encrypted Internode”Rolling migration from unencrypted to encrypted internode communication:
Phase 1: Enable Optional Encryption
# On all nodesserver_encryption_options: internode_encryption: all legacy_ssl_storage_port_enabled: true optional: true # Accept both encrypted and unencryptedPerform rolling restart.
Phase 2: Require Encryption
# On all nodesserver_encryption_options: internode_encryption: all legacy_ssl_storage_port_enabled: false optional: falsePerform rolling restart.
Unauthenticated Ring Joining
Section titled “Unauthenticated Ring Joining”The default value of internode_authenticator is AllowAllInternodeAuthenticator, which accepts every peer connection. In Cassandra 4.0, 4.1 and 5.0, a cluster left on this default accepts a bootstrapping node from any host that can reach the storage port, is configured with the same cluster_name, and has a reachable seed. The joining node claims token ranges and the existing replicas stream it a copy of the data for those ranges. No credential is presented at any point in that exchange.
Client authentication does not authenticate peers
PasswordAuthenticator authenticates client sessions on the native protocol (port 9042) and says nothing about peers connecting on the storage protocol (port 7000). A cluster with client authentication enabled and internode_authenticator left at its default still accepts an anonymous joining node.
Write survey mode extends the exposure further. A node started with -Dcassandra.write_survey=true streams the full data for the ranges it claims, then receives every subsequent write for those ranges while never becoming a read replica and never completing the join, described under write survey mode. The result is a continuously updated copy of the data on a host that never has to serve a request. Such a node is not hidden: peers report it as UJ in nodetool status, and it holds that state for as long as it keeps gossiping, which reads as a stalled bootstrap rather than as an intrusion.
Three controls apply, strongest first:
require_client_auth: trueinserver_encryption_options, withinternode_encryption: all. A joining node must then present a certificate signed by a CA in the truststore of the nodes it connects to. The option is available in 4.0, 4.1 and 5.0 and defaults tofalse.internode_encryption: dcandrackleave connections within the datacenter or rack unencrypted, and a certificate is only checked during a TLS handshake, so those modes leave intra-scope joins unauthenticated.allis required for the check to apply to every connection.MutualTlsInternodeAuthenticator(Cassandra 5.0+, CEP-34, CASSANDRA-18554), which authorizes peers by the identity in the certificate rather than by CA trust alone. It is configured in the subsection below.- Restricting the storage port, 7000, or 7001 when the legacy SSL storage port is enabled (
legacy_ssl_storage_port_enabledin 4.1 and 5.0,enable_legacy_ssl_storage_portin 4.0), to known cluster members at the network layer. This is defense in depth rather than the primary control, because any host inside the permitted range is still accepted without a certificate.
Internode Authentication with Mutual TLS (Cassandra 5.0+)
Section titled “Internode Authentication with Mutual TLS (Cassandra 5.0+)”The MutualTlsInternodeAuthenticator provides certificate-based authentication for internode connections by extracting identities from certificates and verifying them against a list of authorized peers.
internode_authenticator: class_name: org.apache.cassandra.auth.MutualTlsInternodeAuthenticator parameters: validator_class_name: org.apache.cassandra.auth.SpiffeCertificateValidator # Optional: restrict to specific peer identities trusted_peer_identities: "spiffe://testdomain.com/cassandra/node1,spiffe://testdomain.com/cassandra/node2" # Optional: validate outbound keystore identity node_identity: "spiffe://testdomain.com/cassandra/this-node"| Parameter | Required | Description |
|---|---|---|
validator_class_name | Yes | Certificate validator implementation class |
trusted_peer_identities | No | Comma-separated list of authorized peer identities |
node_identity | No | Expected identity from outbound keystore for validation |
Prerequisites:
server_encryption_options.internode_encryptionmust be set toallserver_encryption_options.require_client_authmust betrue
When trusted_peer_identities is configured, only nodes presenting certificates with matching identities can establish internode connections. When omitted, any certificate signed by a trusted CA is accepted.
The node_identity parameter validates the local node's keystore identity at startup, catching configuration errors early.
Client Encryption
Section titled “Client Encryption”Full Configuration Reference
Section titled “Full Configuration Reference”client_encryption_options: # Enable client encryption enabled: true
# Accept unencrypted connections alongside encrypted # Set to false in production optional: false
# Keystore containing node's private key and certificate keystore: /etc/cassandra/certs/node-keystore.jks keystore_password: changeit
# Truststore for validating client certificates # Required when require_client_auth is true truststore: /etc/cassandra/certs/truststore.jks truststore_password: changeit
# Require client certificates (mutual TLS) require_client_auth: false
# Hostname verification (Cassandra 4.0+) require_endpoint_verification: false
# TLS protocol version protocol: TLS
# Accepted protocol versions accepted_protocols: - TLSv1.2 - TLSv1.3
# Cipher suites 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_SHA256Mutual TLS (mTLS)
Section titled “Mutual TLS (mTLS)”Mutual TLS requires clients to present certificates, providing two-way authentication.
client_encryption_options: enabled: true require_client_auth: true truststore: /etc/cassandra/certs/client-truststore.jks truststore_password: changeitWith mTLS enabled:
- Clients must present valid certificates
- Certificates must be signed by a CA in the truststore
- Connection fails if client certificate is invalid or missing
PEM File Configuration (Cassandra 4.0+)
Section titled “PEM File Configuration (Cassandra 4.0+)”Cassandra 4.0+ supports PEM files directly, eliminating JKS/PKCS12 conversion.
Server Encryption with PEM
Section titled “Server Encryption with PEM”server_encryption_options: internode_encryption: all keystore: /etc/cassandra/certs/node-key.pem keystore_password: "" truststore: /etc/cassandra/certs/ca-chain.pem truststore_password: ""Client Encryption with PEM
Section titled “Client Encryption with PEM”client_encryption_options: enabled: true keystore: /etc/cassandra/certs/node-key.pem keystore_password: "" truststore: /etc/cassandra/certs/ca-cert.pem truststore_password: ""PEM File Format
Section titled “PEM File Format”Keystore PEM should contain private key followed by certificate chain:
-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY----------BEGIN CERTIFICATE-----... (node certificate)-----END CERTIFICATE----------BEGIN CERTIFICATE-----... (intermediate CA, if applicable)-----END CERTIFICATE-----Truststore PEM should contain CA certificates:
-----BEGIN CERTIFICATE-----... (root CA)-----END CERTIFICATE----------BEGIN CERTIFICATE-----... (intermediate CA, if applicable)-----END CERTIFICATE-----Hot Reloading (Cassandra 4.0+)
Section titled “Hot Reloading (Cassandra 4.0+)”Cassandra 4.0+ can reload certificates without restart when files change.
Requirements
Section titled “Requirements”- Keystore and truststore file paths remain the same
- New certificates must be valid
- File modification triggers reload
Monitoring Reload
Section titled “Monitoring Reload”Check logs for reload messages:
INFO [main] SSLFactory.java:... - Loaded renewed keystoreINFO [main] SSLFactory.java:... - Loaded renewed truststoreForcing Reload via JMX
Section titled “Forcing Reload via JMX”# Force SSL context reloadnodetool reloadsslConfiguration Validation
Section titled “Configuration Validation”Verify Configuration Before Restart
Section titled “Verify Configuration Before Restart”# Check cassandra.yaml syntaxpython3 -c "import yaml; yaml.safe_load(open('/etc/cassandra/cassandra.yaml'))"
# Verify keystore is readablekeytool -list -keystore /etc/cassandra/certs/node-keystore.jks -storepass changeit
# Verify truststore is readablekeytool -list -keystore /etc/cassandra/certs/truststore.jks -storepass changeit
# Verify certificate validityopenssl x509 -in /etc/cassandra/certs/node-cert.pem -noout -datesTest TLS Connection
Section titled “Test TLS Connection”After restart, verify TLS is working:
# Test client portopenssl s_client -connect localhost:9042 -tls1_2
# Test internode portopenssl s_client -connect localhost:7000 -tls1_2
# Check negotiated cipheropenssl s_client -connect localhost:9042 2>/dev/null | grep "Cipher is"Common Configurations
Section titled “Common Configurations”Development (Self-Signed)
Section titled “Development (Self-Signed)”server_encryption_options: internode_encryption: none
client_encryption_options: enabled: falseProduction (Full Encryption)
Section titled “Production (Full Encryption)”server_encryption_options: internode_encryption: all keystore: /etc/cassandra/certs/node-keystore.p12 keystore_password: ${KEYSTORE_PASSWORD} truststore: /etc/cassandra/certs/truststore.p12 truststore_password: ${TRUSTSTORE_PASSWORD} require_client_auth: true require_endpoint_verification: true accepted_protocols: - TLSv1.2 - TLSv1.3 cipher_suites: - TLS_AES_256_GCM_SHA384 - TLS_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
client_encryption_options: enabled: true optional: false keystore: /etc/cassandra/certs/node-keystore.p12 keystore_password: ${KEYSTORE_PASSWORD} truststore: /etc/cassandra/certs/truststore.p12 truststore_password: ${TRUSTSTORE_PASSWORD} require_client_auth: false accepted_protocols: - TLSv1.2 - TLSv1.3 cipher_suites: - TLS_AES_256_GCM_SHA384 - TLS_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256Mutual TLS (Zero Trust)
Section titled “Mutual TLS (Zero Trust)”client_encryption_options: enabled: true optional: false require_client_auth: true keystore: /etc/cassandra/certs/node-keystore.p12 keystore_password: ${KEYSTORE_PASSWORD} truststore: /etc/cassandra/certs/client-ca-truststore.p12 truststore_password: ${TRUSTSTORE_PASSWORD}Related Documentation
Section titled “Related Documentation”- Encryption Overview - Why encryption is essential
- Certificate Types - Generating certificates
- Client Configuration - Configuring client applications
- Troubleshooting - Common issues