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

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

/*
 * 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.net.InetSocketAddress;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
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;

/**
 * {@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. JAAS * Authentication Tutorial for more on JAAS in general. *
* *

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. Specify the protocol name with the {@code dse.sasl.protocol} system property when starting * your application, e.g. {@code -Ddse.sasl.protocol=cassandra}. *
* * 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