All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.driver.dse.auth.DseGSSAPIAuthProvider Maven / Gradle / Ivy

Go to download

A driver for DataStax Enterprise (DSE) and Apache Cassandra 1.2+ clusters that works exclusively with the Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol, supporting DSE-specific features such as geospatial types, DSE Graph and DSE authentication.

There is a newer version: 2.4.0
Show newest version
/*
 * Copyright DataStax, Inc.
 *
 * This software can be used solely with DataStax Enterprise. Please consult the license at
 * http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */
package com.datastax.driver.dse.auth;

import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Authenticator;
import com.datastax.driver.core.exceptions.AuthenticationException;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;

import java.util.HashMap;
import javax.security.auth.Subject;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.net.InetSocketAddress;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Map;

/**
 * {@link AuthProvider} that provides GSSAPI authenticator instances for clients to connect
 * to DSE clusters secured with {@code DseAuthenticator}.
 * 

* To create a cluster using this auth provider, declare the following: *

{@code
 * Cluster cluster = Cluster.builder()
 *                          .addContactPoint(hostname)
 *                          .withAuthProvider(DseGSSAPIAuthProvider.builder().build())
 *                          .build();
 * }
*

Kerberos Authentication

* Keytab and ticket cache settings are specified using a standard JAAS * configuration file. The location of the file can be set using the * java.security.auth.login.config system property or by adding a * login.config.url.n entry in the java.security properties * file. *

* Alternatively a {@link Configuration} object can be provided using {@link #DseGSSAPIAuthProvider(Configuration)} to * set the JAAS configuration programmatically. *

* See the following documents for further details: *

    *
  1. JAAS Login Configuration File;
  2. *
  3. JAAS Authentication Tutorial * for more on JAAS in general.
  4. *
*

Authentication using ticket cache

* Run kinit to obtain a ticket and populate the cache before * connecting. JAAS config: *
 * DseClient {
 *   com.sun.security.auth.module.Krb5LoginModule required
 *     useTicketCache=true
 *     renewTGT=true;
 * };
 * 
*

Authentication using a keytab file

* To enable authentication using a keytab file, specify its location on disk. * If your keytab contains more than one principal key, you should also specify * which one to select. *
 * DseClient {
 *     com.sun.security.auth.module.Krb5LoginModule required
 *       useKeyTab=true
 *       keyTab="/path/to/file.keytab"
 *       principal="[email protected]";
 * };
 * 
*

Specifying SASL protocol name

* The SASL protocol name used by this auth provider defaults to "{@value #DEFAULT_SASL_PROTOCOL_NAME}". *

* Important: the SASL protocol name should match the username of the * Kerberos service principal used by the DSE server. * This information is specified in the dse.yaml file by the {@code service_principal} option under the * kerberos_options * section, and may vary from one DSE installation to another – especially if you * installed DSE with an automated package installer. *

* For example, if your dse.yaml file contains the following: *

{@code
 * kerberos_options:
 *     ...
 *     service_principal: cassandra/[email protected]
 * }
* The correct SASL protocol name to use when authenticating against this DSE server is "{@code cassandra}". *

* Should you need to change the SASL protocol name, use one of the methods below: *

    *
  1. Specify the protocol name via one of the following constructors: * {@link #DseGSSAPIAuthProvider(String)} or {@link #DseGSSAPIAuthProvider(Configuration, String)};
  2. *
  3. Specify the protocol name with the {@code dse.sasl.protocol} system property when starting your application, * e.g. {@code -Ddse.sasl.protocol=cassandra}.
  4. *
* If a non-null SASL protocol name is provided to the aforementioned constructors, that name takes precedence over * the contents of the {@code dse.sasl.protocol} system property. * * @see Authenticating a DSE cluster with Kerberos */ public class DseGSSAPIAuthProvider implements AuthProvider { /** * The default SASL protocol name used by this auth provider. */ public static final String DEFAULT_SASL_PROTOCOL_NAME = "dse"; /** * The name of the system property to use to specify the SASL protocol name. */ public static final String SASL_PROTOCOL_NAME_PROPERTY = "dse.sasl.protocol"; /** * The default SASL properties: * *
     * javax.security.sasl.server.authentication = true
     * javax.security.sasl.qop = auth
     * 
*/ public static final Map DEFAULT_SASL_PROPERTIES = ImmutableMap.builder() .put(Sasl.SERVER_AUTH, "true") .put(Sasl.QOP, "auth") .build(); private final Configuration loginConfiguration; private final String saslProtocol; private final String authorizationId; private final Subject subject; private final Map properties; public static Builder builder() { return new Builder(); } public static class Builder { private Configuration loginConfiguration; private String saslProtocol; private String authorizationId; private Subject subject; private Map properties = new HashMap(DEFAULT_SASL_PROPERTIES); private Builder() { } /** * @param loginConfiguration The login configuration to use to create a {@link LoginContext}. If * {@link #withSubject} is also used, this input is not used. */ public Builder withLoginConfiguration(Configuration loginConfiguration) { this.loginConfiguration = loginConfiguration; return this; } /** * @param saslProtocol The SASL protocol name to use; should match the username of the * Kerberos service principal used by the DSE server. */ public Builder withSaslProtocol(String saslProtocol) { this.saslProtocol = saslProtocol; return this; } /** * @param authorizationId The authorization ID (allows proxy authentication). */ public Builder withAuthorizationId(String authorizationId) { this.authorizationId = authorizationId; return this; } /** * @param subject A previously authenticated subject to reuse. If provided, any calls to * {@link #withLoginConfiguration} are ignored. */ public Builder withSubject(Subject subject) { this.subject = subject; return this; } /** * Add a SASL property to use when creating the SASL client. * * @param name the property name. * @param value the property value. * @see Sasl */ public Builder addSaslProperty(String name, String value) { this.properties.put(name, value); return this; } public DseGSSAPIAuthProvider build() { return new DseGSSAPIAuthProvider(loginConfiguration, subject, saslProtocol, authorizationId, properties); } } /** * Creates an instance of {@code DseGSSAPIAuthProvider} with default login configuration options and default * SASL protocol name ({@value #DEFAULT_SASL_PROTOCOL_NAME}). * * @deprecated Use {@link Builder} to create {@link DseGSSAPIAuthProvider} instead. */ @Deprecated @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"}) public DseGSSAPIAuthProvider() { this(null, null, null, null, DEFAULT_SASL_PROPERTIES); } /** * Creates an instance of {@code DseGSSAPIAuthProvider} with the given login configuration and default * SASL protocol name ({@value #DEFAULT_SASL_PROTOCOL_NAME}). * * @param loginConfiguration The login configuration to use to create a {@link LoginContext}. * @deprecated Use {@link Builder} to create {@link DseGSSAPIAuthProvider} instead. */ @Deprecated @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"}) public DseGSSAPIAuthProvider(Configuration loginConfiguration) { this(loginConfiguration, null, null, null, DEFAULT_SASL_PROPERTIES); } /** * Creates an instance of {@code DseGSSAPIAuthProvider} with default login configuration and the given * SASL protocol name. * * @param saslProtocol The SASL protocol name to use; should match the username of the * Kerberos service principal used by the DSE server. * @deprecated Use {@link Builder} to create {@link DseGSSAPIAuthProvider} instead. */ @Deprecated @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"}) public DseGSSAPIAuthProvider(String saslProtocol) { this(null, null, saslProtocol, null, DEFAULT_SASL_PROPERTIES); } /** * Creates an instance of {@code DseGSSAPIAuthProvider} with the given login configuration and the given * SASL protocol name. * * @param loginConfiguration The login configuration to use to create a {@link LoginContext}. * @param saslProtocol The SASL protocol name to use; should match the username of the * Kerberos service principal used by the DSE server. * @deprecated Use {@link Builder} to create {@link DseGSSAPIAuthProvider} instead. */ @Deprecated @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"}) public DseGSSAPIAuthProvider(Configuration loginConfiguration, String saslProtocol) { this(loginConfiguration, null, saslProtocol, null, DEFAULT_SASL_PROPERTIES); } private DseGSSAPIAuthProvider(Configuration loginConfiguration, Subject subject, String saslProtocol, String authorizationId, Map properties) { this.loginConfiguration = loginConfiguration; this.subject = subject; this.saslProtocol = saslProtocol; this.authorizationId = authorizationId; this.properties = ImmutableMap.copyOf(properties); } @Override public Authenticator newAuthenticator(InetSocketAddress host, String authenticator) throws AuthenticationException { if (subject != null) { return new GSSAPIAuthenticator(authenticator, authorizationId, host, subject, saslProtocol, properties); } else { return new GSSAPIAuthenticator(authenticator, authorizationId, host, loginConfiguration, saslProtocol, properties); } } private static class GSSAPIAuthenticator extends BaseDseAuthenticator { private static final String JAAS_CONFIG_ENTRY = "DseClient"; private static final String[] SUPPORTED_MECHANISMS = new String[]{"GSSAPI"}; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private static final byte[] MECHANISM = "GSSAPI".getBytes(Charsets.UTF_8); private static final byte[] SERVER_INITIAL_CHALLENGE = "GSSAPI-START".getBytes(Charsets.UTF_8); private final Subject subject; private final SaslClient saslClient; private GSSAPIAuthenticator(String authenticator, String authorizationId, InetSocketAddress host, Configuration loginConfiguration, String saslProtocol, Map properties) { super(authenticator); try { String protocol = saslProtocol; if (protocol == null) { protocol = System.getProperty(SASL_PROTOCOL_NAME_PROPERTY, DEFAULT_SASL_PROTOCOL_NAME); } LoginContext login = new LoginContext(JAAS_CONFIG_ENTRY, null, null, loginConfiguration); login.login(); subject = login.getSubject(); saslClient = Sasl.createSaslClient(SUPPORTED_MECHANISMS, authorizationId, protocol, host.getAddress().getCanonicalHostName(), properties, null); } catch (LoginException e) { throw new RuntimeException(e); } catch (SaslException e) { throw new RuntimeException(e); } } private GSSAPIAuthenticator(String authenticator, String authorizationId, InetSocketAddress host, Subject subject, String saslProtocol, Map properties) { super(authenticator); try { String protocol = saslProtocol; if (protocol == null) { protocol = System.getProperty(SASL_PROTOCOL_NAME_PROPERTY, DEFAULT_SASL_PROTOCOL_NAME); } this.subject = subject; saslClient = Sasl.createSaslClient(SUPPORTED_MECHANISMS, authorizationId, protocol, host.getAddress().getCanonicalHostName(), properties, null); } catch (SaslException e) { throw new RuntimeException(e); } } @Override public byte[] getMechanism() { return MECHANISM.clone(); } @Override public byte[] getInitialServerChallenge() { return SERVER_INITIAL_CHALLENGE.clone(); } @Override public byte[] evaluateChallenge(byte[] challenge) { if (Arrays.equals(SERVER_INITIAL_CHALLENGE, challenge)) { if (!saslClient.hasInitialResponse()) { return EMPTY_BYTE_ARRAY; } challenge = EMPTY_BYTE_ARRAY; } final byte[] internalChallenge = challenge; try { return Subject.doAs(subject, new PrivilegedExceptionAction() { @Override public byte[] run() throws SaslException { return saslClient.evaluateChallenge(internalChallenge); } }); } catch (PrivilegedActionException e) { throw new RuntimeException(e.getException()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy