|
@@ -0,0 +1,806 @@
|
|
|
+/**
|
|
|
+ * 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.crypto.key.kms.server;
|
|
|
+
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.crypto.key.KeyProvider;
|
|
|
+import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
|
|
|
+import org.apache.hadoop.minikdc.MiniKdc;
|
|
|
+import org.apache.hadoop.security.authorize.AuthorizationException;
|
|
|
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
|
|
|
+import org.junit.AfterClass;
|
|
|
+import org.junit.Assert;
|
|
|
+import org.junit.BeforeClass;
|
|
|
+import org.junit.Test;
|
|
|
+import org.mortbay.jetty.Connector;
|
|
|
+import org.mortbay.jetty.Server;
|
|
|
+import org.mortbay.jetty.security.SslSocketConnector;
|
|
|
+import org.mortbay.jetty.webapp.WebAppContext;
|
|
|
+
|
|
|
+import javax.security.auth.Subject;
|
|
|
+import javax.security.auth.kerberos.KerberosPrincipal;
|
|
|
+import javax.security.auth.login.AppConfigurationEntry;
|
|
|
+import javax.security.auth.login.LoginContext;
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileWriter;
|
|
|
+import java.io.Writer;
|
|
|
+import java.net.InetAddress;
|
|
|
+import java.net.MalformedURLException;
|
|
|
+import java.net.ServerSocket;
|
|
|
+import java.net.URI;
|
|
|
+import java.net.URL;
|
|
|
+import java.security.Principal;
|
|
|
+import java.security.PrivilegedExceptionAction;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Properties;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.concurrent.Callable;
|
|
|
+
|
|
|
+public class TestKMS {
|
|
|
+
|
|
|
+ public static File getTestDir() throws Exception {
|
|
|
+ File file = new File("dummy");
|
|
|
+ file = file.getAbsoluteFile();
|
|
|
+ file = file.getParentFile();
|
|
|
+ file = new File(file, "target");
|
|
|
+ file = new File(file, UUID.randomUUID().toString());
|
|
|
+ if (!file.mkdirs()) {
|
|
|
+ throw new RuntimeException("Could not create test directory: " + file);
|
|
|
+ }
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Server createJettyServer(String keyStore, String password) {
|
|
|
+ try {
|
|
|
+ boolean ssl = keyStore != null;
|
|
|
+ InetAddress localhost = InetAddress.getByName("localhost");
|
|
|
+ String host = "localhost";
|
|
|
+ ServerSocket ss = new ServerSocket(0, 50, localhost);
|
|
|
+ int port = ss.getLocalPort();
|
|
|
+ ss.close();
|
|
|
+ Server server = new Server(0);
|
|
|
+ if (!ssl) {
|
|
|
+ server.getConnectors()[0].setHost(host);
|
|
|
+ server.getConnectors()[0].setPort(port);
|
|
|
+ } else {
|
|
|
+ SslSocketConnector c = new SslSocketConnector();
|
|
|
+ c.setHost(host);
|
|
|
+ c.setPort(port);
|
|
|
+ c.setNeedClientAuth(false);
|
|
|
+ c.setKeystore(keyStore);
|
|
|
+ c.setKeystoreType("jks");
|
|
|
+ c.setKeyPassword(password);
|
|
|
+ server.setConnectors(new Connector[]{c});
|
|
|
+ }
|
|
|
+ return server;
|
|
|
+ } catch (Exception ex) {
|
|
|
+ throw new RuntimeException("Could not start embedded servlet container, "
|
|
|
+ + ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static URL getJettyURL(Server server) {
|
|
|
+ boolean ssl = server.getConnectors()[0].getClass()
|
|
|
+ == SslSocketConnector.class;
|
|
|
+ try {
|
|
|
+ String scheme = (ssl) ? "https" : "http";
|
|
|
+ return new URL(scheme + "://" +
|
|
|
+ server.getConnectors()[0].getHost() + ":" +
|
|
|
+ server.getConnectors()[0].getPort());
|
|
|
+ } catch (MalformedURLException ex) {
|
|
|
+ throw new RuntimeException("It should never happen, " + ex.getMessage(),
|
|
|
+ ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static abstract class KMSCallable implements Callable<Void> {
|
|
|
+ private URL kmsUrl;
|
|
|
+
|
|
|
+ protected URL getKMSUrl() {
|
|
|
+ return kmsUrl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void runServer(String keystore, String password, File confDir,
|
|
|
+ KMSCallable callable) throws Exception {
|
|
|
+ System.setProperty(KMSConfiguration.KMS_CONFIG_DIR,
|
|
|
+ confDir.getAbsolutePath());
|
|
|
+ System.setProperty("log4j.configuration", "log4j.properties");
|
|
|
+ Server jetty = createJettyServer(keystore, password);
|
|
|
+ try {
|
|
|
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
|
|
+ URL url = cl.getResource("webapp");
|
|
|
+ if (url == null) {
|
|
|
+ throw new RuntimeException(
|
|
|
+ "Could not find webapp/ dir in test classpath");
|
|
|
+ }
|
|
|
+ WebAppContext context = new WebAppContext(url.getPath(), "/kms");
|
|
|
+ jetty.addHandler(context);
|
|
|
+ jetty.start();
|
|
|
+ url = new URL(getJettyURL(jetty), "kms");
|
|
|
+ System.out.println("Test KMS running at: " + url);
|
|
|
+ callable.kmsUrl = url;
|
|
|
+ callable.call();
|
|
|
+ } finally {
|
|
|
+ if (jetty != null && jetty.isRunning()) {
|
|
|
+ try {
|
|
|
+ jetty.stop();
|
|
|
+ } catch (Exception ex) {
|
|
|
+ throw new RuntimeException("Could not stop embedded Jetty, " +
|
|
|
+ ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected Configuration createBaseKMSConf(File keyStoreDir) throws Exception {
|
|
|
+ Configuration conf = new Configuration(false);
|
|
|
+ conf.set("hadoop.security.key.provider.path",
|
|
|
+ "jceks://file@/" + keyStoreDir.getAbsolutePath() + "/kms.keystore");
|
|
|
+ conf.set("hadoop.kms.authentication.type", "simple");
|
|
|
+ return conf;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void writeConf(File confDir, Configuration conf) throws Exception {
|
|
|
+ Writer writer = new FileWriter(new File(confDir,
|
|
|
+ KMSConfiguration.KMS_SITE_XML));
|
|
|
+ conf.writeXml(writer);
|
|
|
+ writer.close();
|
|
|
+
|
|
|
+ writer = new FileWriter(new File(confDir, KMSConfiguration.KMS_ACLS_XML));
|
|
|
+ conf.writeXml(writer);
|
|
|
+ writer.close();
|
|
|
+
|
|
|
+ //create empty core-site.xml
|
|
|
+ writer = new FileWriter(new File(confDir, "core-site.xml"));
|
|
|
+ new Configuration(false).writeXml(writer);
|
|
|
+ writer.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected URI createKMSUri(URL kmsUrl) throws Exception {
|
|
|
+ String str = kmsUrl.toString();
|
|
|
+ str = str.replaceFirst("://", "@");
|
|
|
+ return new URI("kms://" + str);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static class KerberosConfiguration
|
|
|
+ extends javax.security.auth.login.Configuration {
|
|
|
+ private String principal;
|
|
|
+ private String keytab;
|
|
|
+ private boolean isInitiator;
|
|
|
+
|
|
|
+ private KerberosConfiguration(String principal, File keytab,
|
|
|
+ boolean client) {
|
|
|
+ this.principal = principal;
|
|
|
+ this.keytab = keytab.getAbsolutePath();
|
|
|
+ this.isInitiator = client;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static javax.security.auth.login.Configuration createClientConfig(
|
|
|
+ String principal,
|
|
|
+ File keytab) {
|
|
|
+ return new KerberosConfiguration(principal, keytab, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String getKrb5LoginModuleName() {
|
|
|
+ return System.getProperty("java.vendor").contains("IBM")
|
|
|
+ ? "com.ibm.security.auth.module.Krb5LoginModule"
|
|
|
+ : "com.sun.security.auth.module.Krb5LoginModule";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
|
|
|
+ Map<String, String> options = new HashMap<String, String>();
|
|
|
+ options.put("keyTab", keytab);
|
|
|
+ options.put("principal", principal);
|
|
|
+ options.put("useKeyTab", "true");
|
|
|
+ options.put("storeKey", "true");
|
|
|
+ options.put("doNotPrompt", "true");
|
|
|
+ options.put("useTicketCache", "true");
|
|
|
+ options.put("renewTGT", "true");
|
|
|
+ options.put("refreshKrb5Config", "true");
|
|
|
+ options.put("isInitiator", Boolean.toString(isInitiator));
|
|
|
+ String ticketCache = System.getenv("KRB5CCNAME");
|
|
|
+ if (ticketCache != null) {
|
|
|
+ options.put("ticketCache", ticketCache);
|
|
|
+ }
|
|
|
+ options.put("debug", "true");
|
|
|
+
|
|
|
+ return new AppConfigurationEntry[]{
|
|
|
+ new AppConfigurationEntry(getKrb5LoginModuleName(),
|
|
|
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
|
|
|
+ options)};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MiniKdc kdc;
|
|
|
+ private static File keytab;
|
|
|
+
|
|
|
+ @BeforeClass
|
|
|
+ public static void setUpMiniKdc() throws Exception {
|
|
|
+ File kdcDir = getTestDir();
|
|
|
+ Properties kdcConf = MiniKdc.createConf();
|
|
|
+ kdc = new MiniKdc(kdcConf, kdcDir);
|
|
|
+ kdc.start();
|
|
|
+ keytab = new File(kdcDir, "keytab");
|
|
|
+ List<String> principals = new ArrayList<String>();
|
|
|
+ principals.add("HTTP/localhost");
|
|
|
+ principals.add("client");
|
|
|
+ principals.add("client/host");
|
|
|
+ for (KMSACLs.Type type : KMSACLs.Type.values()) {
|
|
|
+ principals.add(type.toString());
|
|
|
+ }
|
|
|
+ principals.add("CREATE_MATERIAL");
|
|
|
+ principals.add("ROLLOVER_MATERIAL");
|
|
|
+ kdc.createPrincipal(keytab,
|
|
|
+ principals.toArray(new String[principals.size()]));
|
|
|
+ }
|
|
|
+
|
|
|
+ @AfterClass
|
|
|
+ public static void tearDownMiniKdc() throws Exception {
|
|
|
+ if (kdc != null) {
|
|
|
+ kdc.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void doAs(String user, final PrivilegedExceptionAction<Void> action)
|
|
|
+ throws Exception {
|
|
|
+ Set<Principal> principals = new HashSet<Principal>();
|
|
|
+ principals.add(new KerberosPrincipal(user));
|
|
|
+
|
|
|
+ //client login
|
|
|
+ Subject subject = new Subject(false, principals,
|
|
|
+ new HashSet<Object>(), new HashSet<Object>());
|
|
|
+ LoginContext loginContext = new LoginContext("", subject, null,
|
|
|
+ KerberosConfiguration.createClientConfig(user, keytab));
|
|
|
+ try {
|
|
|
+ loginContext.login();
|
|
|
+ subject = loginContext.getSubject();
|
|
|
+ Subject.doAs(subject, action);
|
|
|
+ } finally {
|
|
|
+ loginContext.logout();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testStartStop(final boolean ssl, final boolean kerberos)
|
|
|
+ throws Exception {
|
|
|
+ File testDir = getTestDir();
|
|
|
+ Configuration conf = createBaseKMSConf(testDir);
|
|
|
+
|
|
|
+ final String keystore;
|
|
|
+ final String password;
|
|
|
+ if (ssl) {
|
|
|
+ String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestKMS.class);
|
|
|
+ KeyStoreTestUtil.setupSSLConfig(testDir.getAbsolutePath(), sslConfDir,
|
|
|
+ conf, false);
|
|
|
+ keystore = testDir.getAbsolutePath() + "/serverKS.jks";
|
|
|
+ password = "serverP";
|
|
|
+ } else {
|
|
|
+ keystore = null;
|
|
|
+ password = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (kerberos) {
|
|
|
+ conf.set("hadoop.kms.authentication.type", "kerberos");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.keytab",
|
|
|
+ keytab.getAbsolutePath());
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT");
|
|
|
+ }
|
|
|
+
|
|
|
+ writeConf(testDir, conf);
|
|
|
+
|
|
|
+ runServer(keystore, password, testDir, new KMSCallable() {
|
|
|
+ @Override
|
|
|
+ public Void call() throws Exception {
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ URL url = getKMSUrl();
|
|
|
+ Assert.assertEquals(keystore != null,
|
|
|
+ url.getProtocol().equals("https"));
|
|
|
+ URI uri = createKMSUri(getKMSUrl());
|
|
|
+ final KeyProvider kp = new KMSClientProvider(uri, conf);
|
|
|
+
|
|
|
+ if (kerberos) {
|
|
|
+ for (String user : new String[]{"client", "client/host"}) {
|
|
|
+ doAs(user, new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ // getKeys() empty
|
|
|
+ Assert.assertTrue(kp.getKeys().isEmpty());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // getKeys() empty
|
|
|
+ Assert.assertTrue(kp.getKeys().isEmpty());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testStartStopHttpPseudo() throws Exception {
|
|
|
+ testStartStop(false, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testStartStopHttpsPseudo() throws Exception {
|
|
|
+ testStartStop(true, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testStartStopHttpKerberos() throws Exception {
|
|
|
+ testStartStop(false, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testStartStopHttpsKerberos() throws Exception {
|
|
|
+ testStartStop(true, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testKMSProvider() throws Exception {
|
|
|
+ File confDir = getTestDir();
|
|
|
+ Configuration conf = createBaseKMSConf(confDir);
|
|
|
+ writeConf(confDir, conf);
|
|
|
+
|
|
|
+ runServer(null, null, confDir, new KMSCallable() {
|
|
|
+ @Override
|
|
|
+ public Void call() throws Exception {
|
|
|
+ Date started = new Date();
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ URI uri = createKMSUri(getKMSUrl());
|
|
|
+ KeyProvider kp = new KMSClientProvider(uri, conf);
|
|
|
+
|
|
|
+ // getKeys() empty
|
|
|
+ Assert.assertTrue(kp.getKeys().isEmpty());
|
|
|
+
|
|
|
+ // getKeysMetadata() empty
|
|
|
+ Assert.assertEquals(0, kp.getKeysMetadata().length);
|
|
|
+
|
|
|
+ // createKey()
|
|
|
+ KeyProvider.Options options = new KeyProvider.Options(conf);
|
|
|
+ options.setCipher("AES/CTR/NoPadding");
|
|
|
+ options.setBitLength(128);
|
|
|
+ options.setDescription("l1");
|
|
|
+ KeyProvider.KeyVersion kv0 = kp.createKey("k1", options);
|
|
|
+ Assert.assertNotNull(kv0);
|
|
|
+ Assert.assertNotNull(kv0.getVersionName());
|
|
|
+ Assert.assertNotNull(kv0.getMaterial());
|
|
|
+
|
|
|
+ // getKeyVersion()
|
|
|
+ KeyProvider.KeyVersion kv1 = kp.getKeyVersion(kv0.getVersionName());
|
|
|
+ Assert.assertEquals(kv0.getVersionName(), kv1.getVersionName());
|
|
|
+ Assert.assertNotNull(kv1.getMaterial());
|
|
|
+
|
|
|
+ // getCurrent()
|
|
|
+ KeyProvider.KeyVersion cv1 = kp.getCurrentKey("k1");
|
|
|
+ Assert.assertEquals(kv0.getVersionName(), cv1.getVersionName());
|
|
|
+ Assert.assertNotNull(cv1.getMaterial());
|
|
|
+
|
|
|
+ // getKeyMetadata() 1 version
|
|
|
+ KeyProvider.Metadata m1 = kp.getMetadata("k1");
|
|
|
+ Assert.assertEquals("AES/CTR/NoPadding", m1.getCipher());
|
|
|
+ Assert.assertEquals("AES", m1.getAlgorithm());
|
|
|
+ Assert.assertEquals(128, m1.getBitLength());
|
|
|
+ Assert.assertEquals(1, m1.getVersions());
|
|
|
+ Assert.assertNotNull(m1.getCreated());
|
|
|
+ Assert.assertTrue(started.before(m1.getCreated()));
|
|
|
+
|
|
|
+ // getKeyVersions() 1 version
|
|
|
+ List<KeyProvider.KeyVersion> lkv1 = kp.getKeyVersions("k1");
|
|
|
+ Assert.assertEquals(1, lkv1.size());
|
|
|
+ Assert.assertEquals(kv0.getVersionName(), lkv1.get(0).getVersionName());
|
|
|
+ Assert.assertNotNull(kv1.getMaterial());
|
|
|
+
|
|
|
+ // rollNewVersion()
|
|
|
+ KeyProvider.KeyVersion kv2 = kp.rollNewVersion("k1");
|
|
|
+ Assert.assertNotSame(kv0.getVersionName(), kv2.getVersionName());
|
|
|
+ Assert.assertNotNull(kv2.getMaterial());
|
|
|
+
|
|
|
+ // getKeyVersion()
|
|
|
+ kv2 = kp.getKeyVersion(kv2.getVersionName());
|
|
|
+ boolean eq = true;
|
|
|
+ for (int i = 0; i < kv1.getMaterial().length; i++) {
|
|
|
+ eq = eq && kv1.getMaterial()[i] == kv2.getMaterial()[i];
|
|
|
+ }
|
|
|
+ Assert.assertFalse(eq);
|
|
|
+
|
|
|
+ // getCurrent()
|
|
|
+ KeyProvider.KeyVersion cv2 = kp.getCurrentKey("k1");
|
|
|
+ Assert.assertEquals(kv2.getVersionName(), cv2.getVersionName());
|
|
|
+ Assert.assertNotNull(cv2.getMaterial());
|
|
|
+ eq = true;
|
|
|
+ for (int i = 0; i < kv1.getMaterial().length; i++) {
|
|
|
+ eq = eq && cv2.getMaterial()[i] == kv2.getMaterial()[i];
|
|
|
+ }
|
|
|
+ Assert.assertTrue(eq);
|
|
|
+
|
|
|
+ // getKeyVersions() 2 versions
|
|
|
+ List<KeyProvider.KeyVersion> lkv2 = kp.getKeyVersions("k1");
|
|
|
+ Assert.assertEquals(2, lkv2.size());
|
|
|
+ Assert.assertEquals(kv1.getVersionName(), lkv2.get(0).getVersionName());
|
|
|
+ Assert.assertNotNull(lkv2.get(0).getMaterial());
|
|
|
+ Assert.assertEquals(kv2.getVersionName(), lkv2.get(1).getVersionName());
|
|
|
+ Assert.assertNotNull(lkv2.get(1).getMaterial());
|
|
|
+
|
|
|
+ // getKeyMetadata() 2 version
|
|
|
+ KeyProvider.Metadata m2 = kp.getMetadata("k1");
|
|
|
+ Assert.assertEquals("AES/CTR/NoPadding", m2.getCipher());
|
|
|
+ Assert.assertEquals("AES", m2.getAlgorithm());
|
|
|
+ Assert.assertEquals(128, m2.getBitLength());
|
|
|
+ Assert.assertEquals(2, m2.getVersions());
|
|
|
+ Assert.assertNotNull(m2.getCreated());
|
|
|
+ Assert.assertTrue(started.before(m2.getCreated()));
|
|
|
+
|
|
|
+ // getKeys() 1 key
|
|
|
+ List<String> ks1 = kp.getKeys();
|
|
|
+ Assert.assertEquals(1, ks1.size());
|
|
|
+ Assert.assertEquals("k1", ks1.get(0));
|
|
|
+
|
|
|
+ // getKeysMetadata() 1 key 2 versions
|
|
|
+ KeyProvider.Metadata[] kms1 = kp.getKeysMetadata("k1");
|
|
|
+ Assert.assertEquals(1, kms1.length);
|
|
|
+ Assert.assertEquals("AES/CTR/NoPadding", kms1[0].getCipher());
|
|
|
+ Assert.assertEquals("AES", kms1[0].getAlgorithm());
|
|
|
+ Assert.assertEquals(128, kms1[0].getBitLength());
|
|
|
+ Assert.assertEquals(2, kms1[0].getVersions());
|
|
|
+ Assert.assertNotNull(kms1[0].getCreated());
|
|
|
+ Assert.assertTrue(started.before(kms1[0].getCreated()));
|
|
|
+
|
|
|
+ // deleteKey()
|
|
|
+ kp.deleteKey("k1");
|
|
|
+
|
|
|
+ // getKey()
|
|
|
+ Assert.assertNull(kp.getKeyVersion("k1"));
|
|
|
+
|
|
|
+ // getKeyVersions()
|
|
|
+ Assert.assertNull(kp.getKeyVersions("k1"));
|
|
|
+
|
|
|
+ // getMetadata()
|
|
|
+ Assert.assertNull(kp.getMetadata("k1"));
|
|
|
+
|
|
|
+ // getKeys() empty
|
|
|
+ Assert.assertTrue(kp.getKeys().isEmpty());
|
|
|
+
|
|
|
+ // getKeysMetadata() empty
|
|
|
+ Assert.assertEquals(0, kp.getKeysMetadata().length);
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testACLs() throws Exception {
|
|
|
+ final File testDir = getTestDir();
|
|
|
+ Configuration conf = createBaseKMSConf(testDir);
|
|
|
+ conf.set("hadoop.kms.authentication.type", "kerberos");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.keytab",
|
|
|
+ keytab.getAbsolutePath());
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT");
|
|
|
+
|
|
|
+ for (KMSACLs.Type type : KMSACLs.Type.values()) {
|
|
|
+ conf.set(type.getConfigKey(), type.toString());
|
|
|
+ }
|
|
|
+ conf.set(KMSACLs.Type.CREATE.getConfigKey(),
|
|
|
+ KMSACLs.Type.CREATE.toString() + ",SET_KEY_MATERIAL");
|
|
|
+
|
|
|
+ conf.set(KMSACLs.Type.ROLLOVER.getConfigKey(),
|
|
|
+ KMSACLs.Type.ROLLOVER.toString() + ",SET_KEY_MATERIAL");
|
|
|
+
|
|
|
+ writeConf(testDir, conf);
|
|
|
+
|
|
|
+ runServer(null, null, testDir, new KMSCallable() {
|
|
|
+ @Override
|
|
|
+ public Void call() throws Exception {
|
|
|
+ final Configuration conf = new Configuration();
|
|
|
+ conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64);
|
|
|
+ URI uri = createKMSUri(getKMSUrl());
|
|
|
+ final KeyProvider kp = new KMSClientProvider(uri, conf);
|
|
|
+
|
|
|
+ //nothing allowed
|
|
|
+ doAs("client", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ kp.createKey("k", new KeyProvider.Options(conf));
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.createKey("k", new byte[8], new KeyProvider.Options(conf));
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.rollNewVersion("k");
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.rollNewVersion("k", new byte[8]);
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getKeys();
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getKeysMetadata("k");
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getKeyVersion(KMSClientProvider.buildVersionName("k", 0));
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getCurrentKey("k");
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getMetadata("k");
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ kp.getKeyVersions("k");
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("CREATE", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.createKey("k0",
|
|
|
+ new KeyProvider.Options(conf));
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("DELETE", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ kp.deleteKey("k0");
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.createKey("k1", new byte[8],
|
|
|
+ new KeyProvider.Options(conf));
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("ROLLOVER", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.rollNewVersion("k1");
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.rollNewVersion("k1", new byte[8]);
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("GET", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ kp.getKeyVersion("k1@0");
|
|
|
+ kp.getCurrentKey("k1");
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("GET_KEYS", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ kp.getKeys();
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("GET_METADATA", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ kp.getMetadata("k1");
|
|
|
+ kp.getKeysMetadata("k1");
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // test ACL reloading
|
|
|
+ Thread.sleep(10); // to ensure the ACLs file modifiedTime is newer
|
|
|
+ conf.set(KMSACLs.Type.CREATE.getConfigKey(), "foo");
|
|
|
+ writeConf(testDir, conf);
|
|
|
+
|
|
|
+ KMSWebApp.getACLs().run(); // forcing a reload by hand.
|
|
|
+
|
|
|
+ // should not be able to create a key now
|
|
|
+ doAs("CREATE", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.createKey("k2",
|
|
|
+ new KeyProvider.Options(conf));
|
|
|
+ Assert.fail();
|
|
|
+ } catch (AuthorizationException ex) {
|
|
|
+ //NOP
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testServicePrincipalACLs() throws Exception {
|
|
|
+ File testDir = getTestDir();
|
|
|
+ Configuration conf = createBaseKMSConf(testDir);
|
|
|
+ conf.set("hadoop.kms.authentication.type", "kerberos");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.keytab",
|
|
|
+ keytab.getAbsolutePath());
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost");
|
|
|
+ conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT");
|
|
|
+ for (KMSACLs.Type type : KMSACLs.Type.values()) {
|
|
|
+ conf.set(type.getConfigKey(), " ");
|
|
|
+ }
|
|
|
+ conf.set(KMSACLs.Type.CREATE.getConfigKey(), "client");
|
|
|
+
|
|
|
+ writeConf(testDir, conf);
|
|
|
+
|
|
|
+ runServer(null, null, testDir, new KMSCallable() {
|
|
|
+ @Override
|
|
|
+ public Void call() throws Exception {
|
|
|
+ final Configuration conf = new Configuration();
|
|
|
+ conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64);
|
|
|
+ URI uri = createKMSUri(getKMSUrl());
|
|
|
+ final KeyProvider kp = new KMSClientProvider(uri, conf);
|
|
|
+
|
|
|
+ doAs("client", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.createKey("ck0",
|
|
|
+ new KeyProvider.Options(conf));
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ doAs("client/host", new PrivilegedExceptionAction<Void>() {
|
|
|
+ @Override
|
|
|
+ public Void run() throws Exception {
|
|
|
+ try {
|
|
|
+ KeyProvider.KeyVersion kv = kp.createKey("ck1",
|
|
|
+ new KeyProvider.Options(conf));
|
|
|
+ Assert.assertNull(kv.getMaterial());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Assert.fail(ex.toString());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+}
|