Scylla Documentation Logo Documentation
  • Server
    • Scylla Open Source
    • Scylla Enterprise
    • Scylla Alternator
  • Cloud
    • Scylla Cloud
    • Scylla Cloud Docs
  • Tools
    • Scylla Manager
    • Scylla Monitoring Stack
    • Scylla Operator
  • Drivers
    • CQL Drivers
    • DynamoDB Drivers
Download
Menu

Caution

You're viewing documentation for a previous version of Scylla Python Driver. Switch to the latest stable version.

Scylla Python Driver Security

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 when protocol_version is set to 2 or higher. 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 cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider

auth_provider = PlainTextAuthProvider(username='cassandra', password='cassandra')
cluster = Cluster(auth_provider=auth_provider, protocol_version=2)

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.

Protocol v1 Authentication¶

When working with Cassandra 1.2 (or a higher version with protocol_version set to 1), you will not pass in an AuthProvider instance. Instead, you should pass in a function that takes one argument, the IP address of a host, and returns a dict of credentials with a username and password key:

from cassandra.cluster import Cluster

def get_credentials(host_address):
    return {'username': 'joe', 'password': '1234'}

cluster = Cluster(auth_provider=get_credentials, protocol_version=1)

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 3.17.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.

If you create your SSLContext using ssl.create_default_context, be aware that SSLContext.check_hostname is set to True by default, so the hostname validation will be done by Python and not the driver. For this reason, we need to set the server_hostname at best effort, which is the resolved ip address. If this validation needs to be done against the FQDN, consider enabling it using the ssl_options as described in the following examples or implement your own EndPoint and EndPointFactory.

The following examples assume you have generated your Cassandra certificate and keystore files with these intructions:

  • Setup SSL Cert

It might be also useful to learn about the different levels of identity verification to understand the examples:

  • Using SSL in DSE drivers

SSL with Twisted or Eventlet¶

Twisted and Eventlet both use an alternative SSL implementation called pyOpenSSL, so if your Cluster’s connection class is TwistedConnection or EventletConnection, you must pass a pyOpenSSL context instead. An example is provided in these docs, and more details can be found in the documentation. pyOpenSSL is not installed by the driver and must be installed separately.

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.

Server verifies client and client verifies server using Twisted and pyOpenSSL

from OpenSSL import SSL, crypto
from cassandra.cluster import Cluster
from cassandra.io.twistedreactor import TwistedConnection

ssl_context = SSL.Context(SSL.TLSv1_METHOD)
ssl_context.set_verify(SSL.VERIFY_PEER, callback=lambda _1, _2, _3, _4, ok: ok)
ssl_context.use_certificate_file('/path/to/client.crt_signed')
ssl_context.use_privatekey_file('/path/to/client.key')
ssl_context.load_verify_locations('/path/to/rootca.crt')

cluster = Cluster(
    contact_points=['127.0.0.1'],
    connection_class=TwistedConnection,
    ssl_context=ssl_context,
    ssl_options={'check_hostname': True}
)
session = cluster.connect()

Connecting using Eventlet would look similar except instead of importing and using TwistedConnection, you would import and use EventletConnection, including the appropriate monkey-patching.

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 cassandra.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.

DSE Authentication¶

When authenticating against DSE, the Cassandra driver provides two auth providers that work both with legacy kerberos and Cassandra authenticators, as well as the new DSE Unified Authentication. This allows client to configure this auth provider independently, and in advance of any server upgrade. These auth providers are configured in the same way as any previous implementation:

from cassandra.auth import DSEGSSAPIAuthProvider
auth_provider = DSEGSSAPIAuthProvider(service='dse', qops=["auth"])
cluster = Cluster(auth_provider=auth_provider)
session = cluster.connect()

Implementations are DSEPlainTextAuthProvider, DSEGSSAPIAuthProvider and SaslAuthProvider.

DSE Unified Authentication¶

With DSE (>=5.1), unified Authentication allows you to:

  • Proxy Login: Authenticate using a fixed set of authentication credentials but allow authorization of resources based another user id.

  • Proxy Execute: Authenticate using a fixed set of authentication credentials but execute requests based on another user id.

Proxy Login¶

Proxy login allows you to authenticate with a user but act as another one. You need to ensure the authenticated user has the permission to use the authorization of resources of the other user. ie. this example will allow the server user to authenticate as usual but use the authorization of user1:

GRANT PROXY.LOGIN on role user1 to server

then you can do the proxy authentication….

from cassandra.cluster import Cluster
from cassandra.auth import SaslAuthProvider

sasl_kwargs = {
  "service": 'dse',
  "mechanism":"PLAIN",
  "username": 'server',
  'password': 'server',
  'authorization_id': 'user1'
}

auth_provider = SaslAuthProvider(**sasl_kwargs)
c = Cluster(auth_provider=auth_provider)
s = c.connect()
s.execute(...)  # all requests will be executed as 'user1'

If you are using kerberos, you can use directly DSEGSSAPIAuthProvider and pass the authorization_id, like this:

from cassandra.cluster import Cluster
from cassandra.auth import DSEGSSAPIAuthProvider

# Ensure the kerberos ticket of the server user is set with the kinit utility.
auth_provider = DSEGSSAPIAuthProvider(service='dse', qops=["auth"], principal="server@DATASTAX.COM",
                                      authorization_id='user1@DATASTAX.COM')
c = Cluster(auth_provider=auth_provider)
s = c.connect()
s.execute(...)  # all requests will be executed as 'user1'

Proxy Execute¶

Proxy execute allows you to execute requests as another user than the authenticated one. You need to ensure the authenticated user has the permission to use the authorization of resources of the specified user. ie. this example will allow the server user to execute requests as user1:

GRANT PROXY.EXECUTE on role user1 to server

then you can do a proxy execute…

from cassandra.cluster import Cluster
from cassandra.auth import DSEPlainTextAuthProvider,

auth_provider = DSEPlainTextAuthProvider('server', 'server')

c = Cluster(auth_provider=auth_provider)
s = c.connect()
s.execute('select * from k.t;', execute_as='user1')  # the request will be executed as 'user1'

Please see the official documentation for more details on the feature and configuration process.

PREVIOUS
Lightweight Transactions (Compare-and-set)
NEXT
User Defined Types
  • 3.24.8
    • 3.25.4
    • 3.24.8
    • 3.22.3
    • 3.21.0
  • API Documentation
    • cassandra - Exceptions and Enums
    • cassandra.cluster - Clusters and Sessions
    • cassandra.policies - Load balancing and Failure Handling Policies
    • cassandra.auth - Authentication
    • cassandra.graph - Graph Statements, Options, and Row Factories
    • cassandra.metadata - Schema and Ring Topology
    • cassandra.metrics - Performance Metrics
    • cassandra.query - Prepared Statements, Batch Statements, Tracing, and Row Factories
    • cassandra.pool - Hosts and Connection Pools
    • cassandra.protocol - Protocol Features
    • cassandra.encoder - Encoders for non-prepared Statements
    • cassandra.decoder - Data Return Formats
    • cassandra.concurrent - Utilities for Concurrent Statement Execution
    • cassandra.connection - Low Level Connection Info
    • cassandra.util - Utilities
    • cassandra.timestamps - Timestamp Generation
    • cassandra.io.asyncioreactor - asyncio Event Loop
    • cassandra.io.asyncorereactor - asyncore Event Loop
    • cassandra.io.eventletreactor - eventlet-compatible Connection
    • cassandra.io.libevreactor - libev Event Loop
    • cassandra.io.geventreactor - gevent-compatible Event Loop
    • cassandra.io.twistedreactor - Twisted Event Loop
    • cassandra.cqlengine.models - Table models for object mapping
    • cassandra.cqlengine.columns - Column types for object mapping models
    • cassandra.cqlengine.query - Query and filter model objects
    • cassandra.cqlengine.connection - Connection management for cqlengine
    • cassandra.cqlengine.management - Schema management for cqlengine
    • cassandra.cqlengine.usertype - Model classes for User Defined Types
    • cassandra.datastax.graph - Graph Statements, Options, and Row Factories
    • cassandra.datastax.graph.fluent
    • cassandra.datastax.graph.fluent.query
    • cassandra.datastax.graph.fluent.predicates
  • Installation
  • Getting Started
  • Scylla Specific Features
  • Upgrading
  • Execution Profiles
  • Performance Notes
  • Paging Large Queries
  • Lightweight Transactions (Compare-and-set)
  • Security
  • User Defined Types
  • Object Mapper
    • Upgrade Guide
    • Models
    • Making Queries
    • Batch Queries
    • Connections
    • Third party integrations
    • Frequently Asked Questions
  • Working with Dates and Times
  • Scylla Cloud
  • Frequently Asked Questions
  • Create an issue
  • Edit this page

On this page

  • Security
    • Authentication
      • Custom Authenticators
      • Protocol v1 Authentication
    • SSL
      • SSL with Twisted or Eventlet
      • SSL Configuration Examples
        • No identity verification
        • Client verifies server
        • Server verifies client
        • Server verifies client and client verifies server
      • Versions 3.16.0 and lower
        • SSL with Twisted
    • DSE Authentication
      • DSE Unified Authentication
        • Proxy Login
        • Proxy Execute
Logo
Docs Contact Us About Us
Mail List Icon Slack Icon
© ScyllaDB 2021 and © DataStax 2013-2017
Powered by Sphinx 4.3.2 & ScyllaDB Theme 1.2.2