Repository: oozie Updated Branches: refs/heads/master d8f0a5104 -> 75da42b73
OOZIE-1959 TestZKUtilsWithSecurity fails (rkanter) Project: http://git-wip-us.apache.org/repos/asf/oozie/repo Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/75da42b7 Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/75da42b7 Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/75da42b7 Branch: refs/heads/master Commit: 75da42b7377a34e21ca458857ecc7c60f7dc21e0 Parents: d8f0a51 Author: Robert Kanter <[email protected]> Authored: Thu Oct 30 13:44:05 2014 -0700 Committer: Robert Kanter <[email protected]> Committed: Thu Oct 30 13:44:05 2014 -0700 ---------------------------------------------------------------------- core/pom.xml | 12 -- .../java/org/apache/oozie/test/ZKXTestCase.java | 2 +- .../oozie/test/ZKXTestCaseWithSecurity.java | 134 ------------- .../oozie/util/TestZKUtilsWithSecurity.java | 188 ------------------- pom.xml | 6 +- release-log.txt | 1 + zookeeper-security-tests/pom.xml | 101 ++++++++++ .../oozie/test/ZKXTestCaseWithSecurity.java | 135 +++++++++++++ .../oozie/util/TestZKUtilsWithSecurity.java | 188 +++++++++++++++++++ 9 files changed, 427 insertions(+), 340 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/core/pom.xml ---------------------------------------------------------------------- diff --git a/core/pom.xml b/core/pom.xml index 597775c..ca40e2e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -43,12 +43,6 @@ </dependency> <dependency> - <groupId>org.apache.hadoop</groupId> - <artifactId>hadoop-minikdc</artifactId> - <scope>test</scope> - </dependency> - - <dependency> <groupId>org.apache.oozie</groupId> <artifactId>oozie-hadoop</artifactId> <scope>provided</scope> @@ -479,12 +473,6 @@ </dependency> </dependencies> </plugin> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - </plugin> </plugins> </build> http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java b/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java index 84d6e5d..dfbea88 100644 --- a/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java +++ b/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java @@ -144,7 +144,7 @@ public abstract class ZKXTestCase extends XDataTestCase { } private void createClient() throws Exception { - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + RetryPolicy retryPolicy = ZKUtils.getRetryPolicy(); String zkConnectionString = Services.get().getConf().get("oozie.zookeeper.connection.string", zkServer.getConnectString()); String zkNamespace = Services.get().getConf().get("oozie.zookeeper.namespace", "oozie"); client = CuratorFrameworkFactory.builder() http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java b/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java deleted file mode 100644 index f8482aa..0000000 --- a/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * 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.oozie.test; - -import java.io.File; -import javax.security.auth.login.Configuration; - -import org.apache.curator.test.TestingServer; -import org.apache.hadoop.minikdc.MiniKdc; -import org.apache.oozie.service.HadoopAccessorService; -import org.apache.oozie.service.Services; -import org.apache.oozie.util.JaasConfiguration; -import org.apache.zookeeper.server.ZooKeeperSaslServer; - -/** - * Provides a version of {@link ZKXTestCase} with security. A MiniKdc will be started (so no special outside setup is needed) and - * the embedded ZooKeeper provided by this class will support connecting to it with SASL/Kerberos authentication. However, - * currently, the client returned by {@link #getClient()) and the client used by DummyZKOozie do not authenticate, so they won't - * have full access to any znodes with "sasl" ACLs (this is not always true, see {@link #setupZKServer()). - * <p> - * Anything using {@link ZKUtils} can connect using authentication by simply setting "oozie.zookeeper.secure" to "true" before - * creating the first thing that uses ZKUtils. Make sure to set it back to false when done. - */ -public abstract class ZKXTestCaseWithSecurity extends ZKXTestCase { - private MiniKdc kdc = null; - private File keytabFile; - private String originalKeytabLoc; - private String originalPrincipal; - - /** - * The primary part of the principal name for the Kerberos user - */ - protected static final String PRIMARY_PRINCIPAL = "oozie"; - - @Override - protected void setUp() throws Exception { - super.setUp(); - // Set the keytab location and principal to the miniKDC values - originalKeytabLoc = Services.get().getConf().get(HadoopAccessorService.KERBEROS_KEYTAB); - originalPrincipal = Services.get().getConf().get(HadoopAccessorService.KERBEROS_PRINCIPAL); - Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB, keytabFile.getAbsolutePath()); - Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL, getPrincipal()); - } - - @Override - protected void tearDown() throws Exception { - // Restore these values - Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB, originalKeytabLoc); - Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL, originalPrincipal); - // Just in case the test forgets to set this back - Services.get().getConf().set("oozie.zookeeper.secure", "false"); - super.tearDown(); - if (kdc != null) { - kdc.stop(); - } - } - - /** - * Creates and sets up the embedded ZooKeeper server. Test subclasses should have no reason to override this method. - * <p> - * Here we override it to start the MiniKdc, set the jaas configuration, configure ZooKeeper for SASL/Kerberos authentication - * and ACLs, and to start the ZooKeeper server. - * <p> - * Unfortunately, ZooKeeper security requires setting the security for the entire JVM. And for the tests, we're running the - * ZK server and one or more clients from the same JVM, so things get messy. There are two ways to tell ZooKeeper to - * authenticate: (1) set the system property, "java.security.auth.login.config", to a jaas.conf file and (2) create a - * javax.security.auth.login.Configuration object with the same info as the jaas.conf and set it. In either case, once set and - * something has authenticated, it seems that it can't be unset or changed, and there's no way to log out. By setting the - * system property, "javax.security.auth.useSubjectCredsOnly", to "false" we can sort-of change the jaas Configuration, but its - * kind of funny about it. Another effect of this is that we have to add jaas entries for the "Server" and "Client" here - * instead of just the "Server" here and the "Client" in the normal place ({@link ZKUtils}) or it will be unable to find the - * "Client" info. Also, because there is no way to logout, once any client has authenticated once, all subsequent clients will - * automatically connect using the same authentication; trying to stop this is futile and either results in an error or has no - * effect. This means that there's no way to do any tests with an unauthenticated client. Also, if any tests using secure - * ZooKeeper get run before tests not using secure ZooKeeper, they will likely fail because it will try to use authentication: - * so they should be run separately. - * - * @return the embedded ZooKeeper server - * @throws Exception - */ - @Override - protected TestingServer setupZKServer() throws Exception { - // Not entirely sure exactly what "javax.security.auth.useSubjectCredsOnly=false" does, but it has something to do with - // re-authenticating in cases where it otherwise wouldn't. One of the sections on this page briefly mentions it: - // http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/Troubleshooting.html - setSystemProperty("javax.security.auth.useSubjectCredsOnly", "false"); - - // Setup KDC and principal - kdc = new MiniKdc(MiniKdc.createConf(), new File(getTestCaseDir())); - kdc.start(); - keytabFile = new File(getTestCaseDir(), "test.keytab"); - String serverPrincipal = "zookeeper/" + kdc.getHost(); - kdc.createPrincipal(keytabFile, getPrincipal(), serverPrincipal); - - setSystemProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); - setSystemProperty("zookeeper.kerberos.removeHostFromPrincipal", "true"); - setSystemProperty("zookeeper.kerberos.removeRealmFromPrincipal", "true"); - - JaasConfiguration.addEntry("Server", serverPrincipal, keytabFile.getAbsolutePath()); - // Here's where we add the "Client" to the jaas configuration, even though we'd like not to - JaasConfiguration.addEntry("Client", getPrincipal(), keytabFile.getAbsolutePath()); - Configuration.setConfiguration(JaasConfiguration.getInstance()); - - setSystemProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, "Server"); - - return new TestingServer(); - } - - /** - * Returns the principal of the Kerberos user. This would be {@link #PRIMARY_PRINCIPAL}/_host_ - * - * @return the principal of the Kerberos user - */ - protected String getPrincipal() { - return PRIMARY_PRINCIPAL + "/" + kdc.getHost(); - } -} - http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java b/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java deleted file mode 100644 index ffced02..0000000 --- a/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java +++ /dev/null @@ -1,188 +0,0 @@ -/** - * 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.oozie.util; - -import java.util.List; -import static junit.framework.Assert.assertEquals; - -import org.apache.oozie.lock.LockToken; -import org.apache.oozie.service.Services; -import org.apache.oozie.service.ZKLocksService; -import org.apache.oozie.test.ZKXTestCaseWithSecurity; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Stat; - -public class TestZKUtilsWithSecurity extends ZKXTestCaseWithSecurity { - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testCheckAndSetACLs() throws Exception { - // We want to verify the ACLs on locks and the service discovery; ZKUtils does the service discovery and starting - // ZKLocksService will use ZKUtils which will start advertising on the service discovery. We can also acquire a lock so - // it will create a lock znode. - ZKLocksService zkls = new ZKLocksService(); - try { - zkls.init(Services.get()); - LockToken lock = zkls.getWriteLock("foo", 3); - lock.release(); - List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks/foo"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - } - finally { - // unregistering all users of ZKUtils (i.e. ZKLocksService) will cause it to disconnect so when we set - // "oozie.zookeeper.secure" to true, it will again connect but using SASL/Kerberos - zkls.destroy(); - } - - // Verify that the expected paths created above still exist with the "world" ACLs - List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks/foo"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("world", acls.get(0).getId().getScheme()); - assertEquals("anyone", acls.get(0).getId().getId()); - - zkls = new ZKLocksService(); - try { - Services.get().getConf().set("oozie.zookeeper.secure", "true"); - // Now that security is enabled, it will trigger the checkAndSetACLs() code to go through and set all of the previously - // created znodes to have "sasl" ACLs - zkls.init(Services.get()); - acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks/foo"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - } - finally { - zkls.destroy(); - Services.get().getConf().set("oozie.zookeeper.secure", "false"); - } - } - - public void testNewUsingACLs() throws Exception { - // We want to verify the ACLs on new locks and the service discovery; ZKUtils does the service discovery and starting - // ZKLocksService will use ZKUtils which will start advertising on the service discovery. We can also acquire a lock so - // it will create a lock znode. - ZKLocksService zkls = new ZKLocksService(); - try { - Services.get().getConf().set("oozie.zookeeper.secure", "true"); - // Verify that the znodes don't already exist - assertNull(getClient().getZookeeperClient().getZooKeeper().exists("/oozie", null)); - assertNull(getClient().checkExists().forPath("/locks")); - assertNull(getClient().checkExists().forPath("/services")); - // Check that new znodes will use the ACLs - zkls.init(Services.get()); - LockToken lock = zkls.getWriteLock("foo", 3); - lock.release(); - List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/locks/foo"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers"); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); - assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); - assertEquals("sasl", acls.get(0).getId().getScheme()); - assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); - } - finally { - zkls.destroy(); - Services.get().getConf().set("oozie.zookeeper.secure", "false"); - } - } -} http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index d505916..1e79186 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,7 @@ <module>tools</module> <module>minitest</module> <module>distro</module> + <module>zookeeper-security-tests</module> </modules> <distributionManagement> @@ -1051,11 +1052,6 @@ to test the SSH action --> <exclude>**/TestSsh*.java</exclude> - - <!-- Explictly use -Dtest=TestZKUtilsWithSecurity to test the ZKUtils with security. - It can conflict with other non-secure tests that use zookeeper - --> - <exclude>**/TestZKUtilsWithSecurity.java</exclude> </excludes> <!-- DO NOT CHANGE THIS VALUES, TESTCASES CANNOT RUN IN PARALLEL --> <parallel>classes</parallel> http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/release-log.txt ---------------------------------------------------------------------- diff --git a/release-log.txt b/release-log.txt index 991a4a6..e8b9aff 100644 --- a/release-log.txt +++ b/release-log.txt @@ -40,6 +40,7 @@ OOZIE-1943 Bump up trunk to 4.2.0-SNAPSHOT (bzhang) -- Oozie 4.1.0 release (4.1 - unreleased) +OOZIE-1959 TestZKUtilsWithSecurity fails (rkanter) OOZIE-2033 HA and misc improvements to SSL docs (rkanter) OOZIE-1789 Support backward compatibility of oozie share lib (shwethags) OOZIE-2034 Disable SSLv3 (POODLEbleed vulnerability) (rkanter) http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/zookeeper-security-tests/pom.xml ---------------------------------------------------------------------- diff --git a/zookeeper-security-tests/pom.xml b/zookeeper-security-tests/pom.xml new file mode 100644 index 0000000..b24ce33 --- /dev/null +++ b/zookeeper-security-tests/pom.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-main</artifactId> + <version>4.2.0-SNAPSHOT</version> + </parent> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-zookeeper-security-tests</artifactId> + <version>4.2.0-SNAPSHOT</version> + <description>Apache Oozie ZooKeeper Security Tests</description> + <name>Apache Oozie ZooKeeper Security Tests</name> + <packaging>jar</packaging> + + <!-- The unit tests in here change JVM level security configs in such a way that tests non expecting them would + fail. In order to not interfere with other tests, these unit tests need to be run in their own JVM, which + happens when they're in a separate module --> + + <dependencies> + <dependency> + <groupId>org.apache.curator</groupId> + <artifactId>curator-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-minikdc</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-core</artifactId> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-hadoop</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.oozie</groupId> + <artifactId>oozie-hadoop-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludeSubProjects>false</excludeSubProjects> + <excludes> + <!-- excluding all as the root POM does the full check--> + <exclude>**</exclude> + </excludes> + </configuration> + </plugin> + <!-- Required for MiniKDC --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <inherited>true</inherited> + <extensions>true</extensions> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/zookeeper-security-tests/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java ---------------------------------------------------------------------- diff --git a/zookeeper-security-tests/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java b/zookeeper-security-tests/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java new file mode 100644 index 0000000..f9f3e88 --- /dev/null +++ b/zookeeper-security-tests/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java @@ -0,0 +1,135 @@ +/** + * 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.oozie.test; + +import java.io.File; +import javax.security.auth.login.Configuration; + +import org.apache.curator.test.TestingServer; +import org.apache.hadoop.minikdc.MiniKdc; +import org.apache.oozie.service.HadoopAccessorService; +import org.apache.oozie.service.Services; +import org.apache.oozie.util.JaasConfiguration; +import org.apache.zookeeper.server.ZooKeeperSaslServer; + +/** + * Provides a version of {@link ZKXTestCase} with security. A MiniKdc will be started (so no special outside setup is needed) and + * the embedded ZooKeeper provided by this class will support connecting to it with SASL/Kerberos authentication. However, + * currently, the client returned by {@link #getClient()) and the client used by DummyZKOozie do not authenticate, so they won't + * have full access to any znodes with "sasl" ACLs (this is not always true, see {@link #setupZKServer()). + * <p> + * Anything using {@link ZKUtils} can connect using authentication by simply setting "oozie.zookeeper.secure" to "true" before + * creating the first thing that uses ZKUtils. Make sure to set it back to false when done. + */ +public abstract class ZKXTestCaseWithSecurity extends ZKXTestCase { + private MiniKdc kdc = null; + private File keytabFile; + private String originalKeytabLoc; + private String originalPrincipal; + + /** + * The primary part of the principal name for the Kerberos user + */ + protected static final String PRIMARY_PRINCIPAL = "oozie"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + // Set the keytab location and principal to the miniKDC values + originalKeytabLoc = Services.get().getConf().get(HadoopAccessorService.KERBEROS_KEYTAB); + originalPrincipal = Services.get().getConf().get(HadoopAccessorService.KERBEROS_PRINCIPAL); + Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB, keytabFile.getAbsolutePath()); + Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL, getPrincipal()); + } + + @Override + protected void tearDown() throws Exception { + // Restore these values + Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB, originalKeytabLoc); + Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL, originalPrincipal); + // Just in case the test forgets to set this back + Services.get().getConf().set("oozie.zookeeper.secure", "false"); + super.tearDown(); + if (kdc != null) { + kdc.stop(); + } + } + + /** + * Creates and sets up the embedded ZooKeeper server. Test subclasses should have no reason to override this method. + * <p> + * Here we override it to start the MiniKdc, set the jaas configuration, configure ZooKeeper for SASL/Kerberos authentication + * and ACLs, and to start the ZooKeeper server. + * <p> + * Unfortunately, ZooKeeper security requires setting the security for the entire JVM. And for the tests, we're running the + * ZK server and one or more clients from the same JVM, so things get messy. There are two ways to tell ZooKeeper to + * authenticate: (1) set the system property, "java.security.auth.login.config", to a jaas.conf file and (2) create a + * javax.security.auth.login.Configuration object with the same info as the jaas.conf and set it. In either case, once set and + * something has authenticated, it seems that it can't be unset or changed, and there's no way to log out. By setting the + * system property, "javax.security.auth.useSubjectCredsOnly", to "false" we can sort-of change the jaas Configuration, but its + * kind of funny about it. Another effect of this is that we have to add jaas entries for the "Server" and "Client" here + * instead of just the "Server" here and the "Client" in the normal place ({@link ZKUtils}) or it will be unable to find the + * "Client" info. Also, because there is no way to logout, once any client has authenticated once, all subsequent clients will + * automatically connect using the same authentication; trying to stop this is futile and either results in an error or has no + * effect. This means that there's no way to do any tests with an unauthenticated client. Also, if any tests using secure + * ZooKeeper get run before tests not using secure ZooKeeper, they will likely fail because it will try to use authentication: + * so they should be run separately. For this reason, the secure tests should be run in a separate module where they will get + * their own JVM. + * + * @return the embedded ZooKeeper server + * @throws Exception + */ + @Override + protected TestingServer setupZKServer() throws Exception { + // Not entirely sure exactly what "javax.security.auth.useSubjectCredsOnly=false" does, but it has something to do with + // re-authenticating in cases where it otherwise wouldn't. One of the sections on this page briefly mentions it: + // http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/Troubleshooting.html + setSystemProperty("javax.security.auth.useSubjectCredsOnly", "false"); + + // Setup KDC and principal + kdc = new MiniKdc(MiniKdc.createConf(), new File(getTestCaseDir())); + kdc.start(); + keytabFile = new File(getTestCaseDir(), "test.keytab"); + String serverPrincipal = "zookeeper/127.0.0.1"; + kdc.createPrincipal(keytabFile, getPrincipal(), serverPrincipal); + + setSystemProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + setSystemProperty("zookeeper.kerberos.removeHostFromPrincipal", "true"); + setSystemProperty("zookeeper.kerberos.removeRealmFromPrincipal", "true"); + + JaasConfiguration.addEntry("Server", serverPrincipal, keytabFile.getAbsolutePath()); + // Here's where we add the "Client" to the jaas configuration, even though we'd like not to + JaasConfiguration.addEntry("Client", getPrincipal(), keytabFile.getAbsolutePath()); + Configuration.setConfiguration(JaasConfiguration.getInstance()); + + setSystemProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, "Server"); + + return new TestingServer(); + } + + /** + * Returns the principal of the Kerberos user. This would be {@link #PRIMARY_PRINCIPAL}/_host_ + * + * @return the principal of the Kerberos user + */ + protected String getPrincipal() { + return PRIMARY_PRINCIPAL + "/" + kdc.getHost(); + } +} + http://git-wip-us.apache.org/repos/asf/oozie/blob/75da42b7/zookeeper-security-tests/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java ---------------------------------------------------------------------- diff --git a/zookeeper-security-tests/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java b/zookeeper-security-tests/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java new file mode 100644 index 0000000..ffced02 --- /dev/null +++ b/zookeeper-security-tests/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java @@ -0,0 +1,188 @@ +/** + * 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.oozie.util; + +import java.util.List; +import static junit.framework.Assert.assertEquals; + +import org.apache.oozie.lock.LockToken; +import org.apache.oozie.service.Services; +import org.apache.oozie.service.ZKLocksService; +import org.apache.oozie.test.ZKXTestCaseWithSecurity; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; + +public class TestZKUtilsWithSecurity extends ZKXTestCaseWithSecurity { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCheckAndSetACLs() throws Exception { + // We want to verify the ACLs on locks and the service discovery; ZKUtils does the service discovery and starting + // ZKLocksService will use ZKUtils which will start advertising on the service discovery. We can also acquire a lock so + // it will create a lock znode. + ZKLocksService zkls = new ZKLocksService(); + try { + zkls.init(Services.get()); + LockToken lock = zkls.getWriteLock("foo", 3); + lock.release(); + List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks/foo"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + } + finally { + // unregistering all users of ZKUtils (i.e. ZKLocksService) will cause it to disconnect so when we set + // "oozie.zookeeper.secure" to true, it will again connect but using SASL/Kerberos + zkls.destroy(); + } + + // Verify that the expected paths created above still exist with the "world" ACLs + List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks/foo"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("world", acls.get(0).getId().getScheme()); + assertEquals("anyone", acls.get(0).getId().getId()); + + zkls = new ZKLocksService(); + try { + Services.get().getConf().set("oozie.zookeeper.secure", "true"); + // Now that security is enabled, it will trigger the checkAndSetACLs() code to go through and set all of the previously + // created znodes to have "sasl" ACLs + zkls.init(Services.get()); + acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks/foo"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + } + finally { + zkls.destroy(); + Services.get().getConf().set("oozie.zookeeper.secure", "false"); + } + } + + public void testNewUsingACLs() throws Exception { + // We want to verify the ACLs on new locks and the service discovery; ZKUtils does the service discovery and starting + // ZKLocksService will use ZKUtils which will start advertising on the service discovery. We can also acquire a lock so + // it will create a lock znode. + ZKLocksService zkls = new ZKLocksService(); + try { + Services.get().getConf().set("oozie.zookeeper.secure", "true"); + // Verify that the znodes don't already exist + assertNull(getClient().getZookeeperClient().getZooKeeper().exists("/oozie", null)); + assertNull(getClient().checkExists().forPath("/locks")); + assertNull(getClient().checkExists().forPath("/services")); + // Check that new znodes will use the ACLs + zkls.init(Services.get()); + LockToken lock = zkls.getWriteLock("foo", 3); + lock.release(); + List<ACL> acls = getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat()); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/locks/foo"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers"); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + acls = getClient().getACL().forPath("/services/servers/" + ZK_ID); + assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms()); + assertEquals("sasl", acls.get(0).getId().getScheme()); + assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId()); + } + finally { + zkls.destroy(); + Services.get().getConf().set("oozie.zookeeper.secure", "false"); + } + } +}
