Security
The two main security components you will use with the Python driver are Authentication and SSL.
Authentication
Versions 2.0 and higher of the driver support a SASL-based
authentication mechanism. To use this authentication, set
auth_provider
to an instance of a subclass
of AuthProvider
. When working with
Cassandra’s PasswordAuthenticator
, you can use
the PlainTextAuthProvider
class.
For example, suppose Cassandra is setup with its default ‘cassandra’ user with a password of ‘cassandra’:
from dse.cluster import Cluster
from dse.auth import PlainTextAuthProvider
auth_provider = PlainTextAuthProvider(username='cassandra', password='cassandra')
cluster = Cluster(auth_provider=auth_provider, protocol_version=3)
Custom Authenticators
If you’re using something other than Cassandra’s PasswordAuthenticator
,
SaslAuthProvider
is provided for generic SASL authentication mechanisms,
utilizing the pure-sasl
package.
If these do not suit your needs, you may need to create your own subclasses of
AuthProvider
and Authenticator
. You can use the Sasl classes
as example implementations.
SSL
SSL should be used when client encryption is enabled in Cassandra.
To give you as much control as possible over your SSL configuration, our SSL API takes a user-created SSLContext instance from the Python standard library. These docs will include some examples for how to achieve common configurations, but the ssl.SSLContext documentation gives a more complete description of what is possible.
To enable SSL with version 2.8.0 and higher, you will need to set Cluster.ssl_context
to a
ssl.SSLContext
instance to enable SSL. Optionally, you can also set Cluster.ssl_options
to a dict of options. These will be passed as kwargs to ssl.SSLContext.wrap_socket()
when new sockets are created.
The following examples assume you have generated your Cassandra certificate and keystore files with these intructions:
It might be also useful to learn about the different levels of identity verification to understand the examples:
SSL Configuration Examples
Here, we’ll describe the server and driver configuration necessary to set up SSL to meet various goals, such as the client verifying the server and the server verifying the client. We’ll also include Python code demonstrating how to use servers and drivers configured in these ways.
No identity verification
No identity verification at all. Note that this is not recommended for for production deployments.
The Cassandra configuration:
client_encryption_options:
enabled: true
keystore: /path/to/127.0.0.1.keystore
keystore_password: myStorePass
require_client_auth: false
The driver configuration:
from cassandra.cluster import Cluster, Session
from ssl import SSLContext, PROTOCOL_TLSv1
ssl_context = SSLContext(PROTOCOL_TLSv1)
cluster = Cluster(['127.0.0.1'], ssl_context=ssl_context)
session = cluster.connect()
Client verifies server
Ensure the python driver verifies the identity of the server.
The Cassandra configuration:
client_encryption_options:
enabled: true
keystore: /path/to/127.0.0.1.keystore
keystore_password: myStorePass
require_client_auth: false
For the driver configuration, it’s very important to set ssl_context.verify_mode to CERT_REQUIRED. Otherwise, the loaded verify certificate will have no effect:
from cassandra.cluster import Cluster, Session
from ssl import SSLContext, PROTOCOL_TLSv1, CERT_REQUIRED
ssl_context = SSLContext(PROTOCOL_TLSv1)
ssl_context.load_verify_locations('/path/to/rootca.crt')
ssl_context.verify_mode = CERT_REQUIRED
cluster = Cluster(['127.0.0.1'], ssl_context=ssl_context)
session = cluster.connect()
Additionally, you can also force the driver to verify the hostname of the server by passing additional options to ssl_context.wrap_socket via the ssl_options kwarg:
from cassandra.cluster import Cluster, Session
from ssl import SSLContext, PROTOCOL_TLSv1, CERT_REQUIRED
ssl_context = SSLContext(PROTOCOL_TLSv1)
ssl_context.load_verify_locations('/path/to/rootca.crt')
ssl_context.verify_mode = CERT_REQUIRED
ssl_context.check_hostname = True
ssl_options = {'server_hostname': '127.0.0.1'}
cluster = Cluster(['127.0.0.1'], ssl_context=ssl_context, ssl_options=ssl_options)
session = cluster.connect()
Server verifies client
If Cassandra is configured to verify clients (require_client_auth
), you need to generate
SSL key and certificate files.
The cassandra configuration:
client_encryption_options:
enabled: true
keystore: /path/to/127.0.0.1.keystore
keystore_password: myStorePass
require_client_auth: true
truststore: /path/to/dse-truststore.jks
truststore_password: myStorePass
The Python ssl
APIs require the certificate in PEM format. First, create a certificate
conf file:
cat > gen_client_cert.conf <<EOF
[ req ]
distinguished_name = req_distinguished_name
prompt = no
output_password = ${ROOT_CERT_PASS}
default_bits = 2048
[ req_distinguished_name ]
C = ${CERT_COUNTRY}
O = ${CERT_ORG_NAME}
OU = ${CERT_OU}
CN = client
EOF
Make sure you replaced the variables with the same values you used for the initial root CA certificate. Then, generate the key:
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -config gen_client_cert.conf
And generate the client signed certificate:
openssl x509 -req -CA ${ROOT_CA_BASE_NAME}.crt -CAkey ${ROOT_CA_BASE_NAME}.key -passin pass:${ROOT_CERT_PASS} \
-in client.csr -out client.crt_signed -days ${CERT_VALIDITY} -CAcreateserial
Finally, you can use that configuration with the following driver code:
from cassandra.cluster import Cluster, Session
from ssl import SSLContext, PROTOCOL_TLSv1
ssl_context = SSLContext(PROTOCOL_TLSv1)
ssl_context.load_cert_chain(
certfile='/path/to/client.crt_signed',
keyfile='/path/to/client.key')
cluster = Cluster(['127.0.0.1'], ssl_context=ssl_context)
session = cluster.connect()
Server verifies client and client verifies server
See the previous section for examples of Cassandra configuration and preparing the client certificates.
The following driver code specifies that the connection should use two-way verification:
from cassandra.cluster import Cluster, Session
from ssl import SSLContext, PROTOCOL_TLSv1, CERT_REQUIRED
ssl_context = SSLContext(PROTOCOL_TLSv1)
ssl_context.load_verify_locations('/path/to/rootca.crt')
ssl_context.verify_mode = CERT_REQUIRED
ssl_context.load_cert_chain(
certfile='/path/to/client.crt_signed',
keyfile='/path/to/client.key')
cluster = Cluster(['127.0.0.1'], ssl_context=ssl_context)
session = cluster.connect()
The driver uses SSLContext
directly to give you many other options in configuring SSL. Consider reading the Python SSL documentation
for more details about SSLContext
configuration.
Versions 3.16.0 and lower
To enable SSL you will need to set Cluster.ssl_options
to a
dict of options. These will be passed as kwargs to ssl.wrap_socket()
when new sockets are created. Note that this use of ssl_options will be
deprecated in the next major release.
By default, a ca_certs
value should be supplied (the value should be
a string pointing to the location of the CA certs file), and you probably
want to specify ssl_version
as ssl.PROTOCOL_TLSv1
to match
Cassandra’s default protocol.
For example:
from dse.cluster import Cluster
from ssl import PROTOCOL_TLSv1, CERT_REQUIRED
ssl_opts = {
'ca_certs': '/path/to/my/ca.certs',
'ssl_version': PROTOCOL_TLSv1,
'cert_reqs': CERT_REQUIRED # Certificates are required and validated
}
cluster = Cluster(ssl_options=ssl_opts)
This is only an example to show how to pass the ssl parameters. Consider reading the python ssl documentation for your configuration. For further reading, Andrew Mussey has published a thorough guide on Using SSL with the DataStax Python driver.
SSL with Twisted
In case the twisted event loop is used pyOpenSSL must be installed or an exception will be risen. Also
to set the ssl_version
and cert_reqs
in ssl_opts
the appropriate constants from pyOpenSSL are expected.