diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java index 9e01d09..927e00d 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java @@ -906,8 +906,8 @@ if (!node.startsWith(zkw.baseZNode)) { return Ids.OPEN_ACL_UNSAFE; } + ArrayList acls = new ArrayList(); if (isSecureZooKeeper) { - ArrayList acls = new ArrayList(); // add permission to hbase supper user String[] superUsers = zkw.getConfiguration().getStrings(Superusers.SUPERUSER_CONF_KEY); if (superUsers != null) { @@ -933,10 +933,24 @@ } else { acls.addAll(Ids.CREATOR_ALL_ACL); } - return acls; } else { - return Ids.OPEN_ACL_UNSAFE; + try { + String zkAclConf = zkw.getConfiguration().get(HConstants.ZOOKEEPER_ACL, HConstants.ZOOKEEPER_ACL_DEFAULT); + zkAclConf = org.apache.hadoop.util.ZKUtil.resolveConfIndirection(zkAclConf); + List zkAcls = org.apache.hadoop.util.ZKUtil.parseACLs(zkAclConf); + if (zkAcls.isEmpty()) { + zkAcls = Ids.CREATOR_ALL_ACL; + } + acls.addAll(zkAcls); + if (zkw.isClientReadable(node)) { + acls.addAll(Ids.READ_ACL_UNSAFE); + } + } catch (IOException e) { + LOG.warn("ACL parse error", e); + return Ids.OPEN_ACL_UNSAFE; + } } + return acls; } // diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java index b665353..35eceb2 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java @@ -21,6 +21,7 @@ import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -40,11 +41,13 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.security.Superusers; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.util.ZKUtil.ZKAuthInfo; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; 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.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; @@ -175,6 +178,22 @@ this.abortable = abortable; setNodeNames(conf); this.recoverableZooKeeper = ZKUtil.connect(conf, quorum, this, identifier); + + if(!ZKUtil.isSecureZooKeeper(conf)){ + ZooKeeper zkClient = recoverableZooKeeper.getZooKeeper(); + String zkAuthConf = conf.get(HConstants.ZOOKEEPER_AUTH); + zkAuthConf = org.apache.hadoop.util.ZKUtil.resolveConfIndirection(zkAuthConf); + List zkAuths; + if (zkAuthConf != null) { + zkAuths = org.apache.hadoop.util.ZKUtil.parseAuth(zkAuthConf); + } else { + zkAuths = Collections.emptyList(); + } + for(ZKAuthInfo auth : zkAuths){ + zkClient.addAuthInfo(auth.getScheme(), auth.getAuth()); + } + } + if (canCreateBaseZNode) { createBaseZNodes(); } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java index 0c6244f..11d6a0b 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java @@ -171,6 +171,11 @@ /** Name of ZooKeeper quorum configuration parameter. */ public static final String ZOOKEEPER_QUORUM = "hbase.zookeeper.quorum"; + public static final String ZOOKEEPER_ACL = "hbase.zookeeper.acl"; + public static final String ZOOKEEPER_ACL_DEFAULT = "world:anyone:rwcda"; + + public static final String ZOOKEEPER_AUTH = "hbase.zookeeper.auth"; + /** Common prefix of ZooKeeper configuration properties */ public static final String ZK_CFG_PROPERTY_PREFIX = "hbase.zookeeper.property."; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/zookeeper/TestSimpleZKACL.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/zookeeper/TestSimpleZKACL.java new file mode 100644 index 0000000..b3e985e --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/zookeeper/TestSimpleZKACL.java @@ -0,0 +1,129 @@ +/** + * 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.util.List; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.*; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.zookeeper.ZooDefs.Perms; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.data.Stat; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(MediumTests.class) +public class TestSimpleZKACL { + private final static HBaseTestingUtility TEST_UTIL = + new HBaseTestingUtility(); + private static ZooKeeperWatcher zkw; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true); + TEST_UTIL.getConfiguration().setInt("hbase.zookeeper.property.maxClientCnxns", 1000); + TEST_UTIL.getConfiguration().set(HConstants.ZOOKEEPER_ACL, "digest:admin:0DPiKuNIrrVmD8IUCuw1hQxNqZc=:cdrwa"); + TEST_UTIL.getConfiguration().set(HConstants.ZOOKEEPER_AUTH, "digest:admin"); + TEST_UTIL.getConfiguration().set("hbase.security.authentication", "simple"); + TEST_UTIL.startMiniCluster(); + zkw = new ZooKeeperWatcher(new Configuration(TEST_UTIL.getConfiguration()), + TestSimpleZKACL.class.getName(), null); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + @Before + public void setUp() throws Exception { + TEST_UTIL.ensureSomeRegionServersAvailable(2); + } + + /** + * if the ZK's node is below, all the client can read the node, but only the + * server(Regionserver & Master which has the auth info) can modify it. + *

+ * /hbase
+ * /hbase/meta-region-server
+ * /hbase/master
+ * /hbase/hbaseid
+ * /hbase/rs
+ * /hbase/table
+ * /hbase/table/$tableName
+ *

+ * @throws Exception + */ + @Test + public void testClientReadableZNodeACL() throws Exception { + ZooKeeper zkClient = zkw.getRecoverableZooKeeper().getZooKeeper(); + String[] nodes = new String[]{ + "/hbase", + "/hbase/meta-region-server", + "/hbase/master", + "/hbase/hbaseid", + "/hbase/rs", + "/hbase/table", + "/hbase/table/hbase:meta" + }; + List acls; + for(String node : nodes){ + acls = zkClient.getACL(node, new Stat()); + Assert.assertEquals(acls.size(), 2); + Assert.assertTrue(acls.contains( + new ACL(Perms.ALL, new Id("digest", "admin:0DPiKuNIrrVmD8IUCuw1hQxNqZc="))) + && acls.contains(new ACL(Perms.READ, new Id("world", "anyone")))); + } + } + + /** + * otherwise, only the server can read and modify the node, the Clients can't see them if they are not authed + * @throws Exception + */ + @Test + public void testClientNoReadZNodeACL() throws Exception { + ZooKeeper zkClient = zkw.getRecoverableZooKeeper().getZooKeeper(); + String node = "/hbase/table-lock"; + List acls = zkClient.getACL(node, new Stat()); + Assert.assertEquals(acls.size(), 1); + Assert.assertTrue(acls.contains(new ACL(Perms.ALL, + new Id("digest", "admin:0DPiKuNIrrVmD8IUCuw1hQxNqZc=")))); + int version = zkClient.exists(node, false).getVersion(); + zkClient.setData(node, "testdata".getBytes(), version); + } + + /** + * Finally, we check the ACLs of a node outside of the /hbase hierarchy and + * verify that its ACL is simply 'hbase:Perms.ALL'. + */ + @Test + public void testOutsideHBaseNodeACL() throws Exception { + ZKUtil.createWithParents(zkw, "/testACLNode"); + List acls = zkw.getRecoverableZooKeeper().getZooKeeper() + .getACL("/testACLNode", new Stat()); + Assert.assertEquals(acls.size(), 1); + Assert.assertTrue(acls.contains(new ACL(Perms.ALL, + new Id("world", "anyone")))); + } +}