hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From apurt...@apache.org
Subject git commit: HBASE-11810 Access SSL Passwords through Credential Provider API (Larry McCay)
Date Fri, 29 Aug 2014 14:55:13 GMT
Repository: hbase
Updated Branches:
  refs/heads/0.98 d0b45a488 -> c970a85ca


HBASE-11810 Access SSL Passwords through Credential Provider API (Larry McCay)


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/c970a85c
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/c970a85c
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/c970a85c

Branch: refs/heads/0.98
Commit: c970a85cae355782e1a60bdec36f8340d8e1e422
Parents: d0b45a4
Author: Andrew Purtell <apurtell@apache.org>
Authored: Thu Aug 28 16:18:11 2014 -0700
Committer: Andrew Purtell <apurtell@apache.org>
Committed: Thu Aug 28 16:22:19 2014 -0700

----------------------------------------------------------------------
 .../apache/hadoop/hbase/HBaseConfiguration.java |  49 ++++
 .../hadoop/hbase/TestHBaseConfiguration.java    | 275 ++++++++++++++++++-
 .../apache/hadoop/hbase/rest/RESTServer.java    |   6 +-
 3 files changed, 327 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/c970a85c/hbase-common/src/main/java/org/apache/hadoop/hbase/HBaseConfiguration.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/HBaseConfiguration.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/HBaseConfiguration.java
index 22fc1f9..1d2f47e 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/HBaseConfiguration.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/HBaseConfiguration.java
@@ -17,6 +17,9 @@
  */
 package org.apache.hadoop.hbase;
 
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Map.Entry;
 
 import org.apache.commons.logging.Log;
@@ -184,6 +187,52 @@ public class HBaseConfiguration extends Configuration {
     }
   }
 
+  /**
+   * Get the password from the Configuration instance using the
+   * getPassword method if it exists. If not, then fall back to the
+   * general get method for configuration elements.
+   * @param conf configuration instance for accessing the passwords
+   * @param alias the name of the password element
+   * @param defPass the default password
+   * @return String password or default password
+   * @throws IOException
+   */
+  public static String getPassword(Configuration conf, String alias,
+      String defPass) throws IOException {
+    String passwd = null;
+    try {
+      Method m = Configuration.class.getMethod("getPassword", String.class);
+      char[] p = (char[]) m.invoke(conf, alias);
+      if (p != null) {
+        LOG.debug(String.format("Config option \"%s\" was found through" +
+        		" the Configuration getPassword method.", alias));
+        passwd = new String(p);
+      }
+      else {
+        LOG.debug(String.format(
+            "Config option \"%s\" was not found. Using provided default value",
+            alias));
+        passwd = defPass;
+      }
+    } catch (NoSuchMethodException e) {
+      // this is a version of Hadoop where the credential
+      //provider API doesn't exist yet
+      LOG.debug(String.format(
+          "Credential.getPassword method is not available." +
+          " Falling back to configuration."));
+      passwd = conf.get(alias, defPass);
+    } catch (SecurityException e) {
+      throw new IOException(e.getMessage(), e);
+    } catch (IllegalAccessException e) {
+      throw new IOException(e.getMessage(), e);
+    } catch (IllegalArgumentException e) {
+      throw new IOException(e.getMessage(), e);
+    } catch (InvocationTargetException e) {
+      throw new IOException(e.getMessage(), e);
+    }
+    return passwd;
+  }
+
   /** For debugging.  Dump configurations to system output as xml format.
    * Master and RS configurations can also be dumped using
    * http services. e.g. "curl http://master:60010/dump"

http://git-wip-us.apache.org/repos/asf/hbase/blob/c970a85c/hbase-common/src/test/java/org/apache/hadoop/hbase/TestHBaseConfiguration.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/TestHBaseConfiguration.java
b/hbase-common/src/test/java/org/apache/hadoop/hbase/TestHBaseConfiguration.java
index 94eac02..60fa3b3 100644
--- a/hbase-common/src/test/java/org/apache/hadoop/hbase/TestHBaseConfiguration.java
+++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/TestHBaseConfiguration.java
@@ -18,9 +18,16 @@
 
 package org.apache.hadoop.hbase;
 
-
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -28,6 +35,8 @@ import org.junit.experimental.categories.Category;
 @Category(SmallTests.class)
 public class TestHBaseConfiguration {
 
+  private static final Log LOG = LogFactory.getLog(TestHBaseConfiguration.class);
+
   @Test
   public void testGetIntDeprecated() {
     int VAL = 1, VAL2 = 2;
@@ -53,4 +62,268 @@ public class TestHBaseConfiguration {
     assertEquals(VAL, HBaseConfiguration.getInt(conf, NAME, DEPRECATED_NAME, 0));
   }
 
+  @Test
+  public void testGetPassword() throws Exception {
+    Configuration conf = HBaseConfiguration.create();
+    conf.set(ReflectiveCredentialProviderClient.CREDENTIAL_PROVIDER_PATH,
+        "jceks://file/tmp/foo.jks");
+    ReflectiveCredentialProviderClient client =
+        new ReflectiveCredentialProviderClient();
+    if (client.isHadoopCredentialProviderAvailable()) {
+      char[] keyPass = {'k', 'e', 'y', 'p', 'a', 's', 's'};
+      char[] storePass = {'s', 't', 'o', 'r', 'e', 'p', 'a', 's', 's'};
+      client.createEntry(conf, "ssl.keypass.alias", keyPass);
+      client.createEntry(conf, "ssl.storepass.alias", storePass);
+
+      String keypass = HBaseConfiguration.getPassword(
+          conf, "ssl.keypass.alias", null);
+      assertEquals(keypass, new String(keyPass));
+
+      String storepass = HBaseConfiguration.getPassword(
+          conf, "ssl.storepass.alias", null);
+      assertEquals(storepass, new String(storePass));
+    }
+  }
+
+  private static class ReflectiveCredentialProviderClient {
+    public static final String HADOOP_CRED_PROVIDER_FACTORY_CLASS_NAME =
+        "org.apache.hadoop.security.alias.JavaKeyStoreProvider$Factory";
+    public static final String
+      HADOOP_CRED_PROVIDER_FACTORY_GET_PROVIDERS_METHOD_NAME = "getProviders";
+
+    public static final String HADOOP_CRED_PROVIDER_CLASS_NAME =
+        "org.apache.hadoop.security.alias.CredentialProvider";
+    public static final String
+        HADOOP_CRED_PROVIDER_GET_CREDENTIAL_ENTRY_METHOD_NAME =
+        "getCredentialEntry";
+    public static final String
+        HADOOP_CRED_PROVIDER_GET_ALIASES_METHOD_NAME = "getAliases";
+    public static final String
+        HADOOP_CRED_PROVIDER_CREATE_CREDENTIAL_ENTRY_METHOD_NAME =
+        "createCredentialEntry";
+    public static final String HADOOP_CRED_PROVIDER_FLUSH_METHOD_NAME = "flush";
+
+    public static final String HADOOP_CRED_ENTRY_CLASS_NAME =
+        "org.apache.hadoop.security.alias.CredentialProvider$CredentialEntry";
+    public static final String HADOOP_CRED_ENTRY_GET_CREDENTIAL_METHOD_NAME =
+        "getCredential";
+
+    public static final String CREDENTIAL_PROVIDER_PATH =
+        "hadoop.security.credential.provider.path";
+
+    private static Object hadoopCredProviderFactory = null;
+    private static Method getProvidersMethod = null;
+    private static Method getAliasesMethod = null;
+    private static Method getCredentialEntryMethod = null;
+    private static Method getCredentialMethod = null;
+    private static Method createCredentialEntryMethod = null;
+    private static Method flushMethod = null;
+    private static Boolean hadoopClassesAvailable = null;
+
+    /**
+     * Determine if we can load the necessary CredentialProvider classes. Only
+     * loaded the first time, so subsequent invocations of this method should
+     * return fast.
+     *
+     * @return True if the CredentialProvider classes/methods are available,
+     *         false otherwise.
+     */
+    private boolean isHadoopCredentialProviderAvailable() {
+      if (null != hadoopClassesAvailable) {
+        // Make sure everything is initialized as expected
+        if (hadoopClassesAvailable && null != getProvidersMethod
+            && null != hadoopCredProviderFactory
+            && null != getCredentialEntryMethod && null != getCredentialMethod)
{
+          return true;
+        } else {
+          // Otherwise we failed to load it
+          return false;
+        }
+      }
+
+      hadoopClassesAvailable = false;
+
+      // Load Hadoop CredentialProviderFactory
+      Class<?> hadoopCredProviderFactoryClz = null;
+      try {
+        hadoopCredProviderFactoryClz = Class
+            .forName(HADOOP_CRED_PROVIDER_FACTORY_CLASS_NAME);
+      } catch (ClassNotFoundException e) {
+        return false;
+      }
+      // Instantiate Hadoop CredentialProviderFactory
+      try {
+        hadoopCredProviderFactory = hadoopCredProviderFactoryClz.newInstance();
+      } catch (InstantiationException e) {
+        return false;
+      } catch (IllegalAccessException e) {
+        return false;
+      }
+
+      try {
+        getProvidersMethod = loadMethod(hadoopCredProviderFactoryClz,
+            HADOOP_CRED_PROVIDER_FACTORY_GET_PROVIDERS_METHOD_NAME,
+            Configuration.class);
+
+        // Load Hadoop CredentialProvider
+        Class<?> hadoopCredProviderClz = null;
+        hadoopCredProviderClz = Class.forName(HADOOP_CRED_PROVIDER_CLASS_NAME);
+        getCredentialEntryMethod = loadMethod(hadoopCredProviderClz,
+            HADOOP_CRED_PROVIDER_GET_CREDENTIAL_ENTRY_METHOD_NAME, String.class);
+
+        getAliasesMethod = loadMethod(hadoopCredProviderClz,
+            HADOOP_CRED_PROVIDER_GET_ALIASES_METHOD_NAME);
+
+        createCredentialEntryMethod = loadMethod(hadoopCredProviderClz,
+            HADOOP_CRED_PROVIDER_CREATE_CREDENTIAL_ENTRY_METHOD_NAME,
+            String.class, char[].class);
+
+        flushMethod = loadMethod(hadoopCredProviderClz,
+            HADOOP_CRED_PROVIDER_FLUSH_METHOD_NAME);
+
+        // Load Hadoop CredentialEntry
+        Class<?> hadoopCredentialEntryClz = null;
+        try {
+          hadoopCredentialEntryClz = Class
+              .forName(HADOOP_CRED_ENTRY_CLASS_NAME);
+        } catch (ClassNotFoundException e) {
+          LOG.error("Failed to load class:" + e);
+          return false;
+        }
+
+        getCredentialMethod = loadMethod(hadoopCredentialEntryClz,
+            HADOOP_CRED_ENTRY_GET_CREDENTIAL_METHOD_NAME);
+      } catch (Exception e1) {
+        return false;
+      }
+
+      hadoopClassesAvailable = true;
+      LOG.info("Credential provider classes have been" +
+      		" loaded and initialized successfully through reflection.");
+      return true;
+
+    }
+
+    private Method loadMethod(Class<?> clz, String name, Class<?>... classes)
+        throws Exception {
+      Method method = null;
+      try {
+        method = clz.getMethod(name, classes);
+      } catch (SecurityException e) {
+        fail("security exception caught for: " + name + " in " +
+      clz.getCanonicalName());
+        throw e;
+      } catch (NoSuchMethodException e) {
+        LOG.error("Failed to load the " + name + ": " + e);
+        fail("no such method: " + name + " in " + clz.getCanonicalName());
+        throw e;
+      }
+      return method;
+    }
+
+    /**
+     * Wrapper to fetch the configured {@code List<CredentialProvider>}s.
+     *
+     * @param conf
+     *    Configuration with GENERAL_SECURITY_CREDENTIAL_PROVIDER_PATHS defined
+     * @return List of CredentialProviders, or null if they could not be loaded
+     */
+    @SuppressWarnings("unchecked")
+    protected  List<Object> getCredentialProviders(Configuration conf) {
+      // Call CredentialProviderFactory.getProviders(Configuration)
+      Object providersObj = null;
+      try {
+        providersObj = getProvidersMethod.invoke(hadoopCredProviderFactory,
+            conf);
+      } catch (IllegalArgumentException e) {
+        LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
+            ": " + e);
+        return null;
+      } catch (IllegalAccessException e) {
+        LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
+            ": " + e);
+        return null;
+      } catch (InvocationTargetException e) {
+        LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
+            ": " + e);
+        return null;
+      }
+
+      // Cast the Object to List<Object> (actually List<CredentialProvider>)
+      try {
+        return (List<Object>) providersObj;
+      } catch (ClassCastException e) {
+        return null;
+      }
+    }
+
+    /**
+     * Create a CredentialEntry using the configured Providers.
+     * If multiple CredentialProviders are configured, the first will be used.
+     *
+     * @param conf
+     *          Configuration for the CredentialProvider
+     * @param name
+     *          CredentialEntry name (alias)
+     * @param credential
+     *          The credential
+     */
+    public  void createEntry(Configuration conf, String name, char[] credential)
+        throws Exception {
+
+      if (!isHadoopCredentialProviderAvailable()) {
+        return;
+      }
+
+      List<Object> providers = getCredentialProviders(conf);
+      if (null == providers) {
+        throw new IOException("Could not fetch any CredentialProviders, " +
+        		"is the implementation available?");
+      }
+
+      Object provider = providers.get(0);
+      createEntryInProvider(provider, name, credential);
+    }
+
+    /**
+     * Create a CredentialEntry with the give name and credential in the
+     * credentialProvider. The credentialProvider argument must be an instance
+     * of Hadoop
+     * CredentialProvider.
+     *
+     * @param credentialProvider
+     *          Instance of CredentialProvider
+     * @param name
+     *          CredentialEntry name (alias)
+     * @param credential
+     *          The credential to store
+     */
+    private void createEntryInProvider(Object credentialProvider,
+        String name, char[] credential) throws Exception {
+
+      if (!isHadoopCredentialProviderAvailable()) {
+        return;
+      }
+
+      try {
+        createCredentialEntryMethod.invoke(credentialProvider, name, credential);
+      } catch (IllegalArgumentException e) {
+        return;
+      } catch (IllegalAccessException e) {
+        return;
+      } catch (InvocationTargetException e) {
+        return;
+      }
+
+      try {
+        flushMethod.invoke(credentialProvider);
+      } catch (IllegalArgumentException e) {
+        throw e;
+      } catch (IllegalAccessException e) {
+        throw e;
+      } catch (InvocationTargetException e) {
+        throw e;
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c970a85c/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
index 83153bc..18f0d33 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
@@ -191,8 +191,10 @@ public class RESTServer implements Constants {
     if(conf.getBoolean(REST_SSL_ENABLED, false)) {
       SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
       String keystore = conf.get(REST_SSL_KEYSTORE_STORE);
-      String password = conf.get(REST_SSL_KEYSTORE_PASSWORD);
-      String keyPassword = conf.get(REST_SSL_KEYSTORE_KEYPASSWORD, password);
+      String password = HBaseConfiguration.getPassword(conf,
+        REST_SSL_KEYSTORE_PASSWORD, null);
+      String keyPassword = HBaseConfiguration.getPassword(conf,
+        REST_SSL_KEYSTORE_KEYPASSWORD, password);
       sslConnector.setKeystore(keystore);
       sslConnector.setPassword(password);
       sslConnector.setKeyPassword(keyPassword);


Mime
View raw message