Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ See [here](http://web.mit.edu/kerberos/www/krb5-latest/doc/admin/conf_files/krb5
The plugin works with the [Cassandra Java driver](https://github.com/datastax/java-driver):

```
Cluster cluster = Cluster.builder()
.addContactPoint(hostname)
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress(ipAddress, 9042))
.withAuthProvider(KerberosAuthProvider.builder().build()
.build();
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
Expand All @@ -46,8 +45,8 @@
* Specify this auth provider when creating a new Cluster object, as follows:
*
* <pre>{@code
* Cluster cluster = Cluster.builder()
* .addContactPoint(hostname)
* CqlSession session = CqlSession.builder()
* .addContactPoint(new InetSocketAddress(ipAddress, 9042))
* .withAuthProvider(KerberosAuthProvider.builder().build()
* .build();
* }</pre>
Expand All @@ -64,8 +63,8 @@
* the builder as follows:
*
* <pre>{@code
* Cluster cluster = Cluster.builder()
* .addContactPoint(hostname)
* CqlSession session = CqlSession.builder()
* .addContactPoint(new InetSocketAddress(ipAddress, 9042))
* .withAuthProvider(KerberosAuthProvider.builder()
* .withSaslProtocol("cassandra")
* .build())
Expand All @@ -78,6 +77,20 @@
* e.g. If your service principal is <code>cassandra/node1.cluster.example.com@EXAMPLE.COM</code>
* then the SASL protocol name must be <code>cassandra</code>.
*
* <h2>Override SASL server name</h2>
*
* The SASL client will use the canonical host name from the contact point IP address. To override this behavior,
* configured the builder with a custom ServerNameResolver as follows:
*
* <pre>{@code
* CqlSession session = CqlSession.builder()
* .addContactPoint(new InetSocketAddress(ipAddress, 9042))
* .withAuthProvider(KerberosAuthProvider.builder()
* .withServerNameResolver(new CustomServerNameResolver())
* .build())
* .build();
* }</pre>
*
* <h2>JAAS configuration file</h2>
* A JAAS configuration file with an entry named {@value KerberosAuthenticator#JAAS_CONFIG_ITEM_NAME} must be provided in order to
* provide the configuration details of the GSS-API subject.
Expand Down Expand Up @@ -110,9 +123,6 @@
*/
public class KerberosAuthProvider implements AuthProvider
{

private static final Logger logger = LoggerFactory.getLogger(KerberosAuthProvider.class);

private static final String DEFAULT_SASL_PROTOCOL = "cassandra";
private static final Map<String, String> DEFAULT_SASL_PROPERTIES =
ImmutableMap.<String, String>builder()
Expand All @@ -121,12 +131,17 @@ public class KerberosAuthProvider implements AuthProvider
.build();

private final String authorizationId;
private final ServerNameResolver serverNameResolver;
private final String saslProtocol;
private final Map<String, ?> saslProperties;

private KerberosAuthProvider(final String authorizationId, final String saslProtocol, final Map<String, ?> saslProperties)
private KerberosAuthProvider(final String authorizationId,
final ServerNameResolver serverNameResolver,
final String saslProtocol,
final Map<String, ?> saslProperties)
{
this.authorizationId = authorizationId;
this.serverNameResolver = serverNameResolver;
this.saslProtocol = saslProtocol;
this.saslProperties = ImmutableMap.copyOf(saslProperties);
}
Expand All @@ -137,6 +152,7 @@ public static Builder builder() {

public static class Builder {
private String authorizationId = null;
private ServerNameResolver serverNameResolver = new ServerNameResolver(){};
private String saslProtocol = DEFAULT_SASL_PROTOCOL;
private Map<String, ?> saslProperties = DEFAULT_SASL_PROPERTIES;

Expand Down Expand Up @@ -204,16 +220,31 @@ public Builder withSaslProperties(Map<String, ?> saslProperties)
return this;
}

/**
* Optional resolver for the serverName as part of the SASL Client API. Defaults to the IP Addresses Canonical HostName.
*
* See <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/sasl/sasl-refguide.html#CLIENT">here</a>
* for further information.
*
* @param serverNameResolver The optional implementation of a resolver for the serverName
* @return the builder object
*/
public Builder withServerNameResolver(ServerNameResolver serverNameResolver)
{
this.serverNameResolver = serverNameResolver;
return this;
}

public KerberosAuthProvider build()
{
return new KerberosAuthProvider(authorizationId, saslProtocol, saslProperties);
return new KerberosAuthProvider(authorizationId, serverNameResolver, saslProtocol, saslProperties);
}
}

@Override
public Authenticator newAuthenticator(@NonNull EndPoint endpoint, @NonNull String authenticator) throws AuthenticationException
{
return new KerberosAuthenticator(authorizationId, saslProtocol, endpoint, saslProperties);
return new KerberosAuthenticator(authorizationId, serverNameResolver, saslProtocol, endpoint, saslProperties);
}

@Override
Expand All @@ -239,25 +270,29 @@ public static class KerberosAuthenticator implements Authenticator
private final Subject subject;
private final SaslClient saslClient;

private KerberosAuthenticator(String authorizationId, String saslProtocol, EndPoint endpoint, Map<String, ?> saslProperties)
private KerberosAuthenticator(String authorizationId,
ServerNameResolver serverNameResolver,
String saslProtocol,
EndPoint endpoint,
Map<String, ?> saslProperties)
{
if (saslProperties == null) {
throw new NullPointerException("No SASL Properties supplied, unable to perform kerberos authentication");
}

this.subject = loginAsSubject();

String hostName = ((InetSocketAddress)endpoint.resolve()).getAddress().getCanonicalHostName();
String serverName = serverNameResolver.resolve(endpoint);

logger.debug("Creating SaslClient for {} on Host {} with {} mechanism. SASL Protocol: {} SASL Properties: {}",
authorizationId, hostName, SASL_MECHANISMS, saslProtocol, saslProperties);
logger.debug("Creating SaslClient for {} on Server {} with {} mechanism. SASL Protocol: {} SASL Properties: {}",
authorizationId, serverName, SASL_MECHANISMS, saslProtocol, saslProperties);

try {
this.saslClient = Sasl.createSaslClient(
SASL_MECHANISMS,
authorizationId,
saslProtocol,
hostName,
serverName,
saslProperties,
null);
} catch (SaslException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.instaclustr.cassandra.driver.auth;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.datastax.oss.driver.api.core.metadata.EndPoint;

import java.net.InetSocketAddress;

/**
* Provide an option to override the server name that the SASL client uses when defining the server to authenticate to.
* <br>
* Specify a ServerNameResolver when the auth scheme requires something other than the resolved FQDN of the IP address
* <p>
* (see
* <a href="https://docs.oracle.com/javase/8/docs/api/javax/security/sasl/Sasl.html#createSaslClient-java.lang.String:A-java.lang.String-java.lang.String-java.lang.String-java.util.Map-javax.security.auth.callback.CallbackHandler-">here</a>
* for more details).
*/
public interface ServerNameResolver {

/**
* Define the mechanism for translating a Cassandra endpoint into a server name when creating the SASL Client.
*
* @param endpoint The Cassandra endpoint of the node to authenticate to.
* @return The server name to be used in creating the SaslClient.
*/
default String resolve(EndPoint endpoint) {
return ((InetSocketAddress) endpoint.resolve()).getAddress().getCanonicalHostName();
}

}