Summary
This article will discuss how to set up and connect with the java 4.9.0 driver.
Applies to
- DataStax Enterprise
- Cassandra
Solution
My dse.yaml:
authentication_options:
other_schemes:
- internal
scheme_permissions: false
default_scheme: kerberos
enabled: true
kerberos_options:
keytab: /home/automaton/ctool_security/dse.keytab
service_principal: dse_automaton/_HOST@lacerda-kerberos
http_principal: HTTP/_HOST@lacerda-kerberos
qop: auth
My kerberos principals which I added with addprinc:
kadmin: listprincs
HTTP/ip-10-101-32-144.srv101.dsinternal.org@lacerda-kerberos
HTTP/ip-10-101-32-187.srv101.dsinternal.org@lacerda-kerberos
HTTP/ip-10-101-33-155.srv101.dsinternal.org@lacerda-kerberos
K/M@lacerda-kerberos
automaton@lacerda-kerberos
cassandra/admin@lacerda-kerberos
cassandra@lacerda-kerberos
dse_automaton/ip-10-101-32-144.srv101.dsinternal.org@lacerda-kerberos
dse_automaton/ip-10-101-32-187.srv101.dsinternal.org@lacerda-kerberos
dse_automaton/ip-10-101-33-155.srv101.dsinternal.org@lacerda-kerberos
kadmin/admin@lacerda-kerberos
kadmin/changepw@lacerda-kerberos
kadmin/ip-10-101-32-187.srv101.dsinternal.org@lacerda-kerberos
kiprop/ip-10-101-32-187.srv101.dsinternal.org@lacerda-kerberos
krbtgt/lacerda-kerberos@lacerda-kerberos
root/admin@lacerda-kerberos
As you can see from the above, I have a 3 node cluster and have added principals for HTTP and dse_automaton for all 3 nodes ([dse_automaton|HTTP]/{hostname}@{realm}).
After adding all of my principles, including the user principal that I will be connecting with, I set up a keytab named dse.keytab for my user service principal cassandra:
$ kadmin -kt dse.keytab -p cassandra
Authenticating as principal cassandrawith keytab dse.keytab.
Password for cassandra@lacerda-kerberos:
kadmin: ktadd -k dse.keytab cassandra@lacerda-kerberos
Entry for principal cassandra@lacerda-kerberos with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:dse.keytab.
Entry for principal cassandra@lacerda-kerberos with kvno 2, encryption type des-hmac-sha1 added to keytab WRFILE:dse.keytab.
Entry for principal cassandra@lacerda-kerberos with kvno 2, encryption type des-cbc-md5 added to keytab WRFILE:dse.keytab.
kadmin: exit
My dse.keytab looked like the following:
$ klist -kt dse.keytab -e
Keytab name: FILE:dse.keytab
KVNO Timestamp Principal
---- ------------------- ------------------------------------------------------
2 01/20/2021 03:04:48 cassandra@lacerda-kerberos (arcfour-hmac)
2 01/20/2021 03:04:48 cassandra@lacerda-kerberos (des-hmac-sha1)
2 01/20/2021 03:04:48 cassandra@lacerda-kerberos (des-cbc-md5)
Here's my ~/.cassandra/cqlshrc file:
[kerberos]
hostname = ip-10-101-32-187.srv101.dsinternal.org
service = dse_automaton
[connection]
hostname = ip-10-101-32-187.srv101.dsinternal.org
And within cqlsh, I set up several roles for the user principals:
cqlsh> list roles;
role | super | login | options
-----------------------------------------------------------------------+-------+-------+---------
cassandra | True | True | {}
cassandra/admin@lacerda-kerberos | True | True | {}
cassandra@lacerda-kerberos | True | True | {}
From my remote client laptop, I ran kinit to get a ticket from the KDC:
$ kinit -p cassandra
Or, if you're using a keytab for your user principal:
$ kinit -kt dse.keytab
And here's the ticket that was produced from the kinit:
$ klist
Ticket cache: KCM:501
Default principal: cassandra@lacerda-kerberos
Valid starting Expires Service principal
01/19/2021 07:13:18 01/19/2021 17:13:18 krbtgt/lacerda-kerberos@lacerda-kerberos
renew until 01/26/2021 07:13:13
And for complete transparency, my /etc/krb5.conf file looks like this (same on client and KDC server):
[libdefaults]
default_realm = lacerda-kerberos
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 72h
renew_lifetime = 7d
forwardable = true
# The following krb5.conf variables are only for MIT Kerberos.
krb4_config = /etc/krb.conf
krb4_realms = /etc/krb.realms
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
# The following encryption type specification will be used by MIT Kerberos
# if uncommented. In general, the defaults in the MIT Kerberos code are
# correct and overriding these specifications only serves to disable new
# encryption types as they are added, creating interoperability problems.
#
# Thie only time when you might need to uncomment these lines and change
# the enctypes is if you have local software that will break on ticket
# caches containing ticket encryption types it doesn't know about (such as
# old versions of Sun Java).
# default_tgs_enctypes = des3-hmac-sha1
# default_tkt_enctypes = des3-hmac-sha1
# permitted_enctypes = des3-hmac-sha1
# The following libdefaults parameters are only for Heimdal Kerberos.
v4_instance_resolve = false
v4_name_convert = {
host = {
rcmd = host
ftp = ftp
}
plain = {
something = something-else
}
}
fcc-mit-ticketflags = true
[realms]
LACERDA-KERBEROS = {
kdc = 10.101.32.187
admin_server = 10.101.32.187
default_domain = lacerda-kerberos
}
lacerda-kerberos = {
kdc = 10.101.32.187
admin_server = 10.101.32.187
}
[domain_realm]
.mit.edu = ATHENA.MIT.EDU
mit.edu = ATHENA.MIT.EDU
.media.mit.edu = MEDIA-LAB.MIT.EDU
media.mit.edu = MEDIA-LAB.MIT.EDU
.csail.mit.edu = CSAIL.MIT.EDU
csail.mit.edu = CSAIL.MIT.EDU
.whoi.edu = ATHENA.MIT.EDU
whoi.edu = ATHENA.MIT.EDU
.stanford.edu = stanford.edu
.slac.stanford.edu = SLAC.STANFORD.EDU
.toronto.edu = UTORONTO.CA
.utoronto.ca = UTORONTO.CA
[login]
krb4_convert = true
krb4_get_tickets = false
With my ticket, I was able to create the following java code to connect to my DSE cluster with the Java 4.9 driver:
package com.example.cassandra;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.*;
import java.net.InetSocketAddress;
public class TestingConnections {
public CqlSession session;
public void connect() {
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("dse.sasl.service", "dse_automaton");
System.setProperty("javax.security.sasl.qop", "auth");
session = CqlSession.builder().build();
String keyspace = "keyspace1";
String table = "names";
this.getData(keyspace, table);
}
public void getData(String keyspace, String table) {
ResultSet results = session.execute("select * from keyspace1.names");
for (Row row : results) {
String firstName = row.getString("first");
String lastName = row.getString("last");
System.out.printf("First: %s, Last: %s\n", firstName, lastName);
}
}
public void close() {
session.close();
}
public static void main(String[] args) {
TestingConnections client = new TestingConnections();
client.connect();
client.close();
}
}
A couple of comments regarding the code around the System.setProperty parameters:
System.setProperty("sun.security.krb5.debug", "true");
Provides additional debugging, which is very useful when troubleshooting Kerberos connection/authentication issues.
System.setProperty("dse.sasl.service", "dse_automaton");
The sasl service by default uses dse, so to bypass that, we had to use the above property. Otherwise, you may get errors regarding sname. Which you'll see with Kerberos debugging turned on.
My pom.xml looks like the following:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.cassandra</groupId>
<artifactId>testing-connection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>testing-connection</name>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
</dependencies>
</project>
My application.conf, which is read in by default if it's located in src/main/resources contains some configuration parameters that my application uses to connect to DSE and tell it what authentication to use:
datastax-java-driver {
basic {
contact-points = [ "10.101.32.187:9042" ]
load-balancing-policy {
local-datacenter = "Cassandra"
}
}
advanced {
connnection {
init-query-timeout = 2000 milliseconds
}
auth-provider {
class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
login-configuration {
principal = "cassandra"
useKeyTab = "true"
refreshKrb5Config = "true"
keyTab = "/Users/stevelacerda/dse.keytab"
debug = "true"
}
sasl-properties {
javax.security.sasl.qop = "auth"
}
}
}
}
I copied the dse.keytab from the server using scp, and then provided the location locally in the keyTab parameter above.
Here's a screenshot of my project in Apache Netbeans so that you can view the file locations: