You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2011/11/20 20:42:31 UTC

svn commit: r1204226 - in /hbase/branches/0.92: ./ src/main/java/org/apache/hadoop/hbase/zookeeper/ src/test/java/org/apache/hadoop/hbase/ src/test/java/org/apache/hadoop/hbase/zookeeper/

Author: apurtell
Date: Sun Nov 20 19:42:30 2011
New Revision: 1204226

URL: http://svn.apache.org/viewvc?rev=1204226&view=rev
Log:
HBASE-2418 Support for ZooKeeper authentication

Added:
    hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java
Modified:
    hbase/branches/0.92/CHANGES.txt
    hbase/branches/0.92/pom.xml
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
    hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java

Modified: hbase/branches/0.92/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/CHANGES.txt?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/CHANGES.txt (original)
+++ hbase/branches/0.92/CHANGES.txt Sun Nov 20 19:42:30 2011
@@ -807,6 +807,7 @@ Release 0.92.0 - Unreleased
                (Jonathan Hsieh via todd)
    HBASE-2742  Provide strong authentication with a secure RPC engine         
    HBASE-3025  Coprocessor based access control
+   HBASE-2418  Support for ZooKeeper authentication
 
 Release 0.90.5 - Unreleased
 

Modified: hbase/branches/0.92/pom.xml
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/pom.xml?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/pom.xml (original)
+++ hbase/branches/0.92/pom.xml Sun Nov 20 19:42:30 2011
@@ -788,7 +788,7 @@
     <slf4j.version>1.5.8</slf4j.version><!-- newer version available -->
     <stax-api.version>1.0.1</stax-api.version>
     <thrift.version>0.7.0</thrift.version>
-    <zookeeper.version>3.3.3</zookeeper.version>
+    <zookeeper.version>3.4.0-SNAPSHOT</zookeeper.version>
     <hadoop-snappy.version>0.0.1-SNAPSHOT</hadoop-snappy.version>
 
     <package.prefix>/usr</package.prefix>	           
@@ -1373,6 +1373,9 @@
     <!-- profile for building against Hadoop 0.20+security-->
     <profile>
       <id>security</id>
+      <properties>
+        <hadoop.version>0.20.205.1-7070-SNAPSHOT</hadoop.version>
+      </properties>
       <build>
         <finalName>${artifactId}-${version}-security</finalName>
         <plugins>

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java Sun Nov 20 19:42:30 2011
@@ -33,8 +33,10 @@ import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileUtil;
-import org.apache.zookeeper.server.NIOServerCnxn;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.zookeeper.server.NIOServerCnxnFactory;
 import org.apache.zookeeper.server.ZooKeeperServer;
 import org.apache.zookeeper.server.persistence.FileTxnLog;
 
@@ -54,20 +56,25 @@ public class MiniZooKeeperCluster {
   private int defaultClientPort = 21818; // use non-standard port
   private int clientPort = defaultClientPort; 
   
-  private List<NIOServerCnxn.Factory> standaloneServerFactoryList;
+  private List<NIOServerCnxnFactory> standaloneServerFactoryList;
   private List<ZooKeeperServer> zooKeeperServers;
   private List<Integer> clientPortList;
   
   private int activeZKServerIndex;
   private int tickTime = 0;
+  private Configuration configuration;
 
-  /** Create mini ZooKeeper cluster. */
   public MiniZooKeeperCluster() {
+    this(new Configuration());
+  }
+
+  public MiniZooKeeperCluster(Configuration configuration) {
     this.started = false;
+    this.configuration = configuration;
     activeZKServerIndex = -1;
     zooKeeperServers = new ArrayList<ZooKeeperServer>();
     clientPortList = new ArrayList<Integer>();
-    standaloneServerFactoryList = new ArrayList<NIOServerCnxn.Factory>();
+    standaloneServerFactoryList = new ArrayList<NIOServerCnxnFactory>();
   }
 
   public void setDefaultClientPort(int clientPort) {
@@ -132,11 +139,14 @@ public class MiniZooKeeperCluster {
         tickTimeToUse = TICK_TIME;
       }
       ZooKeeperServer server = new ZooKeeperServer(dir, dir, tickTimeToUse);    
-      NIOServerCnxn.Factory standaloneServerFactory;
+      NIOServerCnxnFactory standaloneServerFactory;
       while (true) {
         try {
-          standaloneServerFactory =
-            new NIOServerCnxn.Factory(new InetSocketAddress(clientPort));
+          standaloneServerFactory = new NIOServerCnxnFactory();
+          standaloneServerFactory.configure(
+            new InetSocketAddress(clientPort),
+            configuration.getInt(HConstants.ZOOKEEPER_MAX_CLIENT_CNXNS,
+              1000));
         } catch (BindException e) {
           LOG.info("Failed binding ZK Server to client port: " + clientPort);
           //this port is already in use. try to use another
@@ -186,7 +196,7 @@ public class MiniZooKeeperCluster {
     }
     // shut down all the zk servers
     for (int i = 0; i < standaloneServerFactoryList.size(); i++) {
-      NIOServerCnxn.Factory standaloneServerFactory = 
+      NIOServerCnxnFactory standaloneServerFactory =
         standaloneServerFactoryList.get(i);      
       int clientPort = clientPortList.get(i);
       
@@ -218,7 +228,7 @@ public class MiniZooKeeperCluster {
     }
     
     // Shutdown the current active one
-    NIOServerCnxn.Factory standaloneServerFactory = 
+    NIOServerCnxnFactory standaloneServerFactory =
       standaloneServerFactoryList.get(activeZKServerIndex);
     int clientPort = clientPortList.get(activeZKServerIndex);
     
@@ -259,7 +269,7 @@ public class MiniZooKeeperCluster {
     
     int backupZKServerIndex = activeZKServerIndex+1;
     // Shutdown the current active one
-    NIOServerCnxn.Factory standaloneServerFactory = 
+    NIOServerCnxnFactory standaloneServerFactory =
       standaloneServerFactoryList.get(backupZKServerIndex);
     int clientPort = clientPortList.get(backupZKServerIndex);
     

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java Sun Nov 20 19:42:30 2011
@@ -40,9 +40,9 @@ import org.apache.zookeeper.AsyncCallbac
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.ZooKeeper;
 import org.apache.zookeeper.KeeperException.NoNodeException;
 import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
 
 /**
@@ -696,6 +696,41 @@ public class ZKUtil {
     setData(zkw, znode, data, -1);
   }
 
+  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);
+  }
+
+  private static ArrayList<ACL> createACL(ZooKeeperWatcher zkw, String node) {
+    if (isSecureZooKeeper(zkw.getConfiguration())) {
+      // Certain znodes must be readable by non-authenticated clients
+      if ((node.equals(zkw.rootServerZNode) == true) ||
+          (node.equals(zkw.masterAddressZNode) == true) ||
+          (node.equals(zkw.clusterIdZNode) == true)) {
+        return ZooKeeperWatcher.CREATOR_ALL_AND_WORLD_READABLE;
+      }
+      return Ids.CREATOR_ALL_ACL;
+    } else {
+      return Ids.OPEN_ACL_UNSAFE;
+    }
+  }
+
+  public static void waitForZKConnectionIfAuthenticating(ZooKeeperWatcher zkw)
+      throws InterruptedException {
+    if (isSecureZooKeeper(zkw.getConfiguration())) {
+       LOG.debug("Waiting for ZooKeeperWatcher to authenticate");
+       zkw.saslLatch.await();
+       LOG.debug("Done waiting.");
+    }
+  }
+
   //
   // Node creation
   //
@@ -722,7 +757,8 @@ public class ZKUtil {
       String znode, byte [] data)
   throws KeeperException {
     try {
-      zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+      waitForZKConnectionIfAuthenticating(zkw);
+      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
           CreateMode.EPHEMERAL);
     } catch (KeeperException.NodeExistsException nee) {
       if(!watchAndCheckExists(zkw, znode)) {
@@ -761,7 +797,8 @@ public class ZKUtil {
       ZooKeeperWatcher zkw, String znode, byte [] data)
   throws KeeperException {
     try {
-      zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+      waitForZKConnectionIfAuthenticating(zkw);
+      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
           CreateMode.PERSISTENT);
     } catch (KeeperException.NodeExistsException nee) {
       try {
@@ -798,7 +835,8 @@ public class ZKUtil {
       String znode, byte [] data)
   throws KeeperException, KeeperException.NodeExistsException {
     try {
-      zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+      waitForZKConnectionIfAuthenticating(zkw);
+      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
           CreateMode.PERSISTENT);
       return zkw.getRecoverableZooKeeper().exists(znode, zkw).getVersion();
     } catch (InterruptedException e) {
@@ -825,8 +863,13 @@ public class ZKUtil {
   public static void asyncCreate(ZooKeeperWatcher zkw,
       String znode, byte [] data, final AsyncCallback.StringCallback cb,
       final Object ctx) {
-    zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
-       CreateMode.PERSISTENT, cb, ctx);
+    try {
+      waitForZKConnectionIfAuthenticating(zkw);
+      zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data,
+          createACL(zkw, znode), CreateMode.PERSISTENT, cb, ctx);
+    } catch (InterruptedException e) {
+      zkw.interruptedException(e);
+    }
   }
 
   /**
@@ -844,8 +887,9 @@ public class ZKUtil {
   throws KeeperException {
     try {
       RecoverableZooKeeper zk = zkw.getRecoverableZooKeeper();
+      waitForZKConnectionIfAuthenticating(zkw);
       if (zk.exists(znode, false) == null) {
-        zk.create(znode, new byte[0], Ids.OPEN_ACL_UNSAFE,
+        zk.create(znode, new byte[0], createACL(zkw,znode),
             CreateMode.PERSISTENT);
       }
     } catch(KeeperException.NodeExistsException nee) {
@@ -881,7 +925,8 @@ public class ZKUtil {
       if(znode == null) {
         return;
       }
-      zkw.getRecoverableZooKeeper().create(znode, new byte[0], Ids.OPEN_ACL_UNSAFE,
+      waitForZKConnectionIfAuthenticating(zkw);
+      zkw.getRecoverableZooKeeper().create(znode, new byte[0], createACL(zkw, znode),
           CreateMode.PERSISTENT);
     } catch(KeeperException.NodeExistsException nee) {
       return;

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java Sun Nov 20 19:42:30 2011
@@ -20,8 +20,12 @@
 package org.apache.hadoop.hbase.zookeeper;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -33,6 +37,8 @@ import org.apache.hadoop.hbase.util.Thre
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
 
 /**
  * Acts as the single ZooKeeper Watcher.  One instance of this is instantiated
@@ -48,7 +54,7 @@ import org.apache.zookeeper.Watcher;
 public class ZooKeeperWatcher implements Watcher, Abortable {
   private static final Log LOG = LogFactory.getLog(ZooKeeperWatcher.class);
 
-  // Identifiier for this watcher (for logging only).  Its made of the prefix
+  // Identifier for this watcher (for logging only).  It is made of the prefix
   // passed on construction and the zookeeper sessionid.
   private String identifier;
 
@@ -65,6 +71,13 @@ public class ZooKeeperWatcher implements
   private final List<ZooKeeperListener> listeners =
     new CopyOnWriteArrayList<ZooKeeperListener>();
 
+  // Used by ZKUtil:waitForZKConnectionIfAuthenticating to wait for SASL
+  // negotiation to complete
+  public CountDownLatch saslLatch = new CountDownLatch(1);
+
+  // set of unassigned nodes watched
+  private Set<String> unassignedNodes = new HashSet<String>();
+
   // node names
 
   // base znode for this cluster
@@ -88,11 +101,17 @@ public class ZooKeeperWatcher implements
   // znode used for log splitting work assignment
   public String splitLogZNode;
 
+  // Certain ZooKeeper nodes need to be world-readable
+  public static final ArrayList<ACL> CREATOR_ALL_AND_WORLD_READABLE =
+    new ArrayList<ACL>() { {
+      add(new ACL(ZooDefs.Perms.READ,ZooDefs.Ids.ANYONE_ID_UNSAFE));
+      add(new ACL(ZooDefs.Perms.ALL,ZooDefs.Ids.AUTH_IDS));
+    }};
+
   private final Configuration conf;
 
   private final Exception constructorCaller;
 
-
   /**
    * Instantiate a ZooKeeper connection and watcher.
    * @param descriptor Descriptive string that is added to zookeeper sessionid
@@ -315,17 +334,38 @@ public class ZooKeeperWatcher implements
         LOG.debug(this.identifier + " connected");
         break;
 
+      case SaslAuthenticated:
+        if (ZKUtil.isSecureZooKeeper(this.conf)) {
+          // We are authenticated, clients can proceed.
+          saslLatch.countDown();
+        }
+        break;
+
+      case AuthFailed:
+        if (ZKUtil.isSecureZooKeeper(this.conf)) {
+          // We could not be authenticated, but clients should proceed anyway.
+          // Only access to znodes that require SASL authentication will be
+          // denied. The client may never need to access them.
+          saslLatch.countDown();
+        }
+        break;
+
       // Abort the server if Disconnected or Expired
-      // TODO: Åny reason to handle these two differently?
       case Disconnected:
         LOG.debug(prefix("Received Disconnected from ZooKeeper, ignoring"));
         break;
 
       case Expired:
+        if (ZKUtil.isSecureZooKeeper(this.conf)) {
+          // We consider Expired equivalent to AuthFailed for this
+          // connection. Authentication is never going to complete. The
+          // client should proceed to do cleanup.
+          saslLatch.countDown();
+        }
         String msg = prefix(this.identifier + " received expired from " +
           "ZooKeeper, aborting");
         // TODO: One thought is to add call to ZooKeeperListener so say,
-        // ZooKeperNodeTracker can zero out its data values.
+        // ZooKeeperNodeTracker can zero out its data values.
         if (this.abortable != null) this.abortable.abort(msg,
             new KeeperException.SessionExpiredException());
         break;
@@ -396,6 +436,10 @@ public class ZooKeeperWatcher implements
     }
   }
 
+  public Configuration getConfiguration() {
+    return conf;
+  }
+
   @Override
   public void abort(String why, Throwable e) {
     this.abortable.abort(why, e);

Modified: hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java?rev=1204226&r1=1204225&r2=1204226&view=diff
==============================================================================
--- hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (original)
+++ hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java Sun Nov 20 19:42:30 2011
@@ -391,7 +391,7 @@ public class HBaseTestingUtility {
       throw new IOException("Cluster already running at " + dir);
     }
     this.passedZkCluster = false;
-    this.zkCluster = new MiniZooKeeperCluster();
+    this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
     int clientPort =   this.zkCluster.startup(dir,zooKeeperServerNum);
     this.conf.set("hbase.zookeeper.property.clientPort",
       Integer.toString(clientPort));

Added: hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java?rev=1204226&view=auto
==============================================================================
--- hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java (added)
+++ hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java Sun Nov 20 19:42:30 2011
@@ -0,0 +1,266 @@
+/**
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.TestZooKeeper;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestZooKeeperACL {
+  private final static Log LOG = LogFactory.getLog(TestZooKeeperACL.class);
+  private final static HBaseTestingUtility TEST_UTIL =
+      new HBaseTestingUtility();
+
+  private static ZooKeeperWatcher zkw;
+  private static boolean secureZKAvailable;
+  
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    File saslConfFile = File.createTempFile("tmp", "jaas.conf");
+    FileWriter fwriter = new FileWriter(saslConfFile);
+
+    fwriter.write("" +
+      "Server {\n" +
+        "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+        "user_hbase=\"secret\";\n" +
+      "};\n" +
+      "Client {\n" +
+        "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+        "username=\"hbase\"\n" +
+        "password=\"secret\";\n" +
+      "};" + "\n");
+    fwriter.close();
+    System.setProperty("java.security.auth.login.config",
+        saslConfFile.getAbsolutePath());
+    System.setProperty("zookeeper.authProvider.1",
+        "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+
+    TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true);
+    TEST_UTIL.getConfiguration().setInt("hbase.zookeeper.property.maxClientCnxns", 1000);
+
+    // If Hadoop is missing HADOOP-7070 the cluster will fail to start due to
+    // the JAAS configuration required by ZK being clobbered by Hadoop 
+    try {
+      TEST_UTIL.startMiniCluster();
+    } catch (IOException e) {
+      LOG.warn("Hadoop is missing HADOOP-7070", e);
+      secureZKAvailable = false;
+      return;
+    }
+    zkw = new ZooKeeperWatcher(
+      new Configuration(TEST_UTIL.getConfiguration()),
+        TestZooKeeper.class.getName(), null);
+    ZKUtil.waitForZKConnectionIfAuthenticating(zkw);
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+    TEST_UTIL.ensureSomeRegionServersAvailable(2);
+  }
+
+  /**
+   * Create a node and check its ACL. When authentication is enabled on 
+   * Zookeeper, all nodes (except /hbase/root-region-server, /hbase/master
+   * and /hbase/hbaseid) should be created so that only the hbase server user
+   * (master or region server user) that created them can access them, and
+   * this user should have all permissions on this node. For
+   * /hbase/root-region-server, /hbase/master, and /hbase/hbaseid the
+   * permissions should be as above, but should also be world-readable. First
+   * we check the general case of /hbase nodes in the following test, and
+   * then check the subset of world-readable nodes in the three tests after
+   * that.
+   */
+  @Test (timeout=30000)
+  public void testHBaseRootZNodeACL() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+
+    List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+        .getACL("/hbase", new Stat());
+    assertEquals(acls.size(),1);
+    assertEquals(acls.get(0).getId().getScheme(),"sasl");
+    assertEquals(acls.get(0).getId().getId(),"hbase");
+    assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
+  }
+
+  /**
+   * When authentication is enabled on Zookeeper, /hbase/root-region-server
+   * should be created with 2 ACLs: one specifies that the hbase user has
+   * full access to the node; the other, that it is world-readable.
+   */
+  @Test (timeout=30000)
+  public void testHBaseRootRegionServerZNodeACL() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+
+    List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+        .getACL("/hbase/root-region-server", new Stat());
+    assertEquals(acls.size(),2);
+
+    boolean foundWorldReadableAcl = false;
+    boolean foundHBaseOwnerAcl = false;
+    for(int i = 0; i < 2; i++) {
+      if (acls.get(i).getId().getScheme().equals("world") == true) {
+        assertEquals(acls.get(0).getId().getId(),"anyone");
+        assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+        foundWorldReadableAcl = true;
+      }
+      else {
+        if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+          assertEquals(acls.get(1).getId().getId(),"hbase");
+          assertEquals(acls.get(1).getId().getScheme(),"sasl");
+          foundHBaseOwnerAcl = true;
+        } else { // error: should not get here: test fails.
+          assertTrue(false);
+        }
+      }
+    }
+    assertTrue(foundWorldReadableAcl);
+    assertTrue(foundHBaseOwnerAcl);
+  }
+
+  /**
+   * When authentication is enabled on Zookeeper, /hbase/master should be
+   * created with 2 ACLs: one specifies that the hbase user has full access
+   * to the node; the other, that it is world-readable.
+   */
+  @Test (timeout=30000)
+  public void testHBaseMasterServerZNodeACL() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+
+    List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+        .getACL("/hbase/master", new Stat());
+    assertEquals(acls.size(),2);
+
+    boolean foundWorldReadableAcl = false;
+    boolean foundHBaseOwnerAcl = false;
+    for(int i = 0; i < 2; i++) {
+      if (acls.get(i).getId().getScheme().equals("world") == true) {
+        assertEquals(acls.get(0).getId().getId(),"anyone");
+        assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+        foundWorldReadableAcl = true;
+      } else {
+        if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+          assertEquals(acls.get(1).getId().getId(),"hbase");
+          assertEquals(acls.get(1).getId().getScheme(),"sasl");
+          foundHBaseOwnerAcl = true;
+        } else { // error: should not get here: test fails.
+          assertTrue(false);
+        }
+      }
+    }
+    assertTrue(foundWorldReadableAcl);
+    assertTrue(foundHBaseOwnerAcl);
+  }
+
+  /**
+   * When authentication is enabled on Zookeeper, /hbase/hbaseid should be
+   * created with 2 ACLs: one specifies that the hbase user has full access
+   * to the node; the other, that it is world-readable.
+   */
+  @Test (timeout=30000)
+  public void testHBaseIDZNodeACL() throws Exception {
+    if (!secureZKAvailable) {
+      return;
+    }
+
+    List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+        .getACL("/hbase/hbaseid", new Stat());
+    assertEquals(acls.size(),2);
+
+    boolean foundWorldReadableAcl = false;
+    boolean foundHBaseOwnerAcl = false;
+    for(int i = 0; i < 2; i++) {
+      if (acls.get(i).getId().getScheme().equals("world") == true) {
+        assertEquals(acls.get(0).getId().getId(),"anyone");
+        assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+        foundWorldReadableAcl = true;
+      } else {
+        if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+          assertEquals(acls.get(1).getId().getId(),"hbase");
+          assertEquals(acls.get(1).getId().getScheme(),"sasl");
+          foundHBaseOwnerAcl = true;
+        } else { // error: should not get here: test fails.
+          assertTrue(false);
+        }
+      }
+    }
+    assertTrue(foundWorldReadableAcl);
+    assertTrue(foundHBaseOwnerAcl);
+  }
+
+  /**
+   * 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 {
+    if (!secureZKAvailable) {
+      return;
+    }
+
+    ZKUtil.createWithParents(zkw, "/testACLNode");
+    List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+        .getACL("/testACLNode", new Stat());
+    assertEquals(acls.size(),1);
+    assertEquals(acls.get(0).getId().getScheme(),"sasl");
+    assertEquals(acls.get(0).getId().getId(),"hbase");
+    assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
+  }
+}