Index: src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 1423617) +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java (working copy) @@ -281,6 +281,10 @@ this.rsFatals = new MemoryBoundedLogMessageBuffer( conf.getLong("hbase.master.buffer.for.rs.fatals", 1*1024*1024)); + // login the zookeeper client principal (if using security) + ZKUtil.loginClient(this.conf, "hbase.zookeeper.client.keytab.file", + "hbase.zookeeper.client.kerberos.principal", this.isa.getHostName()); + // initialize server principal (if using secure Hadoop) User.login(conf, "hbase.master.keytab.file", "hbase.master.kerberos.principal", this.isa.getHostName()); Index: src/main/java/org/apache/hadoop/hbase/master/HMasterCommandLine.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMasterCommandLine.java (revision 1423617) +++ src/main/java/org/apache/hadoop/hbase/master/HMasterCommandLine.java (working copy) @@ -39,6 +39,7 @@ import org.apache.hadoop.hbase.util.JVMClusterUtil; import org.apache.hadoop.hbase.util.ServerCommandLine; import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.zookeeper.KeeperException; public class HMasterCommandLine extends ServerCommandLine { @@ -124,6 +125,11 @@ + HConstants.ZOOKEEPER_CLIENT_PORT); } zooKeeperCluster.setDefaultClientPort(zkClientPort); + + // login the zookeeper server principal (if using security) + ZKUtil.loginServer(conf, "hbase.zookeeper.server.keytab.file", + "hbase.zookeeper.server.kerberos.principal", null); + int clientPort = zooKeeperCluster.startup(zkDataPath); if (clientPort != zkClientPort) { String errorMsg = "Could not start ZK at requested port of " + Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (revision 1423617) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (working copy) @@ -437,6 +437,10 @@ this.rpcServer.setQosFunction(new QosFunction()); this.startcode = System.currentTimeMillis(); + // login the zookeeper client principal (if using security) + ZKUtil.loginClient(this.conf, "hbase.zookeeper.client.keytab.file", + "hbase.zookeeper.client.kerberos.principal", this.isa.getHostName()); + // login the server principal (if using secure Hadoop) User.login(this.conf, "hbase.regionserver.keytab.file", "hbase.regionserver.kerberos.principal", this.isa.getHostName()); Index: src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java (revision 1423617) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java (working copy) @@ -61,6 +61,12 @@ writeMyID(zkProperties); QuorumPeerConfig zkConfig = new QuorumPeerConfig(); zkConfig.parseProperties(zkProperties); + + // login the zookeeper server principal (if using security) + ZKUtil.loginServer(conf, "hbase.zookeeper.server.keytab.file", + "hbase.zookeeper.server.kerberos.principal", + zkConfig.getClientPortAddress().getHostName()); + runZKServer(zkConfig); } catch (Exception e) { e.printStackTrace(); Index: src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (revision 1423617) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (working copy) @@ -24,13 +24,22 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; +import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Properties; +import java.util.HashMap; +import java.util.Map; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.authentication.util.KerberosUtil; + import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -54,6 +63,8 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.apache.zookeeper.server.ZooKeeperSaslServer; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.SetDataRequest; @@ -117,6 +128,170 @@ retry, retryIntervalMillis); } + /** + * Log in the current zookeeper server process using the given configuration + * keys for the credential file and login principal. + * + *
This is only applicable when running on secure hbase + * On regular HBase (without security features), this will safely be ignored. + *
+ * + * @param conf The configuration data to use + * @param keytabFileKey Property key used to configure the path to the credential file + * @param userNameKey Property key used to configure the login principal + * @param hostname Current hostname to use in any credentials + * @throws IOException underlying exception from SecurityUtil.login() call + */ + public static void loginServer(Configuration conf, String keytabFileKey, + String userNameKey, String hostname) throws IOException { + login(conf, keytabFileKey, userNameKey, hostname, + ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, + JaasConfiguration.SERVER_KEYTAB_KERBEROS_CONFIG_NAME); + } + + /** + * Log in the current zookeeper client using the given configuration + * keys for the credential file and login principal. + * + *This is only applicable when running on secure hbase + * On regular HBase (without security features), this will safely be ignored. + *
+ * + * @param conf The configuration data to use + * @param keytabFileKey Property key used to configure the path to the credential file + * @param userNameKey Property key used to configure the login principal + * @param hostname Current hostname to use in any credentials + * @throws IOException underlying exception from SecurityUtil.login() call + */ + public static void loginClient(Configuration conf, String keytabFileKey, + String userNameKey, String hostname) throws IOException { + login(conf, keytabFileKey, userNameKey, hostname, + ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, + JaasConfiguration.CLIENT_KEYTAB_KERBEROS_CONFIG_NAME); + } + + /** + * Log in the current process using the given configuration keys for the + * credential file and login principal. + * + *This is only applicable when running on secure hbase + * On regular HBase (without security features), this will safely be ignored. + *
+ * + * @param conf The configuration data to use + * @param keytabFileKey Property key used to configure the path to the credential file + * @param userNameKey Property key used to configure the login principal + * @param hostname Current hostname to use in any credentials + * @param loginContextProperty property name to expose the entry name + * @param loginContextName jaas entry name + * @throws IOException underlying exception from SecurityUtil.login() call + */ + private static void login(Configuration conf, String keytabFileKey, + String userNameKey, String hostname, + String loginContextProperty, String loginContextName) + throws IOException { + if (!isSecureZooKeeper(conf)) + return; + + // User has specified a jaas.conf, keep this one as the good one. + // HBASE_OPTS="-Djava.security.auth.login.config=jaas.conf" + if (System.getProperty("java.security.auth.login.config") != null) + return; + + String keytabFilename = conf.get(keytabFileKey); + String principalConfig = conf.get(userNameKey, System.getProperty("user.name")); + String principalName = SecurityUtil.getServerPrincipal(principalConfig, hostname); + + // Initialize the "jaas.conf" for keyTab/principal, + // If keyTab is not specified use the Ticket Cache. + // and set the zookeeper login context name. + JaasConfiguration jaasConf = new JaasConfiguration(loginContextName, + keytabFilename, principalName); + javax.security.auth.login.Configuration.setConfiguration(jaasConf); + System.setProperty(loginContextProperty, loginContextName); + } + + /** + * A JAAS configuration that defines the login modules that we want to use for login. + */ + private static class JaasConfiguration extends javax.security.auth.login.Configuration { + private static final String SERVER_KEYTAB_KERBEROS_CONFIG_NAME = + "zookeeper-server-keytab-kerberos"; + private static final String CLIENT_KEYTAB_KERBEROS_CONFIG_NAME = + "zookeeper-client-keytab-kerberos"; + + private static final Maphbase.security.authentication is set to
+ * kerberos.
+ */
public static boolean isSecureZooKeeper(Configuration conf) {
- // TODO: We need a better check for security enabled ZooKeeper. Currently
- // the secure ZooKeeper client is set up using a supplied JaaS
- // configuration file. But if the system property for the JaaS
- // configuration file is set, this may not be an exclusive indication
- // that HBase should set ACLs on znodes. As an alternative, we could do
- // this more like Hadoop and build a JaaS configuration programmatically
- // based on a site conf setting. The scope of such a change will be
- // addressed in HBASE-4791.
- return (System.getProperty("java.security.auth.login.config") != null);
+ // hbase shell need to use:
+ // -Djava.security.auth.login.config=user-jaas.conf
+ // since each user has a different jaas.conf
+ if (System.getProperty("java.security.auth.login.config") != null)
+ return true;
+
+ // Master & RSs uses hbase.zookeeper.client.*
+ return "kerberos".equalsIgnoreCase(conf.get("hbase.security.authentication"));
}
private static ArrayList