HBASE-26616 Refactor code related to ZooKeeper authentication (#3973)
This refactor reduces the size and scope of the `ZKUtil` class. The core of this refactor is moving the `login*` methods from `ZKUtil` into their own class, `ZKAuthentication`. The class `JaasConfiguration` is also moved along with them. Signed-off-by: Andrew Purtell <apurtell@apache.org> Signed-off-by: Duo Zhang <zhangduo@apache.org>
This commit is contained in:
parent
d9fae5cb67
commit
2a42e8ed7a
|
@ -34,7 +34,7 @@ import org.apache.hadoop.hbase.regionserver.HRegionServer;
|
||||||
import org.apache.hadoop.hbase.util.JVMClusterUtil;
|
import org.apache.hadoop.hbase.util.JVMClusterUtil;
|
||||||
import org.apache.hadoop.hbase.util.ServerCommandLine;
|
import org.apache.hadoop.hbase.util.ServerCommandLine;
|
||||||
import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
|
import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
import org.apache.hadoop.hbase.zookeeper.ZKAuthentication;
|
||||||
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
|
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
|
||||||
import org.apache.yetus.audience.InterfaceAudience;
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
|
@ -214,7 +214,7 @@ public class HMasterCommandLine extends ServerCommandLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
// login the zookeeper server principal (if using security)
|
// login the zookeeper server principal (if using security)
|
||||||
ZKUtil.loginServer(conf, HConstants.ZK_SERVER_KEYTAB_FILE,
|
ZKAuthentication.loginServer(conf, HConstants.ZK_SERVER_KEYTAB_FILE,
|
||||||
HConstants.ZK_SERVER_KERBEROS_PRINCIPAL, null);
|
HConstants.ZK_SERVER_KERBEROS_PRINCIPAL, null);
|
||||||
int localZKClusterSessionTimeout =
|
int localZKClusterSessionTimeout =
|
||||||
conf.getInt(HConstants.ZK_SESSION_TIMEOUT + ".localHBaseCluster", 10*1000);
|
conf.getInt(HConstants.ZK_SESSION_TIMEOUT + ".localHBaseCluster", 10*1000);
|
||||||
|
|
|
@ -181,6 +181,7 @@ import org.apache.hadoop.hbase.wal.WAL;
|
||||||
import org.apache.hadoop.hbase.wal.WALFactory;
|
import org.apache.hadoop.hbase.wal.WALFactory;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ClusterStatusTracker;
|
import org.apache.hadoop.hbase.zookeeper.ClusterStatusTracker;
|
||||||
import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
|
import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZKAuthentication;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
|
import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKNodeTracker;
|
import org.apache.hadoop.hbase.zookeeper.ZKNodeTracker;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
||||||
|
@ -639,7 +640,7 @@ public class HRegionServer extends Thread implements
|
||||||
rpcRetryingCallerFactory = RpcRetryingCallerFactory.instantiate(this.conf);
|
rpcRetryingCallerFactory = RpcRetryingCallerFactory.instantiate(this.conf);
|
||||||
|
|
||||||
// login the zookeeper client principal (if using security)
|
// login the zookeeper client principal (if using security)
|
||||||
ZKUtil.loginClient(this.conf, HConstants.ZK_CLIENT_KEYTAB_FILE,
|
ZKAuthentication.loginClient(this.conf, HConstants.ZK_CLIENT_KEYTAB_FILE,
|
||||||
HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL, hostName);
|
HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL, hostName);
|
||||||
// login the server principal (if using secure Hadoop)
|
// login the server principal (if using secure Hadoop)
|
||||||
login(userProvider, hostName);
|
login(userProvider, hostName);
|
||||||
|
|
|
@ -275,7 +275,7 @@ public class TestZooKeeperACL {
|
||||||
@Test
|
@Test
|
||||||
public void testIsZooKeeperSecure() throws Exception {
|
public void testIsZooKeeperSecure() throws Exception {
|
||||||
boolean testJaasConfig =
|
boolean testJaasConfig =
|
||||||
ZKUtil.isSecureZooKeeper(new Configuration(TEST_UTIL.getConfiguration()));
|
ZKAuthentication.isSecureZooKeeper(new Configuration(TEST_UTIL.getConfiguration()));
|
||||||
assertEquals(testJaasConfig, secureZKAvailable);
|
assertEquals(testJaasConfig, secureZKAvailable);
|
||||||
// Define Jaas configuration without ZooKeeper Jaas config
|
// Define Jaas configuration without ZooKeeper Jaas config
|
||||||
File saslConfFile = File.createTempFile("tmp", "fakeJaas.conf");
|
File saslConfFile = File.createTempFile("tmp", "fakeJaas.conf");
|
||||||
|
@ -286,7 +286,8 @@ public class TestZooKeeperACL {
|
||||||
System.setProperty("java.security.auth.login.config",
|
System.setProperty("java.security.auth.login.config",
|
||||||
saslConfFile.getAbsolutePath());
|
saslConfFile.getAbsolutePath());
|
||||||
|
|
||||||
testJaasConfig = ZKUtil.isSecureZooKeeper(new Configuration(TEST_UTIL.getConfiguration()));
|
testJaasConfig = ZKAuthentication.isSecureZooKeeper(
|
||||||
|
new Configuration(TEST_UTIL.getConfiguration()));
|
||||||
assertFalse(testJaasConfig);
|
assertFalse(testJaasConfig);
|
||||||
saslConfFile.delete();
|
saslConfFile.delete();
|
||||||
}
|
}
|
||||||
|
@ -300,13 +301,13 @@ public class TestZooKeeperACL {
|
||||||
javax.security.auth.login.Configuration.setConfiguration(new DummySecurityConfiguration());
|
javax.security.auth.login.Configuration.setConfiguration(new DummySecurityConfiguration());
|
||||||
|
|
||||||
Configuration config = new Configuration(HBaseConfiguration.create());
|
Configuration config = new Configuration(HBaseConfiguration.create());
|
||||||
boolean testJaasConfig = ZKUtil.isSecureZooKeeper(config);
|
boolean testJaasConfig = ZKAuthentication.isSecureZooKeeper(config);
|
||||||
assertFalse(testJaasConfig);
|
assertFalse(testJaasConfig);
|
||||||
|
|
||||||
// Now set authentication scheme to Kerberos still it should return false
|
// Now set authentication scheme to Kerberos still it should return false
|
||||||
// because no configuration set
|
// because no configuration set
|
||||||
config.set("hbase.security.authentication", "kerberos");
|
config.set("hbase.security.authentication", "kerberos");
|
||||||
testJaasConfig = ZKUtil.isSecureZooKeeper(config);
|
testJaasConfig = ZKAuthentication.isSecureZooKeeper(config);
|
||||||
assertFalse(testJaasConfig);
|
assertFalse(testJaasConfig);
|
||||||
|
|
||||||
// Now set programmatic options related to security
|
// Now set programmatic options related to security
|
||||||
|
@ -314,7 +315,7 @@ public class TestZooKeeperACL {
|
||||||
config.set(HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL, "dummy");
|
config.set(HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL, "dummy");
|
||||||
config.set(HConstants.ZK_SERVER_KEYTAB_FILE, "/dummy/file");
|
config.set(HConstants.ZK_SERVER_KEYTAB_FILE, "/dummy/file");
|
||||||
config.set(HConstants.ZK_SERVER_KERBEROS_PRINCIPAL, "dummy");
|
config.set(HConstants.ZK_SERVER_KERBEROS_PRINCIPAL, "dummy");
|
||||||
testJaasConfig = ZKUtil.isSecureZooKeeper(config);
|
testJaasConfig = ZKAuthentication.isSecureZooKeeper(config);
|
||||||
assertTrue(testJaasConfig);
|
assertTrue(testJaasConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ public final class HQuorumPeer {
|
||||||
zkConfig.parseProperties(zkProperties);
|
zkConfig.parseProperties(zkProperties);
|
||||||
|
|
||||||
// login the zookeeper server principal (if using security)
|
// login the zookeeper server principal (if using security)
|
||||||
ZKUtil.loginServer(conf, HConstants.ZK_SERVER_KEYTAB_FILE,
|
ZKAuthentication.loginServer(conf, HConstants.ZK_SERVER_KEYTAB_FILE,
|
||||||
HConstants.ZK_SERVER_KERBEROS_PRINCIPAL,
|
HConstants.ZK_SERVER_KERBEROS_PRINCIPAL,
|
||||||
zkConfig.getClientPortAddress().getHostName());
|
zkConfig.getClientPortAddress().getHostName());
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
package org.apache.hadoop.hbase.zookeeper;
|
package org.apache.hadoop.hbase.zookeeper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.conf.Configured;
|
import org.apache.hadoop.conf.Configured;
|
||||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
|
@ -61,7 +60,7 @@ public class ZKAclReset extends Configured implements Tool {
|
||||||
zk.setACL(znode, ZooDefs.Ids.OPEN_ACL_UNSAFE, -1);
|
zk.setACL(znode, ZooDefs.Ids.OPEN_ACL_UNSAFE, -1);
|
||||||
} else {
|
} else {
|
||||||
LOG.info(" - set ACLs for {}", znode);
|
LOG.info(" - set ACLs for {}", znode);
|
||||||
zk.setACL(znode, ZKUtil.createACL(zkw, znode, true), -1);
|
zk.setACL(znode, zkw.createACL(znode, true), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.hbase.zookeeper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.security.auth.login.AppConfigurationEntry;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
|
import org.apache.hadoop.security.SecurityUtil;
|
||||||
|
import org.apache.hadoop.security.authentication.util.KerberosUtil;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.apache.zookeeper.client.ZooKeeperSaslClient;
|
||||||
|
import org.apache.zookeeper.server.ZooKeeperSaslServer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides ZooKeeper authentication services for both client and server processes.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class ZKAuthentication {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ZKAuthentication.class);
|
||||||
|
|
||||||
|
private ZKAuthentication() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log in the current zookeeper server process using the given configuration
|
||||||
|
* keys for the credential file and login principal.
|
||||||
|
*
|
||||||
|
* <p><strong>This is only applicable when running on secure hbase</strong>
|
||||||
|
* On regular HBase (without security features), this will safely be ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><strong>This is only applicable when running on secure hbase</strong>
|
||||||
|
* On regular HBase (without security features), this will safely be ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><strong>This is only applicable when running on secure hbase</strong>
|
||||||
|
* On regular HBase (without security features), this will safely be ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No keytab specified, no auth
|
||||||
|
String keytabFilename = conf.get(keytabFileKey);
|
||||||
|
if (keytabFilename == null) {
|
||||||
|
LOG.warn("no keytab specified for: {}", keytabFileKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
principalName, keytabFilename);
|
||||||
|
javax.security.auth.login.Configuration.setConfiguration(jaasConf);
|
||||||
|
System.setProperty(loginContextProperty, loginContextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} when secure authentication is enabled
|
||||||
|
* (whether {@code hbase.security.authentication} is set to
|
||||||
|
* "{@code kerberos}").
|
||||||
|
*/
|
||||||
|
public static boolean isSecureZooKeeper(Configuration conf) {
|
||||||
|
// Detection for embedded HBase client with jaas configuration
|
||||||
|
// defined for third party programs.
|
||||||
|
try {
|
||||||
|
javax.security.auth.login.Configuration testConfig =
|
||||||
|
javax.security.auth.login.Configuration.getConfiguration();
|
||||||
|
if (testConfig.getAppConfigurationEntry("Client") == null
|
||||||
|
&& testConfig.getAppConfigurationEntry(
|
||||||
|
JaasConfiguration.CLIENT_KEYTAB_KERBEROS_CONFIG_NAME) == null
|
||||||
|
&& testConfig.getAppConfigurationEntry(
|
||||||
|
JaasConfiguration.SERVER_KEYTAB_KERBEROS_CONFIG_NAME) == null
|
||||||
|
&& conf.get(HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL) == null
|
||||||
|
&& conf.get(HConstants.ZK_SERVER_KERBEROS_PRINCIPAL) == null) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(Exception e) {
|
||||||
|
// No Jaas configuration defined.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Master & RSs uses hbase.zookeeper.client.*
|
||||||
|
return "kerberos".equalsIgnoreCase(conf.get("hbase.security.authentication"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JAAS configuration that defines the login modules that we want to use for ZooKeeper login.
|
||||||
|
*/
|
||||||
|
private static class JaasConfiguration extends javax.security.auth.login.Configuration {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JaasConfiguration.class);
|
||||||
|
|
||||||
|
public static final String SERVER_KEYTAB_KERBEROS_CONFIG_NAME =
|
||||||
|
"zookeeper-server-keytab-kerberos";
|
||||||
|
public static final String CLIENT_KEYTAB_KERBEROS_CONFIG_NAME =
|
||||||
|
"zookeeper-client-keytab-kerberos";
|
||||||
|
|
||||||
|
private static final Map<String, String> BASIC_JAAS_OPTIONS = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
String jaasEnvVar = System.getenv("HBASE_JAAS_DEBUG");
|
||||||
|
if ("true".equalsIgnoreCase(jaasEnvVar)) {
|
||||||
|
BASIC_JAAS_OPTIONS.put("debug", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, String> KEYTAB_KERBEROS_OPTIONS = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("doNotPrompt", "true");
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("storeKey", "true");
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("refreshKrb5Config", "true");
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final AppConfigurationEntry KEYTAB_KERBEROS_LOGIN =
|
||||||
|
new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
|
||||||
|
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, KEYTAB_KERBEROS_OPTIONS);
|
||||||
|
|
||||||
|
private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
|
||||||
|
new AppConfigurationEntry[] { KEYTAB_KERBEROS_LOGIN };
|
||||||
|
|
||||||
|
private javax.security.auth.login.Configuration baseConfig;
|
||||||
|
private final String loginContextName;
|
||||||
|
private final boolean useTicketCache;
|
||||||
|
private final String keytabFile;
|
||||||
|
private final String principal;
|
||||||
|
|
||||||
|
public JaasConfiguration(String loginContextName, String principal, String keytabFile) {
|
||||||
|
this(loginContextName, principal, keytabFile, keytabFile == null || keytabFile.length() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JaasConfiguration(String loginContextName, String principal, String keytabFile,
|
||||||
|
boolean useTicketCache) {
|
||||||
|
try {
|
||||||
|
this.baseConfig = javax.security.auth.login.Configuration.getConfiguration();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
this.baseConfig = null;
|
||||||
|
}
|
||||||
|
this.loginContextName = loginContextName;
|
||||||
|
this.useTicketCache = useTicketCache;
|
||||||
|
this.keytabFile = keytabFile;
|
||||||
|
this.principal = principal;
|
||||||
|
LOG.info(
|
||||||
|
"JaasConfiguration loginContextName={} principal={} useTicketCache={} keytabFile={}",
|
||||||
|
loginContextName, principal, useTicketCache, keytabFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
|
||||||
|
if (loginContextName.equals(appName)) {
|
||||||
|
if (!useTicketCache) {
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("useKeyTab", "true");
|
||||||
|
}
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("principal", principal);
|
||||||
|
KEYTAB_KERBEROS_OPTIONS.put("useTicketCache", useTicketCache ? "true" : "false");
|
||||||
|
return KEYTAB_KERBEROS_CONF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseConfig != null) {
|
||||||
|
return baseConfig.getAppConfigurationEntry(appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,28 +32,19 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.security.auth.login.AppConfigurationEntry;
|
|
||||||
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.hbase.AuthUtil;
|
|
||||||
import org.apache.hadoop.hbase.HConstants;
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
import org.apache.hadoop.hbase.exceptions.DeserializationException;
|
import org.apache.hadoop.hbase.exceptions.DeserializationException;
|
||||||
import org.apache.hadoop.hbase.security.Superusers;
|
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.apache.hadoop.hbase.util.Threads;
|
import org.apache.hadoop.hbase.util.Threads;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.CreateAndFailSilent;
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.CreateAndFailSilent;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.DeleteNodeFailSilent;
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.DeleteNodeFailSilent;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.SetData;
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.SetData;
|
||||||
import org.apache.hadoop.security.SecurityUtil;
|
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
|
||||||
import org.apache.hadoop.security.authentication.util.KerberosUtil;
|
|
||||||
import org.apache.yetus.audience.InterfaceAudience;
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
import org.apache.zookeeper.AsyncCallback;
|
import org.apache.zookeeper.AsyncCallback;
|
||||||
import org.apache.zookeeper.CreateMode;
|
import org.apache.zookeeper.CreateMode;
|
||||||
|
@ -61,22 +52,14 @@ import org.apache.zookeeper.KeeperException;
|
||||||
import org.apache.zookeeper.KeeperException.NoNodeException;
|
import org.apache.zookeeper.KeeperException.NoNodeException;
|
||||||
import org.apache.zookeeper.Op;
|
import org.apache.zookeeper.Op;
|
||||||
import org.apache.zookeeper.Watcher;
|
import org.apache.zookeeper.Watcher;
|
||||||
import org.apache.zookeeper.ZooDefs.Ids;
|
|
||||||
import org.apache.zookeeper.ZooDefs.Perms;
|
|
||||||
import org.apache.zookeeper.ZooKeeper;
|
import org.apache.zookeeper.ZooKeeper;
|
||||||
import org.apache.zookeeper.client.ZooKeeperSaslClient;
|
|
||||||
import org.apache.zookeeper.data.ACL;
|
|
||||||
import org.apache.zookeeper.data.Id;
|
|
||||||
import org.apache.zookeeper.data.Stat;
|
import org.apache.zookeeper.data.Stat;
|
||||||
import org.apache.zookeeper.proto.CreateRequest;
|
import org.apache.zookeeper.proto.CreateRequest;
|
||||||
import org.apache.zookeeper.proto.DeleteRequest;
|
import org.apache.zookeeper.proto.DeleteRequest;
|
||||||
import org.apache.zookeeper.proto.SetDataRequest;
|
import org.apache.zookeeper.proto.SetDataRequest;
|
||||||
import org.apache.zookeeper.server.ZooKeeperSaslServer;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
|
import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
|
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos;
|
||||||
|
|
||||||
|
@ -143,175 +126,6 @@ public final class ZKUtil {
|
||||||
retry, retryIntervalMillis, maxSleepTime, identifier, multiMaxSize);
|
retry, retryIntervalMillis, maxSleepTime, identifier, multiMaxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log in the current zookeeper server process using the given configuration
|
|
||||||
* keys for the credential file and login principal.
|
|
||||||
*
|
|
||||||
* <p><strong>This is only applicable when running on secure hbase</strong>
|
|
||||||
* On regular HBase (without security features), this will safely be ignored.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* <p><strong>This is only applicable when running on secure hbase</strong>
|
|
||||||
* On regular HBase (without security features), this will safely be ignored.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* <p><strong>This is only applicable when running on secure hbase</strong>
|
|
||||||
* On regular HBase (without security features), this will safely be ignored.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No keytab specified, no auth
|
|
||||||
String keytabFilename = conf.get(keytabFileKey);
|
|
||||||
if (keytabFilename == null) {
|
|
||||||
LOG.warn("no keytab specified for: {}", keytabFileKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
principalName, keytabFilename);
|
|
||||||
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 Map<String, String> BASIC_JAAS_OPTIONS = new HashMap<>();
|
|
||||||
static {
|
|
||||||
String jaasEnvVar = System.getenv("HBASE_JAAS_DEBUG");
|
|
||||||
if ("true".equalsIgnoreCase(jaasEnvVar)) {
|
|
||||||
BASIC_JAAS_OPTIONS.put("debug", "true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Map<String,String> KEYTAB_KERBEROS_OPTIONS = new HashMap<>();
|
|
||||||
static {
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("doNotPrompt", "true");
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("storeKey", "true");
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("refreshKrb5Config", "true");
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final AppConfigurationEntry KEYTAB_KERBEROS_LOGIN =
|
|
||||||
new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
|
|
||||||
LoginModuleControlFlag.REQUIRED,
|
|
||||||
KEYTAB_KERBEROS_OPTIONS);
|
|
||||||
|
|
||||||
private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
|
|
||||||
new AppConfigurationEntry[]{KEYTAB_KERBEROS_LOGIN};
|
|
||||||
|
|
||||||
private javax.security.auth.login.Configuration baseConfig;
|
|
||||||
private final String loginContextName;
|
|
||||||
private final boolean useTicketCache;
|
|
||||||
private final String keytabFile;
|
|
||||||
private final String principal;
|
|
||||||
|
|
||||||
public JaasConfiguration(String loginContextName, String principal, String keytabFile) {
|
|
||||||
this(loginContextName, principal, keytabFile, keytabFile == null || keytabFile.length() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JaasConfiguration(String loginContextName, String principal,
|
|
||||||
String keytabFile, boolean useTicketCache) {
|
|
||||||
try {
|
|
||||||
this.baseConfig = javax.security.auth.login.Configuration.getConfiguration();
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
this.baseConfig = null;
|
|
||||||
}
|
|
||||||
this.loginContextName = loginContextName;
|
|
||||||
this.useTicketCache = useTicketCache;
|
|
||||||
this.keytabFile = keytabFile;
|
|
||||||
this.principal = principal;
|
|
||||||
LOG.info("JaasConfiguration loginContextName={} principal={} useTicketCache={} keytabFile={}",
|
|
||||||
loginContextName, principal, useTicketCache, keytabFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
|
|
||||||
if (loginContextName.equals(appName)) {
|
|
||||||
if (!useTicketCache) {
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("useKeyTab", "true");
|
|
||||||
}
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("principal", principal);
|
|
||||||
KEYTAB_KERBEROS_OPTIONS.put("useTicketCache", useTicketCache ? "true" : "false");
|
|
||||||
return KEYTAB_KERBEROS_CONF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseConfig != null) {
|
|
||||||
return baseConfig.getAppConfigurationEntry(appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Helper methods
|
// Helper methods
|
||||||
//
|
//
|
||||||
|
@ -902,86 +716,6 @@ public final class ZKUtil {
|
||||||
setData(zkw, sd.getPath(), sd.getData(), sd.getVersion());
|
setData(zkw, sd.getPath(), sd.getData(), sd.getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether or not secure authentication is enabled
|
|
||||||
* (whether <code>hbase.security.authentication</code> is set to
|
|
||||||
* <code>kerberos</code>.
|
|
||||||
*/
|
|
||||||
public static boolean isSecureZooKeeper(Configuration conf) {
|
|
||||||
// Detection for embedded HBase client with jaas configuration
|
|
||||||
// defined for third party programs.
|
|
||||||
try {
|
|
||||||
javax.security.auth.login.Configuration testConfig =
|
|
||||||
javax.security.auth.login.Configuration.getConfiguration();
|
|
||||||
if (testConfig.getAppConfigurationEntry("Client") == null
|
|
||||||
&& testConfig.getAppConfigurationEntry(
|
|
||||||
JaasConfiguration.CLIENT_KEYTAB_KERBEROS_CONFIG_NAME) == null
|
|
||||||
&& testConfig.getAppConfigurationEntry(
|
|
||||||
JaasConfiguration.SERVER_KEYTAB_KERBEROS_CONFIG_NAME) == null
|
|
||||||
&& conf.get(HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL) == null
|
|
||||||
&& conf.get(HConstants.ZK_SERVER_KERBEROS_PRINCIPAL) == null) {
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
// No Jaas configuration defined.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Master & RSs uses hbase.zookeeper.client.*
|
|
||||||
return "kerberos".equalsIgnoreCase(conf.get("hbase.security.authentication"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ArrayList<ACL> createACL(ZKWatcher zkw, String node) {
|
|
||||||
return createACL(zkw, node, isSecureZooKeeper(zkw.getConfiguration()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ArrayList<ACL> createACL(ZKWatcher zkw, String node,
|
|
||||||
boolean isSecureZooKeeper) {
|
|
||||||
if (!node.startsWith(zkw.getZNodePaths().baseZNode)) {
|
|
||||||
return Ids.OPEN_ACL_UNSAFE;
|
|
||||||
}
|
|
||||||
if (isSecureZooKeeper) {
|
|
||||||
ArrayList<ACL> acls = new ArrayList<>();
|
|
||||||
// add permission to hbase supper user
|
|
||||||
String[] superUsers = zkw.getConfiguration().getStrings(Superusers.SUPERUSER_CONF_KEY);
|
|
||||||
String hbaseUser = null;
|
|
||||||
try {
|
|
||||||
hbaseUser = UserGroupInformation.getCurrentUser().getShortUserName();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.debug("Could not acquire current User.", e);
|
|
||||||
}
|
|
||||||
if (superUsers != null) {
|
|
||||||
List<String> groups = new ArrayList<>();
|
|
||||||
for (String user : superUsers) {
|
|
||||||
if (AuthUtil.isGroupPrincipal(user)) {
|
|
||||||
// TODO: Set node ACL for groups when ZK supports this feature
|
|
||||||
groups.add(user);
|
|
||||||
} else {
|
|
||||||
if(!user.equals(hbaseUser)) {
|
|
||||||
acls.add(new ACL(Perms.ALL, new Id("sasl", user)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!groups.isEmpty()) {
|
|
||||||
LOG.warn("Znode ACL setting for group {} is skipped, ZooKeeper doesn't support this " +
|
|
||||||
"feature presently.", groups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Certain znodes are accessed directly by the client,
|
|
||||||
// so they must be readable by non-authenticated clients
|
|
||||||
if (zkw.getZNodePaths().isClientReadable(node)) {
|
|
||||||
acls.addAll(Ids.CREATOR_ALL_ACL);
|
|
||||||
acls.addAll(Ids.READ_ACL_UNSAFE);
|
|
||||||
} else {
|
|
||||||
acls.addAll(Ids.CREATOR_ALL_ACL);
|
|
||||||
}
|
|
||||||
return acls;
|
|
||||||
} else {
|
|
||||||
return Ids.OPEN_ACL_UNSAFE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Node creation
|
// Node creation
|
||||||
//
|
//
|
||||||
|
@ -1004,12 +738,11 @@ public final class ZKUtil {
|
||||||
* @return true if node created, false if not, watch set in both cases
|
* @return true if node created, false if not, watch set in both cases
|
||||||
* @throws KeeperException if unexpected zookeeper exception
|
* @throws KeeperException if unexpected zookeeper exception
|
||||||
*/
|
*/
|
||||||
public static boolean createEphemeralNodeAndWatch(ZKWatcher zkw,
|
public static boolean createEphemeralNodeAndWatch(ZKWatcher zkw, String znode, byte [] data)
|
||||||
String znode, byte [] data)
|
|
||||||
throws KeeperException {
|
throws KeeperException {
|
||||||
boolean ret = true;
|
boolean ret = true;
|
||||||
try {
|
try {
|
||||||
zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
|
zkw.getRecoverableZooKeeper().create(znode, data, zkw.createACL(znode),
|
||||||
CreateMode.EPHEMERAL);
|
CreateMode.EPHEMERAL);
|
||||||
} catch (KeeperException.NodeExistsException nee) {
|
} catch (KeeperException.NodeExistsException nee) {
|
||||||
ret = false;
|
ret = false;
|
||||||
|
@ -1049,7 +782,7 @@ public final class ZKUtil {
|
||||||
throws KeeperException {
|
throws KeeperException {
|
||||||
boolean ret = true;
|
boolean ret = true;
|
||||||
try {
|
try {
|
||||||
zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
|
zkw.getRecoverableZooKeeper().create(znode, data, zkw.createACL(znode),
|
||||||
CreateMode.PERSISTENT);
|
CreateMode.PERSISTENT);
|
||||||
} catch (KeeperException.NodeExistsException nee) {
|
} catch (KeeperException.NodeExistsException nee) {
|
||||||
ret = false;
|
ret = false;
|
||||||
|
@ -1082,17 +815,14 @@ public final class ZKUtil {
|
||||||
*/
|
*/
|
||||||
public static String createNodeIfNotExistsNoWatch(ZKWatcher zkw, String znode, byte[] data,
|
public static String createNodeIfNotExistsNoWatch(ZKWatcher zkw, String znode, byte[] data,
|
||||||
CreateMode createMode) throws KeeperException {
|
CreateMode createMode) throws KeeperException {
|
||||||
String createdZNode = null;
|
|
||||||
try {
|
try {
|
||||||
createdZNode = zkw.getRecoverableZooKeeper().create(znode, data,
|
return zkw.getRecoverableZooKeeper().create(znode, data, zkw.createACL(znode), createMode);
|
||||||
createACL(zkw, znode), createMode);
|
|
||||||
} catch (KeeperException.NodeExistsException nee) {
|
} catch (KeeperException.NodeExistsException nee) {
|
||||||
return znode;
|
return znode;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
zkw.interruptedException(e);
|
zkw.interruptedException(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return createdZNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1115,8 +845,8 @@ public final class ZKUtil {
|
||||||
String znode, byte [] data)
|
String znode, byte [] data)
|
||||||
throws KeeperException, KeeperException.NodeExistsException {
|
throws KeeperException, KeeperException.NodeExistsException {
|
||||||
try {
|
try {
|
||||||
zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
|
zkw.getRecoverableZooKeeper().create(znode, data, zkw.createACL(znode),
|
||||||
CreateMode.PERSISTENT);
|
CreateMode.PERSISTENT);
|
||||||
Stat stat = zkw.getRecoverableZooKeeper().exists(znode, zkw);
|
Stat stat = zkw.getRecoverableZooKeeper().exists(znode, zkw);
|
||||||
if (stat == null){
|
if (stat == null){
|
||||||
// Likely a race condition. Someone deleted the znode.
|
// Likely a race condition. Someone deleted the znode.
|
||||||
|
@ -1148,7 +878,7 @@ public final class ZKUtil {
|
||||||
String znode, byte [] data, final AsyncCallback.StringCallback cb,
|
String znode, byte [] data, final AsyncCallback.StringCallback cb,
|
||||||
final Object ctx) {
|
final Object ctx) {
|
||||||
zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data,
|
zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data,
|
||||||
createACL(zkw, znode), CreateMode.PERSISTENT, cb, ctx);
|
zkw.createACL(znode), CreateMode.PERSISTENT, cb, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1193,8 +923,9 @@ public final class ZKUtil {
|
||||||
if (zk.exists(znode, false) == null) {
|
if (zk.exists(znode, false) == null) {
|
||||||
zk.create(znode, create.getData(), create.getAcl(), CreateMode.fromFlag(create.getFlags()));
|
zk.create(znode, create.getData(), create.getAcl(), CreateMode.fromFlag(create.getFlags()));
|
||||||
}
|
}
|
||||||
} catch(KeeperException.NodeExistsException nee) {
|
} catch (KeeperException.NodeExistsException nee) {
|
||||||
} catch(KeeperException.NoAuthException nee){
|
// pass
|
||||||
|
} catch (KeeperException.NoAuthException nee) {
|
||||||
try {
|
try {
|
||||||
if (null == zkw.getRecoverableZooKeeper().exists(znode, false)) {
|
if (null == zkw.getRecoverableZooKeeper().exists(znode, false)) {
|
||||||
// If we failed to create the file and it does not already exist.
|
// If we failed to create the file and it does not already exist.
|
||||||
|
@ -1203,7 +934,7 @@ public final class ZKUtil {
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
zkw.interruptedException(ie);
|
zkw.interruptedException(ie);
|
||||||
}
|
}
|
||||||
} catch(InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
zkw.interruptedException(ie);
|
zkw.interruptedException(ie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1243,7 +974,7 @@ public final class ZKUtil {
|
||||||
if(znode == null) {
|
if(znode == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
|
zkw.getRecoverableZooKeeper().create(znode, data, zkw.createACL(znode),
|
||||||
CreateMode.PERSISTENT);
|
CreateMode.PERSISTENT);
|
||||||
} catch(KeeperException.NodeExistsException nee) {
|
} catch(KeeperException.NodeExistsException nee) {
|
||||||
return;
|
return;
|
||||||
|
@ -1746,7 +1477,7 @@ public final class ZKUtil {
|
||||||
|
|
||||||
if (op instanceof CreateAndFailSilent) {
|
if (op instanceof CreateAndFailSilent) {
|
||||||
CreateAndFailSilent cafs = (CreateAndFailSilent)op;
|
CreateAndFailSilent cafs = (CreateAndFailSilent)op;
|
||||||
return Op.create(cafs.getPath(), cafs.getData(), createACL(zkw, cafs.getPath()),
|
return Op.create(cafs.getPath(), cafs.getData(), zkw.createACL(cafs.getPath()),
|
||||||
CreateMode.PERSISTENT);
|
CreateMode.PERSISTENT);
|
||||||
} else if (op instanceof DeleteNodeFailSilent) {
|
} else if (op instanceof DeleteNodeFailSilent) {
|
||||||
DeleteNodeFailSilent dnfs = (DeleteNodeFailSilent)op;
|
DeleteNodeFailSilent dnfs = (DeleteNodeFailSilent)op;
|
||||||
|
|
|
@ -38,7 +38,6 @@ import org.apache.hadoop.hbase.security.Superusers;
|
||||||
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
|
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
|
||||||
import org.apache.hadoop.hbase.util.Threads;
|
import org.apache.hadoop.hbase.util.Threads;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
import org.apache.yetus.audience.InterfaceAudience;
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import org.apache.zookeeper.WatchedEvent;
|
import org.apache.zookeeper.WatchedEvent;
|
||||||
|
@ -50,6 +49,7 @@ import org.apache.zookeeper.data.Id;
|
||||||
import org.apache.zookeeper.data.Stat;
|
import org.apache.zookeeper.data.Stat;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acts as the single ZooKeeper Watcher. One instance of this is instantiated
|
* Acts as the single ZooKeeper Watcher. One instance of this is instantiated
|
||||||
|
@ -68,11 +68,11 @@ public class ZKWatcher implements Watcher, Abortable, Closeable {
|
||||||
|
|
||||||
// Identifier for this watcher (for logging only). It is made of the prefix
|
// Identifier for this watcher (for logging only). It is made of the prefix
|
||||||
// passed on construction and the zookeeper sessionid.
|
// passed on construction and the zookeeper sessionid.
|
||||||
private String prefix;
|
private final String prefix;
|
||||||
private String identifier;
|
private String identifier;
|
||||||
|
|
||||||
// zookeeper quorum
|
// zookeeper quorum
|
||||||
private String quorum;
|
private final String quorum;
|
||||||
|
|
||||||
// zookeeper connection
|
// zookeeper connection
|
||||||
private final RecoverableZooKeeper recoverableZooKeeper;
|
private final RecoverableZooKeeper recoverableZooKeeper;
|
||||||
|
@ -196,6 +196,55 @@ public class ZKWatcher implements Watcher, Abortable, Closeable {
|
||||||
HConstants.ZK_SYNC_BLOCKING_TIMEOUT_DEFAULT_MS);
|
HConstants.ZK_SYNC_BLOCKING_TIMEOUT_DEFAULT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ACL> createACL(String node) {
|
||||||
|
return createACL(node, ZKAuthentication.isSecureZooKeeper(getConfiguration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ACL> createACL(String node, boolean isSecureZooKeeper) {
|
||||||
|
if (!node.startsWith(getZNodePaths().baseZNode)) {
|
||||||
|
return Ids.OPEN_ACL_UNSAFE;
|
||||||
|
}
|
||||||
|
if (isSecureZooKeeper) {
|
||||||
|
ArrayList<ACL> acls = new ArrayList<>();
|
||||||
|
// add permission to hbase supper user
|
||||||
|
String[] superUsers = getConfiguration().getStrings(Superusers.SUPERUSER_CONF_KEY);
|
||||||
|
String hbaseUser = null;
|
||||||
|
try {
|
||||||
|
hbaseUser = UserGroupInformation.getCurrentUser().getShortUserName();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.debug("Could not acquire current User.", e);
|
||||||
|
}
|
||||||
|
if (superUsers != null) {
|
||||||
|
List<String> groups = new ArrayList<>();
|
||||||
|
for (String user : superUsers) {
|
||||||
|
if (AuthUtil.isGroupPrincipal(user)) {
|
||||||
|
// TODO: Set node ACL for groups when ZK supports this feature
|
||||||
|
groups.add(user);
|
||||||
|
} else {
|
||||||
|
if(!user.equals(hbaseUser)) {
|
||||||
|
acls.add(new ACL(Perms.ALL, new Id("sasl", user)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!groups.isEmpty()) {
|
||||||
|
LOG.warn("Znode ACL setting for group {} is skipped, ZooKeeper doesn't support this " +
|
||||||
|
"feature presently.", groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Certain znodes are accessed directly by the client,
|
||||||
|
// so they must be readable by non-authenticated clients
|
||||||
|
if (getZNodePaths().isClientReadable(node)) {
|
||||||
|
acls.addAll(Ids.CREATOR_ALL_ACL);
|
||||||
|
acls.addAll(Ids.READ_ACL_UNSAFE);
|
||||||
|
} else {
|
||||||
|
acls.addAll(Ids.CREATOR_ALL_ACL);
|
||||||
|
}
|
||||||
|
return acls;
|
||||||
|
} else {
|
||||||
|
return Ids.OPEN_ACL_UNSAFE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createBaseZNodes() throws ZooKeeperConnectionException {
|
private void createBaseZNodes() throws ZooKeeperConnectionException {
|
||||||
try {
|
try {
|
||||||
// Create all the necessary "directories" of znodes
|
// Create all the necessary "directories" of znodes
|
||||||
|
@ -219,7 +268,7 @@ public class ZKWatcher implements Watcher, Abortable, Closeable {
|
||||||
* perms.
|
* perms.
|
||||||
*/
|
*/
|
||||||
public void checkAndSetZNodeAcls() {
|
public void checkAndSetZNodeAcls() {
|
||||||
if (!ZKUtil.isSecureZooKeeper(getConfiguration())) {
|
if (!ZKAuthentication.isSecureZooKeeper(getConfiguration())) {
|
||||||
LOG.info("not a secure deployment, proceeding");
|
LOG.info("not a secure deployment, proceeding");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +302,7 @@ public class ZKWatcher implements Watcher, Abortable, Closeable {
|
||||||
for (String child : children) {
|
for (String child : children) {
|
||||||
setZnodeAclsRecursive(ZNodePaths.joinZNode(znode, child));
|
setZnodeAclsRecursive(ZNodePaths.joinZNode(znode, child));
|
||||||
}
|
}
|
||||||
List<ACL> acls = ZKUtil.createACL(this, znode, true);
|
List<ACL> acls = createACL(znode, true);
|
||||||
LOG.info("Setting ACLs for znode:{} , acl:{}", znode, acls);
|
LOG.info("Setting ACLs for znode:{} , acl:{}", znode, acls);
|
||||||
recoverableZooKeeper.setAcl(znode, acls, -1);
|
recoverableZooKeeper.setAcl(znode, acls, -1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class TestZKUtilNoServer {
|
||||||
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1");
|
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1");
|
||||||
String node = "/hbase/testUnsecure";
|
String node = "/hbase/testUnsecure";
|
||||||
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
||||||
List<ACL> aclList = ZKUtil.createACL(watcher, node, false);
|
List<ACL> aclList = watcher.createACL(node, false);
|
||||||
assertEquals(1, aclList.size());
|
assertEquals(1, aclList.size());
|
||||||
assertTrue(aclList.contains(Ids.OPEN_ACL_UNSAFE.iterator().next()));
|
assertTrue(aclList.contains(Ids.OPEN_ACL_UNSAFE.iterator().next()));
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class TestZKUtilNoServer {
|
||||||
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1");
|
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1");
|
||||||
String node = "/hbase/testSecuritySingleSuperuser";
|
String node = "/hbase/testSecuritySingleSuperuser";
|
||||||
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
||||||
List<ACL> aclList = ZKUtil.createACL(watcher, node, true);
|
List<ACL> aclList = watcher.createACL(node, true);
|
||||||
assertEquals(2, aclList.size()); // 1+1, since ACL will be set for the creator by default
|
assertEquals(2, aclList.size()); // 1+1, since ACL will be set for the creator by default
|
||||||
assertTrue(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "user1"))));
|
assertTrue(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "user1"))));
|
||||||
assertTrue(aclList.contains(Ids.CREATOR_ALL_ACL.iterator().next()));
|
assertTrue(aclList.contains(Ids.CREATOR_ALL_ACL.iterator().next()));
|
||||||
|
@ -75,7 +75,7 @@ public class TestZKUtilNoServer {
|
||||||
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1,@group1,user2,@group2,user3");
|
conf.set(Superusers.SUPERUSER_CONF_KEY, "user1,@group1,user2,@group2,user3");
|
||||||
String node = "/hbase/testCreateACL";
|
String node = "/hbase/testCreateACL";
|
||||||
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
||||||
List<ACL> aclList = ZKUtil.createACL(watcher, node, true);
|
List<ACL> aclList = watcher.createACL(node, true);
|
||||||
assertEquals(4, aclList.size()); // 3+1, since ACL will be set for the creator by default
|
assertEquals(4, aclList.size()); // 3+1, since ACL will be set for the creator by default
|
||||||
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group1"))));
|
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group1"))));
|
||||||
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group2"))));
|
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group2"))));
|
||||||
|
@ -91,7 +91,7 @@ public class TestZKUtilNoServer {
|
||||||
UserGroupInformation.setLoginUser(UserGroupInformation.createRemoteUser("user4"));
|
UserGroupInformation.setLoginUser(UserGroupInformation.createRemoteUser("user4"));
|
||||||
String node = "/hbase/testCreateACL";
|
String node = "/hbase/testCreateACL";
|
||||||
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
ZKWatcher watcher = new ZKWatcher(conf, node, null, false);
|
||||||
List<ACL> aclList = ZKUtil.createACL(watcher, node, true);
|
List<ACL> aclList = watcher.createACL(node, true);
|
||||||
assertEquals(3, aclList.size()); // 3, since service user the same as one of superuser
|
assertEquals(3, aclList.size()); // 3, since service user the same as one of superuser
|
||||||
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group1"))));
|
assertFalse(aclList.contains(new ACL(Perms.ALL, new Id("sasl", "@group1"))));
|
||||||
assertTrue(aclList.contains(new ACL(Perms.ALL, new Id("auth", ""))));
|
assertTrue(aclList.contains(new ACL(Perms.ALL, new Id("auth", ""))));
|
||||||
|
|
Loading…
Reference in New Issue