Apache Cassandra 5.0.9
Overview
Apache Cassandra is a distributed, wide-column NoSQL database built for high write throughput and linear scale-out. It partitions data across nodes by a partition key, every node can serve reads and writes, and you configure replication per keyspace. You query it in CQL (Cassandra Query Language), which looks like SQL but models tables around the queries you run. Cassandra 5.0 adds storage-attached indexes (SAI), vector search, trie-based memtables and SSTables, and runs on Java 17.
This image installs the official Apache Cassandra 5.0.9 Debian package from the Apache Cassandra repository (verified by SHA-256 at build time) on Ubuntu's OpenJDK 17, and runs it as a native systemd service. This guide covers launching and verifying it on Amazon EC2.
The image is one version of the SecureAnyCloud Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The listing's guide hub describes the other databases in the listing.
What you get:
- Apache Cassandra 5.0.9 (
cassandra.service): CQL on127.0.0.1:9042(TLS required), inter-node storage on127.0.0.1:7000, JMX on127.0.0.1:7199 - A single-node cluster named
sac-cassandra, with its data under/var/lib/cassandra - The superuser role
adminwith a password unique to this instance, and a TLS certificate generated on the instance's first boot cqlsh(CQL shell) andnodetool, withsudo cqlshpreconfigured to connect over TLS asadmin
Default user: ubuntu. Architecture: x86_64. Root volume: 20 GiB gp3.
Security posture
Upstream Cassandra starts with authentication turned off (AllowAllAuthenticator). When you switch password authentication on, it creates the well-known superuser cassandra with the password cassandra. This image changes that:
- Authentication and authorization on. Cassandra runs with
PasswordAuthenticatorandCassandraAuthorizer. Every client must log in, and roles get only the permissions you grant them. - No default superuser. First boot creates the superuser role
adminwith a random password unique to this instance. It then drops thecassandrarole and writes the password to/root/cassandra-credentials.txt, readable by root only. This happens with Cassandra running in an isolated network namespace, so nothing else can reach the default login.cassandra.servicedoes not start until first boot has proved thatcassandra/cassandrais refused. - TLS required on the CQL port. First boot creates this instance's own RSA-3072 key and self-signed certificate. Cassandra refuses any client that does not use TLS on port 9042. No key ships in the image, so no two instances share one.
- Localhost only. CQL, inter-node traffic and JMX listen on
127.0.0.1, and the listing's security group opens SSH (22) only. Reach the database through an SSH tunnel (Step 7), or open it to your VPC as described there. - JMX stays local. JMX (used by
nodetool) keeps Cassandra's local-only default. It is bound to127.0.0.1:7199, so only processes on the instance can reach it. - No metadata access for the database. The Cassandra server and its first-boot service are blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). Amazon Time Sync (169.254.169.123) still works.
- Sandboxed service. Cassandra runs as the unprivileged
cassandrauser with no Linux capabilities andNoNewPrivileges. It has a read-only view of the operating system (ProtectSystem=strict, writable only under/var/lib/cassandraand/var/log/cassandra), a private/tmpand no device access. - SSH keys only. The SSH server accepts key logins only. Password and keyboard-interactive logins, empty passwords, direct
rootlogin and X11 forwarding are all switched off (pinned in/etc/ssh/sshd_config.d/00-sac.conf), androothas no password. The firmware metadata refresh (fwupd-refresh) is masked, because EC2 instances have no firmware it can update. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - Fails closed. If first boot cannot finish, Cassandra stays stopped. First boot tries again on the next boot, from a fresh, empty node.
- No secrets in logs. Passwords are never passed on a command line (cqlsh reads them from root-only credentials files), so they do not appear in
/var/log/auth.logor the journal. - Per-instance identity. SSH host keys, the machine ID, the node's host ID and tokens are created fresh on every instance. The image ships no data.
Prerequisites
- An AWS account and permission to launch EC2 instances and create security groups
- An EC2 key pair in the Region you launch in
- A VPC subnet, with a public IP on the instance if you connect over the internet
- For remote access,
cqlshor a Cassandra driver (Java, Python, Go, Node.js ...) on your workstation or application server
Recommended instance type: m5.large. Cassandra sizes its Java heap from the instance's memory (about 2 GB on m5.large). For production data sets, choose a larger memory size and dedicated EBS storage.
Connecting to your instance
Version Operating system SSH user
Apache Cassandra 5.0.9 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace, choose View purchase options, then choose Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Apache Cassandra 5.0.9 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largeis recommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: the listing's default group allows SSH (22) only. Restrict it to your own IP address. Do not open 9042, 7000 or 7199 to the internet.
- Choose your VPC and subnet. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run these commands on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR with your own:
SG=$(aws ec2 create-security-group --group-name cassandra \
--description "Apache Cassandra" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=cassandra}]'You do not need --metadata-options, because the AMI already requires IMDSv2.
Connect and read your credentials
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within about two minutes of launch. It creates this instance's TLS key and certificate, creates the admin superuser, drops the default cassandra role, checks that the old login is refused, and then writes the credentials file.
Show the connection details. The command leaves out the password line. Read the password with sudo cat /root/cassandra-credentials.txt and keep it in your secrets manager:
sudo grep -v '^CASSANDRA_PASSWORD=' /root/cassandra-credentials.txt# Apache Cassandra 5.0.9 - credentials for THIS instance (generated at first boot; not shared with any other instance)
#
# CQL listens on 127.0.0.1:9042 with TLS required (loopback only; the security group opens SSH only).
# The default cassandra/cassandra superuser does not exist on this instance; log in as the admin role.
# On the instance:
sudo cqlsh (TLS + these credentials are preconfigured in /root/.cassandra)
# From your workstation:
ssh -i <your-key.pem> -N -L 9042:127.0.0.1:9042 ubuntu@<instance-public-ip>
# then connect with TLS, trusting the certificate below. CASSANDRA_HOST=127.0.0.1 CASSANDRA_PORT=9042 CASSANDRA_USER=admin CASSANDRA_TLS_CERTIFICATE=/etc/cassandra/tls/cassandra.crt CASSANDRA_CLUSTER_NAME=sac-cassandraThe same password is in /root/.cassandra/credentials, next to a cqlshrc that turns TLS on. This lets sudo cqlsh log in as admin without the password ever appearing on screen or on a command line.
Confirm Cassandra is running
Check both services, the Java version, the listening sockets and the node status:
systemctl is-active cassandra-firstboot.service cassandra.service java -version 2>&1 | head -1 sudo ss -Hltn nodetool statusactive active openjdk version "17.0.20.1" 2026-08-18 LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 50 127.0.0.1:7199 0.0.0.0:* LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* LISTEN 0 512 127.0.0.1:7000 0.0.0.0:* LISTEN 0 4096 127.0.0.1:9042 0.0.0.0:* LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* LISTEN 0 4096 [::]:22 [::]:* Datacenter: datacenter1 ======================= Status=Up/Down |/ State=Normal/Leaving/Joining/Moving -- Address Load Tokens Owns (effective) Host ID Rack UN 127.0.0.1 278.9 KiB 16 100.0% 28942fcf-bd97-4213-aa87-41a7c7de3146 rack1Only SSH listens on a public address. CQL (9042), inter-node storage (7000) and JMX (7199) listen on localhost. Port 53 on 127.0.0.53/127.0.0.54 is Ubuntu's local DNS resolver. UN means the node is Up and Normal. The host ID is unique to your instance.
Connect with cqlsh
Query the cluster name and release version, then list the roles:
sudo cqlsh -e "SELECT cluster_name, release_version FROM system.local"
sudo cqlsh -e "LIST ROLES"cluster_name | release_version --------------------+----------------- sac-cassandra | 5.0.9 (1 rows) role | super | login | options | datacenters -------+-------+-------+---------+------------- admin | True | True | {} | ALL (1 rows)admin is the only role, because the default cassandra superuser does not exist. For an interactive session, run sudo cqlsh with no arguments. Type exit to leave.
Create a keyspace and a table
A keyspace sets the replication for the tables inside it. On a single node, use SimpleStrategy with replication_factor: 1. Use NetworkTopologyStrategy once you run several nodes.
Design tables around the queries you run. In this example, orders are partitioned by customer and sorted newest first:
sudo cqlsh <<'CQL' CREATE KEYSPACE IF NOT EXISTS shop WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}; CREATE TABLE IF NOT EXISTS shop.orders ( customer text, order_id timeuuid, total decimal, status text, PRIMARY KEY (customer, order_id) ) WITH CLUSTERING ORDER BY (order_id DESC); INSERT INTO shop.orders (customer, order_id, total, status) VALUES ('acme', now(), 249.00, 'paid'); INSERT INTO shop.orders (customer, order_id, total, status) VALUES ('acme', now(), 99.50, 'shipped'); INSERT INTO shop.orders (customer, order_id, total, status) VALUES ('globex', now(), 1200.00, 'paid'); CQL
sudo cqlsh -e "SELECT customer, total, status FROM shop.orders WHERE customer = 'acme'"customer | total | status ----------+--------+--------- acme | 99.50 | shipped acme | 249.00 | paid (2 rows)Give an application its own role
Applications should not use admin. Create a login role that can only read and write the shop keyspace.
The password is generated on the instance and saved first to a credentials file of its own, ~/.cassandra/shop_app.credentials (mode 0600). A TLS settings file, ~/.cassandra/shop_app.cqlshrc, sits next to it and trusts this instance's certificate. The CREATE ROLE statement reaches cqlsh on standard input, so the password never appears on a command line:
mkdir -p ~/.cassandra && chmod 700 ~/.cassandra printf '[connection]\nssl = true\n\n[ssl]\ncertfile = /etc/cassandra/tls/cassandra.crt\nvalidate = true\n' > ~/.cassandra/shop_app.cqlshrc if [ ! -s ~/.cassandra/shop_app.credentials ]; then ( umask 077; printf '[PlainTextAuthProvider]\nusername = shop_app\npassword = %s\n' "$(openssl rand -hex 20)" > ~/.cassandra/shop_app.credentials ) fi APP_PW="$(sed -n 's/^password = //p' ~/.cassandra/shop_app.credentials)" printf "CREATE ROLE IF NOT EXISTS shop_app WITH LOGIN = true;\nALTER ROLE shop_app WITH PASSWORD = '%s';\nGRANT SELECT ON KEYSPACE shop TO shop_app;\nGRANT MODIFY ON KEYSPACE shop TO shop_app;\n" "$APP_PW" | sudo cqlsh unset APP_PW
sudo cqlsh -e "LIST ALL PERMISSIONS OF shop_app"role | username | resource | permission ----------+----------+-----------------+------------ shop_app | shop_app | <keyspace shop> | SELECT shop_app | shop_app | <keyspace shop> | MODIFY (2 rows)You can safely run the block again. It keeps the saved password and applies it again, and Cassandra warns that the permissions were already granted.
Log in as shop_app by naming both files on the command line. This stops cqlsh from falling back to the default ~/.cassandra/cqlshrc and ~/.cassandra/credentials (for root, those hold the admin login). The role can read and write the shop data, but CassandraAuthorizer refuses anything it was not granted:
SHOP=(--cqlshrc ~/.cassandra/shop_app.cqlshrc --credentials ~/.cassandra/shop_app.credentials) cqlsh "${SHOP[@]}" -e "SELECT customer, total, status FROM shop.orders WHERE customer = 'globex'" OUT="$(cqlsh "${SHOP[@]}" -e "CREATE KEYSPACE other WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}" 2>&1 || true)" case "$OUT" in *"User shop_app has no CREATE permission"*) echo "shop_app creating a keyspace: refused (no CREATE permission)" ;; *) echo "UNEXPECTED: $OUT"; exit 1 ;; esaccustomer | total | status ----------+---------+-------- globex | 1200.00 | paid (1 rows) shop_app creating a keyspace: refused (no CREATE permission)Connect from your workstation or application
Through an SSH tunnel (recommended)
Copy the instance's certificate to your workstation and forward a local port to the localhost-only CQL endpoint. Then connect over TLS with the admin password from Step 2, or with an application role:
scp -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>:/etc/cassandra/tls/cassandra.crt ./cassandra.crt ssh -i ~/.ssh/my-key-pair.pem -N -L 9042:127.0.0.1:9042 ubuntu@<public-ip> SSL_CERTFILE=./cassandra.crt cqlsh --ssl -u admin 127.0.0.1 9042cqlsh prompts for the password. Drivers connect the same way: enable TLS, trust cassandra.crt and use the contact point 127.0.0.1:9042 with a plain-text auth provider. For example, with the Python driver:
import ssl from cassandra.cluster import Cluster from cassandra.auth import PlainTextAuthProvider ctx = ssl.create_default_context(cafile="cassandra.crt") cluster = Cluster(["127.0.0.1"], port=9042, ssl_context=ctx, auth_provider=PlainTextAuthProvider("shop_app", "<password>")) session = cluster.connect("shop")The certificate lists localhost, 127.0.0.1, the instance's host name and its private IP addresses. Host-name checking therefore works through the tunnel and against the private IP.
From your VPC
To let application servers in your VPC connect directly:
- In
/etc/cassandra/cassandra.yaml, changerpc_address: 127.0.0.1to the instance's private IP. - Run
sudo systemctl restart cassandra.service. - Add an inbound rule for TCP 9042 to the instance's security group, from your application subnet's CIDR only, never
0.0.0.0/0. - Connect clients to the private IP with TLS (required) and their own role.
To grow to a multi-node cluster, set listen_address to each node's private IP and seeds to one or more node IPs. Open TCP 7000 between the nodes' security group only. Turn on inter-node TLS (server_encryption_options) before nodes talk across the network. Once the nodes have joined, raise the replication of system_auth and your keyspaces (NetworkTopologyStrategy).
Check the security controls
First, check the configuration: password authentication, the role-based authorizer, localhost binds and required client TLS:
sudo grep -nE '^(authenticator|authorizer|role_manager|listen_address|rpc_address|native_transport_port|storage_port):' /etc/cassandra/cassandra.yaml
sudo sed -n '/^client_encryption_options:/,/^ require_client_auth/p' /etc/cassandra/cassandra.yaml198:authenticator: PasswordAuthenticator 217:authorizer: CassandraAuthorizer 232:role_manager: CassandraRoleManager 960:storage_port: 7000 981:listen_address: 127.0.0.1 1023:native_transport_port: 9042 1090:rpc_address: 127.0.0.1 client_encryption_options:
# sac: TLS is REQUIRED on the CQL native transport (port 9042). First boot mints this instance's # own RSA-3072 key + self-signed certificate; replace the PEM with a CA-signed one if you have it. enabled: true optional: false ssl_context_factory: class_name: org.apache.cassandra.security.PEMBasedSslContextFactory keystore: /etc/cassandra/tls/cassandra-keystore.pem require_client_auth: falseNext, confirm that the default cassandra / cassandra login is refused, and that a client that does not use TLS is disconnected even with the right password:
T="$(mktemp -d)" printf '[connection]\nssl = true\n\n[ssl]\ncertfile = /etc/cassandra/tls/cassandra.crt\nvalidate = true\n' > "$T/tls.rc" printf '[connection]\nssl = false\n' > "$T/plain.rc" ( umask 077; printf '[PlainTextAuthProvider]\nusername = cassandra\npassword = cassandra\n' > "$T/default.cred" ) OUT="$(cqlsh --cqlshrc "$T/tls.rc" --credentials "$T/default.cred" -e 'SELECT now() FROM system.local' 2>&1 || true)" case "$OUT" in *"Provided username cassandra and/or password are incorrect"*) echo "default cassandra/cassandra login: refused" ;; *) echo "UNEXPECTED: $OUT"; rm -rf "$T"; exit 1 ;; esac OUT="$(cqlsh --cqlshrc "$T/plain.rc" -e 'SELECT now() FROM system.local' 2>&1 || true)" rm -rf "$T" case "$OUT" in *"was closed"*) echo "plaintext (non-TLS) client: disconnected" ;; *) echo "UNEXPECTED: $OUT"; exit 1 ;; esacdefault cassandra/cassandra login: refused plaintext (non-TLS) client: disconnectedThen confirm that the certificate the CQL endpoint serves is this instance's own:
openssl s_client -connect 127.0.0.1:9042 </dev/null 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ip-172-31-82-212, O = Apache Cassandra on this instance X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1, DNS:ip-172-31-82-212, IP Address:172.31.82.212Finally, check the service sandbox and the metadata block. The command then makes a metadata request from inside Cassandra's own control group, where 000 means the connection was blocked:
systemctl show -p User -p NoNewPrivileges -p ProtectSystem -p IPAddressDeny cassandra.service CODE="$(sudo bash -c 'echo $$ > "/sys/fs/cgroup$(systemctl show -p ControlGroup --value cassandra.service)/cgroup.procs"; curl -s -m 5 -o /dev/null -w "%{http_code}" -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 60" http://169.254.169.254/latest/api/token || true')" echo "metadata request from inside cassandra.service: ${CODE}"; [ "${CODE}" = "000" ]IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 User=cassandra ProtectSystem=strict NoNewPrivileges=yes metadata request from inside cassandra.service: 000Maintenance
- Change the admin password. Generate the password on the instance and pipe the statement to
cqlshon standard input. Then update the two root files thatsudo cqlshreads:NEW="$(openssl rand -hex 20)"; printf "ALTER ROLE admin WITH PASSWORD = '%s';\n" "$NEW" | sudo cqlsh. Then setCASSANDRA_PASSWORD=in/root/cassandra-credentials.txtandpassword =in/root/.cassandra/credentialsto the new value. - Your own certificate. Put your PEM private key (unencrypted PKCS#8), followed by your certificate chain, in
/etc/cassandra/tls/cassandra-keystore.pem(owner root, group cassandra, mode 0640). Put the certificate in/etc/cassandra/tls/cassandra.crt, and restartcassandra.service. - Backups.
nodetool snapshot -t nightly shophard-links a consistent snapshot under each table'ssnapshots/directory in/var/lib/cassandra/data. Copy the snapshots off the instance, or take an EBS snapshot of the volume.nodetool clearsnapshot -t nightlyremoves the snapshot afterwards. - Storage. Data lives under
/var/lib/cassandraon the root volume. For production, grow the root volume or attach a dedicated EBS volume. Pointdata_file_directoriesandcommitlog_directoryincassandra.yamlat it. Stop the service first, move the existing directories and keep the ownercassandra. - Memory.
cassandra-env.shsizes the heap from the instance's memory. Override it withMAX_HEAP_SIZEin/etc/cassandra/cassandra-env.shif you need to. - JMX. JMX listens on
127.0.0.1:7199without a JMX password, sonodetoolworks for anyone logged in to the instance. Keep 7199 closed in the security group. If other people can log in to the instance, enable JMX authentication as described in the Cassandra security documentation. - Updates.
sudo apt update && sudo apt upgradekeeps Ubuntu and OpenJDK 17 patched, and unattended security upgrades are on. Cassandra 5.0.9 was installed from the official package and no Cassandra repository is configured, so Cassandra changes only when you decide. To move to a later 5.0.x release, add the official repository (https://debian.cassandra.apache.org, suite50x, with the Apache Cassandra signing keys) and upgrade thecassandrapackage one node at a time. - Logs.
/var/log/cassandra/system.logis the server log.journalctl -u cassandra-firstboot.serviceshows what first boot did.
Support
SecureAnyCloud supports this image by email at sales@secureanycloud.com. Include the instance ID, the version (Apache Cassandra 5.0.9 on Ubuntu 24.04) and what you were doing.
Apache Cassandra is open-source software licensed under the Apache License 2.0. The notice is on the instance at /usr/share/doc/cassandra/copyright, and the licence text is at /usr/share/common-licenses/Apache-2.0. Apache Cassandra, Apache and Cassandra are trademarks of the Apache Software Foundation. Ubuntu is a trademark of Canonical Ltd. Java is a trademark of Oracle and/or its affiliates. SecureAnyCloud is not affiliated with or endorsed by these organisations.
Deploy on AWS
Launch Apache Cassandra 5.0.9 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com