I want to setup my own CA for use with a cassandra cluster so that I do not have to copy all of the certificates around every time I add a new node. I have read a few tutorials for Cassandra and SSL but they all work with copying certificates around. I am a little lost in the CA process
This is what I think I need to do
Now:
Advantage: No more copying of ssl certificates between nodes. Just one for each node and you're set.
edit:
Ok this is how I did it. If I made any mistakes, please let me know. I left out things like JCE files and proper cassandra.yaml config. These need to be present on server!
openssl genrsa -out clusterCA.key 2048
openssl req -x509 -new -key clusterCA.key -days <DAYS> -out clusterCA.pem
keytool -importcert -alias clusterCA -file clusterCA.pem -keystore clustertruststore -storepass <PASS>
#on each cassandra host for clients. for client replace nodename with clientname
keytool -genkeypair -alias <NODENAME> -keyalg RSA -keysize 2048 -dname "CN=<NODENAME>,OU=<UNITNAME>,O=<ORGANISATION>" -keypass <PASS> -keystore <NODENAME>.keystore -storepass <PASS> -validity <DAYS>
keytool -keystore <NODENAME>.keystore -alias <NODENAME> -certreq -file <NODENAME>.cert -storepass <PASS> -keypass <PASS>
# sign it with CA
openssl x509 -req -CA clusterCA.pem -CAkey clusterCa.key -in <NODENAME>.cert -out <NODENAME>.signed -days <DAYS> -CAcreateserial
# add rootCA to host
keytool -keystore <NODENAME>.keystore -storepass <PASS> -alias clusterCA -import -file clusterCA.pem -noprompt
keytool -keystore <NODENAME>.keystore -storepass <PASS> -alias <NODENAME> -import -file <NODENAME>.signed -keypass <PASS>
## use <NODENAME>.keystore as truststore and keystore for cassandra node / client trust/keystore
## No need to copy keystores around. You only need it on your host
## create CQLSH pem
keytool -importkeystore -srckeystore <NODENAME>.keystore -destkeystore <NODENAME>_user1.p12 -deststoretype PKCS12
openssl pkcs12 -in <NODENAME>_user1.p12 -out <NODENAME>_user1.pem -nodes
## use <NODENAME>_user1.pem as certfile for cqlsh
Your strategy is very sound and that is the way I'd do it. You want to have your own Certificate Authority and then create a CSR for each node. That's much easier to manage than trusting node certificates individually.
It is also important to differentiate between internode encryption and client encryption. Cassandra has different settings for each (documented in the links above). If using client-to-node encryption you will also want to have a truststore for client certificates. You could use the same trust store and also issue certificates to clients.
On the client-to-node side here's an example from the java-driver tests how to set up your SSLContext using your key and truststores:
/**
* @param keyStorePath Path to keystore, if absent is not used.
* @param trustStorePath Path to truststore, if absent is not used.
* @return {@link com.datastax.driver.core.SSLOptions} with the given keystore and truststore path's for
* server certificate validation and client certificate authentication.
*/
public SSLOptions getSSLOptions(Optional<String> keyStorePath, Optional<String> trustStorePath) throws Exception {
TrustManagerFactory tmf = null;
if(trustStorePath.isPresent()) {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(this.getClass().getResourceAsStream(trustStorePath.get()), DEFAULT_CLIENT_TRUSTSTORE_PASSWORD.toCharArray());
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
}
KeyManagerFactory kmf = null;
if(keyStorePath.isPresent()) {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(this.getClass().getResourceAsStream(keyStorePath.get()), DEFAULT_CLIENT_KEYSTORE_PASSWORD.toCharArray());
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, DEFAULT_CLIENT_KEYSTORE_PASSWORD.toCharArray());
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf != null ? kmf.getKeyManagers() : null, tmf != null ? tmf.getTrustManagers() : null, new SecureRandom());
return new SSLOptions(sslContext, SSLOptions.DEFAULT_SSL_CIPHER_SUITES);
}
Once you an SSLOptions object you can simply pass it into your Cluster Builder, i.e.:
cluster = Cluster.builder()
.addContactPoint(host)
.withSSL(sslOptions))
.build();
CQLSH supports SSL via the cqlshrc file. You can find an example of how to set that up here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With