http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestZKSignerSecretProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestZKSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestZKSignerSecretProvider.java new file mode 100644 index 0000000..d7b6e17 --- /dev/null +++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestZKSignerSecretProvider.java @@ -0,0 +1,270 @@ +/** + * Licensed 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. See accompanying LICENSE file. + */ +package org.apache.hadoop.security.authentication.util; + +import java.util.Arrays; +import java.util.Properties; +import java.util.Random; +import javax.servlet.ServletContext; +import org.apache.curator.test.TestingServer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestZKSignerSecretProvider { + + private TestingServer zkServer; + + @Before + public void setup() throws Exception { + zkServer = new TestingServer(); + } + + @After + public void teardown() throws Exception { + if (zkServer != null) { + zkServer.stop(); + zkServer.close(); + } + } + + @Test + // Test just one ZKSignerSecretProvider to verify that it works in the + // simplest case + public void testOne() throws Exception { + long rolloverFrequency = 15 * 1000; // rollover every 15 sec + // use the same seed so we can predict the RNG + long seed = System.currentTimeMillis(); + Random rand = new Random(seed); + byte[] secret2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secret1 = Long.toString(rand.nextLong()).getBytes(); + byte[] secret3 = Long.toString(rand.nextLong()).getBytes(); + ZKSignerSecretProvider secretProvider = new ZKSignerSecretProvider(seed); + Properties config = new Properties(); + config.setProperty( + ZKSignerSecretProvider.ZOOKEEPER_CONNECTION_STRING, + zkServer.getConnectString()); + config.setProperty(ZKSignerSecretProvider.ZOOKEEPER_PATH, + "/secret"); + try { + secretProvider.init(config, getDummyServletContext(), rolloverFrequency); + + byte[] currentSecret = secretProvider.getCurrentSecret(); + byte[][] allSecrets = secretProvider.getAllSecrets(); + Assert.assertArrayEquals(secret1, currentSecret); + Assert.assertEquals(2, allSecrets.length); + Assert.assertArrayEquals(secret1, allSecrets[0]); + Assert.assertNull(allSecrets[1]); + Thread.sleep((rolloverFrequency + 2000)); + + currentSecret = secretProvider.getCurrentSecret(); + allSecrets = secretProvider.getAllSecrets(); + Assert.assertArrayEquals(secret2, currentSecret); + Assert.assertEquals(2, allSecrets.length); + Assert.assertArrayEquals(secret2, allSecrets[0]); + Assert.assertArrayEquals(secret1, allSecrets[1]); + Thread.sleep((rolloverFrequency + 2000)); + + currentSecret = secretProvider.getCurrentSecret(); + allSecrets = secretProvider.getAllSecrets(); + Assert.assertArrayEquals(secret3, currentSecret); + Assert.assertEquals(2, allSecrets.length); + Assert.assertArrayEquals(secret3, allSecrets[0]); + Assert.assertArrayEquals(secret2, allSecrets[1]); + Thread.sleep((rolloverFrequency + 2000)); + } finally { + secretProvider.destroy(); + } + } + + @Test + public void testMultipleInit() throws Exception { + long rolloverFrequency = 15 * 1000; // rollover every 15 sec + // use the same seed so we can predict the RNG + long seedA = System.currentTimeMillis(); + Random rand = new Random(seedA); + byte[] secretA2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretA1 = Long.toString(rand.nextLong()).getBytes(); + // use the same seed so we can predict the RNG + long seedB = System.currentTimeMillis() + rand.nextLong(); + rand = new Random(seedB); + byte[] secretB2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretB1 = Long.toString(rand.nextLong()).getBytes(); + // use the same seed so we can predict the RNG + long seedC = System.currentTimeMillis() + rand.nextLong(); + rand = new Random(seedC); + byte[] secretC2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretC1 = Long.toString(rand.nextLong()).getBytes(); + ZKSignerSecretProvider secretProviderA = new ZKSignerSecretProvider(seedA); + ZKSignerSecretProvider secretProviderB = new ZKSignerSecretProvider(seedB); + ZKSignerSecretProvider secretProviderC = new ZKSignerSecretProvider(seedC); + Properties config = new Properties(); + config.setProperty( + ZKSignerSecretProvider.ZOOKEEPER_CONNECTION_STRING, + zkServer.getConnectString()); + config.setProperty(ZKSignerSecretProvider.ZOOKEEPER_PATH, + "/secret"); + try { + secretProviderA.init(config, getDummyServletContext(), rolloverFrequency); + secretProviderB.init(config, getDummyServletContext(), rolloverFrequency); + secretProviderC.init(config, getDummyServletContext(), rolloverFrequency); + + byte[] currentSecretA = secretProviderA.getCurrentSecret(); + byte[][] allSecretsA = secretProviderA.getAllSecrets(); + byte[] currentSecretB = secretProviderB.getCurrentSecret(); + byte[][] allSecretsB = secretProviderB.getAllSecrets(); + byte[] currentSecretC = secretProviderC.getCurrentSecret(); + byte[][] allSecretsC = secretProviderC.getAllSecrets(); + Assert.assertArrayEquals(currentSecretA, currentSecretB); + Assert.assertArrayEquals(currentSecretB, currentSecretC); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertEquals(2, allSecretsB.length); + Assert.assertEquals(2, allSecretsC.length); + Assert.assertArrayEquals(allSecretsA[0], allSecretsB[0]); + Assert.assertArrayEquals(allSecretsB[0], allSecretsC[0]); + Assert.assertNull(allSecretsA[1]); + Assert.assertNull(allSecretsB[1]); + Assert.assertNull(allSecretsC[1]); + char secretChosen = 'z'; + if (Arrays.equals(secretA1, currentSecretA)) { + Assert.assertArrayEquals(secretA1, allSecretsA[0]); + secretChosen = 'A'; + } else if (Arrays.equals(secretB1, currentSecretB)) { + Assert.assertArrayEquals(secretB1, allSecretsA[0]); + secretChosen = 'B'; + }else if (Arrays.equals(secretC1, currentSecretC)) { + Assert.assertArrayEquals(secretC1, allSecretsA[0]); + secretChosen = 'C'; + } else { + Assert.fail("It appears that they all agreed on the same secret, but " + + "not one of the secrets they were supposed to"); + } + Thread.sleep((rolloverFrequency + 2000)); + + currentSecretA = secretProviderA.getCurrentSecret(); + allSecretsA = secretProviderA.getAllSecrets(); + currentSecretB = secretProviderB.getCurrentSecret(); + allSecretsB = secretProviderB.getAllSecrets(); + currentSecretC = secretProviderC.getCurrentSecret(); + allSecretsC = secretProviderC.getAllSecrets(); + Assert.assertArrayEquals(currentSecretA, currentSecretB); + Assert.assertArrayEquals(currentSecretB, currentSecretC); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertEquals(2, allSecretsB.length); + Assert.assertEquals(2, allSecretsC.length); + Assert.assertArrayEquals(allSecretsA[0], allSecretsB[0]); + Assert.assertArrayEquals(allSecretsB[0], allSecretsC[0]); + Assert.assertArrayEquals(allSecretsA[1], allSecretsB[1]); + Assert.assertArrayEquals(allSecretsB[1], allSecretsC[1]); + // The second secret used is prechosen by whoever won the init; so it + // should match with whichever we saw before + if (secretChosen == 'A') { + Assert.assertArrayEquals(secretA2, currentSecretA); + } else if (secretChosen == 'B') { + Assert.assertArrayEquals(secretB2, currentSecretA); + } else if (secretChosen == 'C') { + Assert.assertArrayEquals(secretC2, currentSecretA); + } + } finally { + secretProviderC.destroy(); + secretProviderB.destroy(); + secretProviderA.destroy(); + } + } + + @Test + public void testMultipleUnsychnronized() throws Exception { + long rolloverFrequency = 15 * 1000; // rollover every 15 sec + // use the same seed so we can predict the RNG + long seedA = System.currentTimeMillis(); + Random rand = new Random(seedA); + byte[] secretA2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretA1 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretA3 = Long.toString(rand.nextLong()).getBytes(); + // use the same seed so we can predict the RNG + long seedB = System.currentTimeMillis() + rand.nextLong(); + rand = new Random(seedB); + byte[] secretB2 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretB1 = Long.toString(rand.nextLong()).getBytes(); + byte[] secretB3 = Long.toString(rand.nextLong()).getBytes(); + ZKSignerSecretProvider secretProviderA = new ZKSignerSecretProvider(seedA); + ZKSignerSecretProvider secretProviderB = new ZKSignerSecretProvider(seedB); + Properties config = new Properties(); + config.setProperty( + ZKSignerSecretProvider.ZOOKEEPER_CONNECTION_STRING, + zkServer.getConnectString()); + config.setProperty(ZKSignerSecretProvider.ZOOKEEPER_PATH, + "/secret"); + try { + secretProviderA.init(config, getDummyServletContext(), rolloverFrequency); + + byte[] currentSecretA = secretProviderA.getCurrentSecret(); + byte[][] allSecretsA = secretProviderA.getAllSecrets(); + Assert.assertArrayEquals(secretA1, currentSecretA); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertArrayEquals(secretA1, allSecretsA[0]); + Assert.assertNull(allSecretsA[1]); + Thread.sleep((rolloverFrequency + 2000)); + + currentSecretA = secretProviderA.getCurrentSecret(); + allSecretsA = secretProviderA.getAllSecrets(); + Assert.assertArrayEquals(secretA2, currentSecretA); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertArrayEquals(secretA2, allSecretsA[0]); + Assert.assertArrayEquals(secretA1, allSecretsA[1]); + Thread.sleep((rolloverFrequency / 5)); + + secretProviderB.init(config, getDummyServletContext(), rolloverFrequency); + + byte[] currentSecretB = secretProviderB.getCurrentSecret(); + byte[][] allSecretsB = secretProviderB.getAllSecrets(); + Assert.assertArrayEquals(secretA2, currentSecretB); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertArrayEquals(secretA2, allSecretsB[0]); + Assert.assertArrayEquals(secretA1, allSecretsB[1]); + Thread.sleep((rolloverFrequency)); + + currentSecretA = secretProviderA.getCurrentSecret(); + allSecretsA = secretProviderA.getAllSecrets(); + currentSecretB = secretProviderB.getCurrentSecret(); + allSecretsB = secretProviderB.getAllSecrets(); + Assert.assertArrayEquals(currentSecretA, currentSecretB); + Assert.assertEquals(2, allSecretsA.length); + Assert.assertEquals(2, allSecretsB.length); + Assert.assertArrayEquals(allSecretsA[0], allSecretsB[0]); + Assert.assertArrayEquals(allSecretsA[1], allSecretsB[1]); + if (Arrays.equals(secretA3, currentSecretA)) { + Assert.assertArrayEquals(secretA3, allSecretsA[0]); + } else if (Arrays.equals(secretB3, currentSecretB)) { + Assert.assertArrayEquals(secretB3, allSecretsA[0]); + } else { + Assert.fail("It appears that they all agreed on the same secret, but " + + "not one of the secrets they were supposed to"); + } + } finally { + secretProviderB.destroy(); + secretProviderA.destroy(); + } + } + + private ServletContext getDummyServletContext() { + ServletContext servletContext = Mockito.mock(ServletContext.class); + Mockito.when(servletContext.getAttribute(ZKSignerSecretProvider + .ZOOKEEPER_SIGNER_SECRET_PROVIDER_CURATOR_CLIENT_ATTRIBUTE)) + .thenReturn(null); + return servletContext; + } +}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-common/CHANGES.txt ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 89bce4d..2d906f7 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -520,6 +520,9 @@ Release 2.6.0 - UNRELEASED HADOOP-11091. Eliminate old configuration parameter names from s3a (David S. Wang via Colin Patrick McCabe) + HADOOP-10868. AuthenticationFilter should support externalizing the + secret for signing and provide rotation support. (rkanter via tucu) + OPTIMIZATIONS HADOOP-10838. Byte array native checksumming. (James Thomas via todd) http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java ---------------------------------------------------------------------- diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java index c6c0d19..763d168 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java @@ -66,6 +66,8 @@ import org.mortbay.jetty.Server; import org.mortbay.jetty.webapp.WebAppContext; import com.google.common.collect.Maps; +import java.util.Properties; +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; import org.apache.hadoop.security.authentication.util.StringSignerSecretProvider; public class TestHttpFSServer extends HFSTestCase { @@ -685,7 +687,11 @@ public class TestHttpFSServer extends HFSTestCase { new AuthenticationToken("u", "p", new KerberosDelegationTokenAuthenticationHandler().getType()); token.setExpires(System.currentTimeMillis() + 100000000); - Signer signer = new Signer(new StringSignerSecretProvider("secret")); + StringSignerSecretProvider secretProvider = new StringSignerSecretProvider(); + Properties secretProviderProps = new Properties(); + secretProviderProps.setProperty(AuthenticationFilter.SIGNATURE_SECRET, "secret"); + secretProvider.init(secretProviderProps, null, -1); + Signer signer = new Signer(secretProvider); String tokenSigned = signer.sign(token.toString()); url = new URL(TestJettyHelper.getJettyURL(), http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-project/pom.xml ---------------------------------------------------------------------- diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index 502655f..0f662a2 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -849,6 +849,17 @@ <artifactId>xercesImpl</artifactId> <version>2.9.1</version> </dependency> + + <dependency> + <groupId>org.apache.curator</groupId> + <artifactId>curator-framework</artifactId> + <version>2.6.0</version> + </dependency> + <dependency> + <groupId>org.apache.curator</groupId> + <artifactId>curator-test</artifactId> + <version>2.6.0</version> + </dependency> </dependencies> </dependencyManagement>