Cluster Security: Auth & Encryption

Imagine a bustling corporate office with a wide-open front door, unlocked file cabinets, and employees freely walking out with sensitive documents. This scenario is a security nightmare, yet it reflects the state of many default database deployments. Security in Cassandra is often overlooked until it’s too late—a data breach or unauthorized modification can compromise the entire system.

To secure a production cluster effectively, you must establish defense-in-depth across three primary fronts:

  1. Authentication: Verifying who the user or application is before allowing connection.
  2. Authorization: Verifying what the authenticated user is permitted to do, typically handled via Role-Based Access Control (RBAC).
  3. Encryption: Securing data while it travels across the network (mTLS) to prevent interception.

1. Authorization (RBAC)

Cassandra uses RBAC. Instead of assigning permissions to users, you assign permissions to Roles, and users assume those Roles.

Interactive RBAC Policy Simulator

Define a Role’s permissions and test access against specific resources.

1. Configure Role Permissions

2. Test Access

Configure permissions and click Check...

2. Authentication

Cassandra supports pluggable authentication. The default AllowAllAuthenticator is insecure and should never be used in production.

First Principles: Performance Impact

Enabling authentication adds a small overhead (network roundtrip for login). However, Cassandra drivers use connection pooling. Authentication happens only when a connection is established, not on every query.

  • Connection Open: Auth Handshake (Slow)
  • Query: Auth Token/Session ID (Fast)

Enabling Password Auth

  1. Update cassandra.yaml:
    authenticator: PasswordAuthenticator
    authorizer: CassandraAuthorizer
    
  2. Restart the node.
  3. Login with default credentials (cassandra/cassandra).
  4. IMMEDIATELY change the default password and create new superusers.

3. Encryption (mTLS)

Encryption is critical for preventing man-in-the-middle attacks.

  • Client-to-Node: Encrypts traffic between your application and Cassandra.
  • Node-to-Node (Internode): Encrypts gossip and streaming traffic. Critical because data streaming (bootstrap/repair) moves raw SSTables across the wire.

mTLS (Mutual TLS)

In standard TLS, the client verifies the server. In mTLS, both parties verify each other using certificates signed by a trusted Certificate Authority (CA).

4. Secure Drivers (Code Examples)

Connecting to a secure cluster requires configuring SSL in your driver.

Java Driver (4.x)

import com.datastax.oss.driver.api.core.CqlSession;
import javax.net.ssl.SSLContext;
import java.net.InetSocketAddress;
import java.nio.file.Paths;

public class SecureConnect {
  public static void main(String[] args) {
    // Option 1: Using a Cloud Secure Connect Bundle (simplest for cloud)
    // try (CqlSession session = CqlSession.builder()
    //      .withCloudSecureConnectBundle(Paths.get("/path/to/bundle.zip"))
    //      .withAuthCredentials("user", "pass")
    //      .build()) { ... }

    // Option 2: Programmatic SSL Context (for self-managed)
    try {
      // Assuming you have a helper to load your KeyStore/TrustStore
      SSLContext sslContext = SSLContext.getDefault();

      try (CqlSession session = CqlSession.builder()
          .addContactPoint(new InetSocketAddress("10.0.0.1", 9042))
          .withAuthCredentials("admin", "super_secret_password")
          .withLocalDatacenter("dc1")
          .withSslContext(sslContext) // Enable SSL
          .build()) {

        System.out.println("Connected to: " + session.getName());
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Go Driver

package main

import (
  "crypto/tls"
  "crypto/x509"
  "io/ioutil"
  "log"
  "github.com/gocql/gocql"
)

func main() {
  cluster := gocql.NewCluster("10.0.0.1", "10.0.0.2")
  cluster.Keyspace = "my_keyspace"
  cluster.Consistency = gocql.Quorum

  // 1. Authentication
  cluster.Authenticator = gocql.PasswordAuthenticator{
    Username: "admin",
    Password: "super_secret_password",
  }

  // 2. Load CA Certificate (to verify the server)
  caCert, err := ioutil.ReadFile("/path/to/ca.crt")
  if err != nil {
    log.Fatal(err)
  }
  caCertPool := x509.NewCertPool()
  caCertPool.AppendCertsFromPEM(caCert)

  // 3. Load Client Certificate (for mTLS - so server verifies us)
  cert, err := tls.LoadX509KeyPair("/path/to/client.crt", "/path/to/client.key")
  if err != nil {
    log.Fatal(err)
  }

  // 4. Configure TLS
  cluster.SslOpts = &gocql.SslOptions{
    Config: &tls.Config{
      RootCAs: caCertPool,
      Certificates: []tls.Certificate{cert},
      // In production, InsecureSkipVerify MUST be false
      InsecureSkipVerify: false,
      ServerName: "cassandra.prod.local", // Must match Cert CN/SAN
    },
    EnableHostVerification: true,
  }

  session, err := cluster.CreateSession()
  if err != nil {
    log.Fatal(err)
  }
  defer session.Close()

  log.Println("Secure mTLS connection established.")
}

5. Diagram: Mutual TLS (mTLS) Handshake

Client App Has Client Cert Cassandra Node Has Server Cert CA 1. Client Hello 2. Server Certificate + Request Client Cert Verifies Server Cert 3. Client Certificate Verifies Client Cert 4. Encrypted Session Established

Figure 3: Mutual TLS requires both parties to prove their identity before data is exchanged.