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

org.hbase.async.SecureRpcHelper Maven / Gradle / Ivy

Go to download

An alternative HBase client library for applications requiring fully asynchronous, non-blocking and thread-safe HBase connectivity.

There is a newer version: 1.8.2
Show newest version
/*
 * Copyright (C) 2015  The Async HBase Authors.  All rights reserved.
 * This file is part of Async HBase.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the StumbleUpon nor the names of its contributors
 *     may be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package org.hbase.async;

import java.lang.reflect.Constructor;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;

import org.hbase.async.auth.ClientAuthProvider;
import org.hbase.async.auth.KerberosClientAuthProvider;
import org.hbase.async.auth.SimpleClientAuthProvider;
import org.hbase.async.SecureRpcHelper;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This base class provides the logic needed to interface with SASL supported 
 * RPC versions. It is used by RegionClient to perform RPC handshaking as well 
 * as wrapping/unwrapping the rpc payload depending on the selected QOP level.
 * 
* * For Kerberos the following configurations need to be set as system properties. *
    *
  • hbase.security.authentication=<MECHANISM>
  • *
  • hbase.kerberos.regionserver.principal=<REGIONSERVER PRINCIPAL>
  • *
  • hbase.rpc.protection=[authentication|integrity|privacy]
  • *
  • hbase.sasl.clientconfig=<JAAS Profile Name>
  • *
  • java.security.auth.login.config=<Path to JAAS conf>
  • *
* i.e. *
*
    *
  • hbase.security.authentication=kerberos
  • *
  • hbase.kerberos.regionserver.principal=hbase/[email protected]
  • *
  • hbase.rpc.protection=authentication
  • *
  • hbase.sasl.clientconfig=Client
  • *
  • java.security.auth.login.config=/path/to/jaas.conf
  • *
* @since 1.7 */ public abstract class SecureRpcHelper { public static final String SECURITY_AUTHENTICATION_KEY = "hbase.security.authentication"; public static final String RPC_QOP_KEY = "hbase.rpc.protection"; private static final Logger LOG = LoggerFactory.getLogger(SecureRpcHelper.class); /** The HBaseClient config to pull values from */ protected final Config config; /** The region client this helper will be working with */ protected final RegionClient region_client; /** The IP of the region client */ protected final String host_ip; /** The authentication provider, e.g. Simple or Kerberos */ protected ClientAuthProvider client_auth_provider; /** The Sasl client if used */ protected SaslClient sasl_client; /** Whether or not to encrypt/decrypt the payload on the socket */ protected boolean use_wrap; /** * Ctor that instantiates the authentication provider and attempts to * authenticate at the same time. * @param hbase_client The Hbase client we belong to * @param region_client The region client we're dealing with * @param remote_endpoint The remote endpoint of the HBase Region server. */ public SecureRpcHelper(final HBaseClient hbase_client, final RegionClient region_client, final SocketAddress remote_endpoint) { config = hbase_client.getConfig(); this.host_ip = ((InetSocketAddress)remote_endpoint).getAddress() .getHostAddress(); this.region_client = region_client; initSecureClientProvider(hbase_client); } /** * Instantiates the proper security provider based on the configuration. The * provider can be either "simple", "kerberos" or a canonical class name * to load from the class path. Note that this will attempt to authenticate * so it will throw an exception if the login failed. * @param hbase_client The hbase client we belong to. * @throws IllegalStateException if the config contained a class name and we * couldn't instantiate the class. */ private void initSecureClientProvider(final HBaseClient hbase_client) { final String mechanism = config.hasProperty(SECURITY_AUTHENTICATION_KEY) ? config.getString(SECURITY_AUTHENTICATION_KEY) : "simple"; if ("simple".equalsIgnoreCase(mechanism)) { client_auth_provider = new SimpleClientAuthProvider(hbase_client); use_wrap = false; return; } if ("kerberos".equalsIgnoreCase(mechanism)) { client_auth_provider = new KerberosClientAuthProvider(hbase_client); } else { try { final Class clazz = Class.forName(mechanism); final Constructor ctor = clazz.getConstructor(HBaseClient.class); client_auth_provider = (ClientAuthProvider)ctor.newInstance(hbase_client); LOG.info("Successfully instantiated a security provider of type: " + clazz.getCanonicalName()); } catch (Exception e) { throw new IllegalStateException( "Failed to load specified SecureClientProvider: " + mechanism, e); } } final String qop = parseQOP(); use_wrap = qop != null && !"auth".equalsIgnoreCase(qop); final Map props = new HashMap(2); props.put(Sasl.QOP, parseQOP()); props.put(Sasl.SERVER_AUTH, "true"); sasl_client = client_auth_provider.newSaslClient(host_ip, props); } /** * Helper that checks to see if we should encrypt the packets between HBase * based on the client config. If the client has set "integrity" or "privacy" * then the packets will be encrypted. If set to "authentication" then no * encryption is used. Any other value will throw an exception * @return A string to compare against to see if we should wrap or not. * @throws IllegalArgumentException if the config doesn't contain one of * the three strings above. */ private String parseQOP() { final String protection = config.hasProperty(RPC_QOP_KEY) ? config.getString(RPC_QOP_KEY) : "authentication"; if ("integrity".equalsIgnoreCase(protection)) { return "auth-int"; } if ("privacy".equalsIgnoreCase(protection)) { return "auth-conf"; } if ("authentication".equalsIgnoreCase(protection)) { return "auth"; } throw new IllegalArgumentException("Unrecognized rpc protection level: " + protection); } /** * When QOP of auth-int or auth-conf is selected * This is used to unwrap the contents from the passed buffer payload. It * should be called as soon as it comes off the Netty channel. * Note that the ReplayingBufferDecoder may throw an error if we are unable * to read the full buffer and replay the data. * @param payload A wrapper around content. * @return The content extracted from the payload. * @throws IllegalStateException if the sasl client was unable to unwrap * the payload */ public ChannelBuffer unwrap(final ChannelBuffer payload) { if (!use_wrap) { return payload; } final int len = payload.readInt(); try { final ChannelBuffer unwrapped = ChannelBuffers.wrappedBuffer( sasl_client.unwrap(payload.readBytes(len).array(), 0, len)); // If encryption was enabled, it's a good bet that you shouldn't log it //if (LOG.isDebugEnabled()) { // LOG.debug("Unwrapped payload: " + Bytes.pretty(unwrapped)); //} return unwrapped; } catch (SaslException e) { throw new IllegalStateException("Failed to unwrap payload", e); } } /** * When QOP of auth-int or auth-conf is selected * This is used to wrap the contents into the proper payload (ie encryption, * signature, etc) and should be called just before sending the buffer to Netty. * @param content The content to be wrapped. * @return The wrapped payload. * @throws IllegalStateException if the sasl client was unable to wrap * the payload */ public ChannelBuffer wrap(final ChannelBuffer content) { if (!use_wrap) { return content; } try { final byte[] payload = new byte[content.writerIndex()]; content.readBytes(payload); final byte[] wrapped = sasl_client.wrap(payload, 0, payload.length); final ChannelBuffer ret = ChannelBuffers.wrappedBuffer( new byte[4 + wrapped.length]); ret.clear(); ret.writeInt(wrapped.length); ret.writeBytes(wrapped); //if (LOG.isDebugEnabled()) { // LOG.debug("Wrapped payload: " + Bytes.pretty(ret)); //} return ret; } catch (SaslException e) { throw new IllegalStateException("Failed to wrap payload", e); } } /** * Sends the initial handshake and/or SASL challenge to the region server * @param channel The channel to write to */ public abstract void sendHello(final Channel channel); /** * Handles a buffer received from the region server. It may be security * related or it may be a wrapped packet if security has been negotiated. * If wrapping is disabled, the original buffer is returned. If the data is * security related, a null should be returned. * NOTE: If the buffer IS security related, make sure to drain the entire * buffer or Netty will replay it until you're done consuming. * @param buf The buffer off the channel * @param chan The channel that we received the buffer from * @return The possibly unwrapped channel buffer for further decoding. If null * then the response was security related. */ public abstract ChannelBuffer handleResponse(final ChannelBuffer buf, final Channel chan); /** * Handles passing a buffer off to the sasl client for challenge evaluation * @param b The buffer to process * @return The results of the challenge if successful, or null if there was * a SaslException. * @throws IllegalStateException when processing of the action failed due to * a PrivilegedActionException */ protected byte[] processChallenge(final byte[] b) { try { final class PrivilegedAction implements PrivilegedExceptionAction { @Override public byte[] run() { try { return sasl_client.evaluateChallenge(b); } catch (SaslException e) { LOG.error("Failed Sasl challenge", e); return null; } } @Override public String toString() { return "evaluate sasl challenge"; } } return Subject.doAs(client_auth_provider.getClientSubject(), new PrivilegedAction()); } catch (PrivilegedActionException e) { throw new IllegalStateException("Failed to process challenge", e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy