View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.zookeeper;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertTrue;
22  
23  import java.io.File;
24  import java.io.FileWriter;
25  import java.io.IOException;
26  import java.util.List;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.*;
32  import org.apache.hadoop.hbase.testclassification.MediumTests;
33  import org.apache.zookeeper.ZooDefs;
34  import org.apache.zookeeper.data.ACL;
35  import org.apache.zookeeper.data.Stat;
36  
37  import org.junit.AfterClass;
38  import org.junit.Before;
39  import org.junit.BeforeClass;
40  import org.junit.Test;
41  import org.junit.experimental.categories.Category;
42  
43  @Category(MediumTests.class)
44  public class TestZooKeeperACL {
45    private final static Log LOG = LogFactory.getLog(TestZooKeeperACL.class);
46    private final static HBaseTestingUtility TEST_UTIL =
47        new HBaseTestingUtility();
48  
49    private static ZooKeeperWatcher zkw;
50    private static boolean secureZKAvailable;
51    
52    @BeforeClass
53    public static void setUpBeforeClass() throws Exception {
54      File saslConfFile = File.createTempFile("tmp", "jaas.conf");
55      FileWriter fwriter = new FileWriter(saslConfFile);
56  
57      fwriter.write("" +
58        "Server {\n" +
59          "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
60          "user_hbase=\"secret\";\n" +
61        "};\n" +
62        "Client {\n" +
63          "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
64          "username=\"hbase\"\n" +
65          "password=\"secret\";\n" +
66        "};" + "\n");
67      fwriter.close();
68      System.setProperty("java.security.auth.login.config",
69          saslConfFile.getAbsolutePath());
70      System.setProperty("zookeeper.authProvider.1",
71          "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
72  
73      TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true);
74      TEST_UTIL.getConfiguration().setInt("hbase.zookeeper.property.maxClientCnxns", 1000);
75  
76      // If Hadoop is missing HADOOP-7070 the cluster will fail to start due to
77      // the JAAS configuration required by ZK being clobbered by Hadoop 
78      try {
79        TEST_UTIL.startMiniCluster();
80      } catch (IOException e) {
81        LOG.warn("Hadoop is missing HADOOP-7070", e);
82        secureZKAvailable = false;
83        return;
84      }
85      zkw = new ZooKeeperWatcher(
86        new Configuration(TEST_UTIL.getConfiguration()),
87          TestZooKeeper.class.getName(), null);
88    }
89  
90    /**
91     * @throws java.lang.Exception
92     */
93    @AfterClass
94    public static void tearDownAfterClass() throws Exception {
95      if (!secureZKAvailable) {
96        return;
97      }
98      TEST_UTIL.shutdownMiniCluster();
99    }
100 
101   /**
102    * @throws java.lang.Exception
103    */
104   @Before
105   public void setUp() throws Exception {
106     if (!secureZKAvailable) {
107       return;
108     }
109     TEST_UTIL.ensureSomeRegionServersAvailable(2);
110   }
111 
112   /**
113    * Create a node and check its ACL. When authentication is enabled on 
114    * Zookeeper, all nodes (except /hbase/root-region-server, /hbase/master
115    * and /hbase/hbaseid) should be created so that only the hbase server user
116    * (master or region server user) that created them can access them, and
117    * this user should have all permissions on this node. For
118    * /hbase/root-region-server, /hbase/master, and /hbase/hbaseid the
119    * permissions should be as above, but should also be world-readable. First
120    * we check the general case of /hbase nodes in the following test, and
121    * then check the subset of world-readable nodes in the three tests after
122    * that.
123    */
124   @Test (timeout=30000)
125   public void testHBaseRootZNodeACL() throws Exception {
126     if (!secureZKAvailable) {
127       return;
128     }
129 
130     List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
131         .getACL("/hbase", new Stat());
132     assertEquals(acls.size(),1);
133     assertEquals(acls.get(0).getId().getScheme(),"sasl");
134     assertEquals(acls.get(0).getId().getId(),"hbase");
135     assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
136   }
137 
138   /**
139    * When authentication is enabled on Zookeeper, /hbase/root-region-server
140    * should be created with 2 ACLs: one specifies that the hbase user has
141    * full access to the node; the other, that it is world-readable.
142    */
143   @Test (timeout=30000)
144   public void testHBaseRootRegionServerZNodeACL() throws Exception {
145     if (!secureZKAvailable) {
146       return;
147     }
148 
149     List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
150         .getACL("/hbase/root-region-server", new Stat());
151     assertEquals(acls.size(),2);
152 
153     boolean foundWorldReadableAcl = false;
154     boolean foundHBaseOwnerAcl = false;
155     for(int i = 0; i < 2; i++) {
156       if (acls.get(i).getId().getScheme().equals("world") == true) {
157         assertEquals(acls.get(0).getId().getId(),"anyone");
158         assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
159         foundWorldReadableAcl = true;
160       }
161       else {
162         if (acls.get(i).getId().getScheme().equals("sasl") == true) {
163           assertEquals(acls.get(1).getId().getId(),"hbase");
164           assertEquals(acls.get(1).getId().getScheme(),"sasl");
165           foundHBaseOwnerAcl = true;
166         } else { // error: should not get here: test fails.
167           assertTrue(false);
168         }
169       }
170     }
171     assertTrue(foundWorldReadableAcl);
172     assertTrue(foundHBaseOwnerAcl);
173   }
174 
175   /**
176    * When authentication is enabled on Zookeeper, /hbase/master should be
177    * created with 2 ACLs: one specifies that the hbase user has full access
178    * to the node; the other, that it is world-readable.
179    */
180   @Test (timeout=30000)
181   public void testHBaseMasterServerZNodeACL() throws Exception {
182     if (!secureZKAvailable) {
183       return;
184     }
185 
186     List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
187         .getACL("/hbase/master", new Stat());
188     assertEquals(acls.size(),2);
189 
190     boolean foundWorldReadableAcl = false;
191     boolean foundHBaseOwnerAcl = false;
192     for(int i = 0; i < 2; i++) {
193       if (acls.get(i).getId().getScheme().equals("world") == true) {
194         assertEquals(acls.get(0).getId().getId(),"anyone");
195         assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
196         foundWorldReadableAcl = true;
197       } else {
198         if (acls.get(i).getId().getScheme().equals("sasl") == true) {
199           assertEquals(acls.get(1).getId().getId(),"hbase");
200           assertEquals(acls.get(1).getId().getScheme(),"sasl");
201           foundHBaseOwnerAcl = true;
202         } else { // error: should not get here: test fails.
203           assertTrue(false);
204         }
205       }
206     }
207     assertTrue(foundWorldReadableAcl);
208     assertTrue(foundHBaseOwnerAcl);
209   }
210 
211   /**
212    * When authentication is enabled on Zookeeper, /hbase/hbaseid should be
213    * created with 2 ACLs: one specifies that the hbase user has full access
214    * to the node; the other, that it is world-readable.
215    */
216   @Test (timeout=30000)
217   public void testHBaseIDZNodeACL() throws Exception {
218     if (!secureZKAvailable) {
219       return;
220     }
221 
222     List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
223         .getACL("/hbase/hbaseid", new Stat());
224     assertEquals(acls.size(),2);
225 
226     boolean foundWorldReadableAcl = false;
227     boolean foundHBaseOwnerAcl = false;
228     for(int i = 0; i < 2; i++) {
229       if (acls.get(i).getId().getScheme().equals("world") == true) {
230         assertEquals(acls.get(0).getId().getId(),"anyone");
231         assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
232         foundWorldReadableAcl = true;
233       } else {
234         if (acls.get(i).getId().getScheme().equals("sasl") == true) {
235           assertEquals(acls.get(1).getId().getId(),"hbase");
236           assertEquals(acls.get(1).getId().getScheme(),"sasl");
237           foundHBaseOwnerAcl = true;
238         } else { // error: should not get here: test fails.
239           assertTrue(false);
240         }
241       }
242     }
243     assertTrue(foundWorldReadableAcl);
244     assertTrue(foundHBaseOwnerAcl);
245   }
246 
247   /**
248    * Finally, we check the ACLs of a node outside of the /hbase hierarchy and
249    * verify that its ACL is simply 'hbase:Perms.ALL'.
250    */
251   @Test
252   public void testOutsideHBaseNodeACL() throws Exception {
253     if (!secureZKAvailable) {
254       return;
255     }
256 
257     ZKUtil.createWithParents(zkw, "/testACLNode");
258     List<ACL> acls = zkw.getRecoverableZooKeeper().getZooKeeper()
259         .getACL("/testACLNode", new Stat());
260     assertEquals(acls.size(),1);
261     assertEquals(acls.get(0).getId().getScheme(),"sasl");
262     assertEquals(acls.get(0).getId().getId(),"hbase");
263     assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
264   }
265 
266   /**
267    * Check if ZooKeeper JaasConfiguration is valid.
268    */
269   @Test
270   public void testIsZooKeeperSecure() throws Exception {
271     boolean testJaasConfig = ZKUtil.isSecureZooKeeper(new Configuration(TEST_UTIL.getConfiguration()));
272     assertEquals(testJaasConfig, secureZKAvailable);
273     // Define Jaas configuration without ZooKeeper Jaas config
274     File saslConfFile = File.createTempFile("tmp", "fakeJaas.conf");
275     FileWriter fwriter = new FileWriter(saslConfFile);
276 
277     fwriter.write("");
278     fwriter.close();
279     System.setProperty("java.security.auth.login.config",
280         saslConfFile.getAbsolutePath());
281 
282     testJaasConfig = ZKUtil.isSecureZooKeeper(new Configuration(TEST_UTIL.getConfiguration()));
283     assertEquals(testJaasConfig, false);
284     saslConfFile.delete();
285   }
286 }
287