From d9a86031a077184d429dd5463e7da156df112011 Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Tue, 16 Sep 2014 23:07:01 -0700 Subject: [PATCH] HADOOP-10982 --- .../crypto/key/kms/KMSClientProvider.java | 3 ++ .../hadoop-kms/src/site/apt/index.apt.vm | 26 ++++++++- .../hadoop/crypto/key/kms/server/TestKMS.java | 54 +++++++++++++++---- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java index 899b6c44dc..a97463ac88 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java @@ -45,6 +45,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.lang.reflect.UndeclaredThrowableException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URI; @@ -400,6 +401,8 @@ public HttpURLConnection run() throws Exception { }); } catch (IOException ex) { throw ex; + } catch (UndeclaredThrowableException ex) { + throw new IOException(ex.getUndeclaredThrowable()); } catch (Exception ex) { throw new IOException(ex); } diff --git a/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm b/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm index 5fded9282c..682f4795d8 100644 --- a/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm +++ b/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm @@ -602,7 +602,31 @@ $ keytool -genkey -alias tomcat -keyalg RSA *** HTTP Kerberos Principals Configuration - TBD + When KMS instances are behind a load-balancer or VIP, clients will use the + hostname of the VIP. For Kerberos SPNEGO authentication, the hostname of the + URL is used to construct the Kerberos service name of the server, + <<>>. This means that all KMS instances must have have a + Kerberos service name with the load-balancer or VIP hostname. + + In order to be able to access directly a specific KMS instance, the KMS + instance must also have Kebero service name with its own hostname. This is + require for monitoring and admin purposes. + + Both Kerberos service principal credentials (for the load-balancer/VIP + hostname and for the actual KMS instance hostname) must be in the keytab file + configured for authentication. And the principal name specified in the + configuration must be '*'. For example: + ++---+ + + hadoop.kms.authentication.kerberos.principal + * + ++---+ + + <> If using HTTPS, the SSL certificate used by the KMS instance must + be configured to support multiple hostnames (see Java 7 + <<> SAN extension support for details on how to do this). *** HTTP Authentication Signature diff --git a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java index cdb3c7f509..42afe19a73 100644 --- a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java +++ b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java @@ -32,6 +32,7 @@ import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.security.ssl.KeyStoreTestUtil; import org.junit.AfterClass; @@ -209,6 +210,7 @@ public static void setUpMiniKdc() throws Exception { keytab = new File(kdcDir, "keytab"); List principals = new ArrayList(); principals.add("HTTP/localhost"); + principals.add("HTTP/127.0.0.1"); principals.add("client"); principals.add("hdfs"); principals.add("otheradmin"); @@ -251,8 +253,8 @@ private T doAs(String user, final PrivilegedExceptionAction action) } } - public void testStartStop(final boolean ssl, final boolean kerberos) - throws Exception { + public void testStartStop(final boolean ssl, final boolean kerberos, + final boolean multipleServerPrincipals) throws Exception { Configuration conf = new Configuration(); if (kerberos) { conf.set("hadoop.security.authentication", "kerberos"); @@ -278,7 +280,12 @@ public void testStartStop(final boolean ssl, final boolean 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"); + if (multipleServerPrincipals) { + conf.set("hadoop.kms.authentication.kerberos.principal", "*"); + } else { + conf.set("hadoop.kms.authentication.kerberos.principal", + "HTTP/localhost"); + } conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); } @@ -291,21 +298,42 @@ public Void call() throws Exception { URL url = getKMSUrl(); Assert.assertEquals(keystore != null, url.getProtocol().equals("https")); - final URI uri = createKMSUri(getKMSUrl()); if (kerberos) { for (String user : new String[]{"client", "client/host"}) { doAs(user, new PrivilegedExceptionAction() { @Override public Void run() throws Exception { - final KeyProvider kp = new KMSClientProvider(uri, conf); + URI uri = createKMSUri(getKMSUrl()); + KeyProvider kp = new KMSClientProvider(uri, conf); // getKeys() empty Assert.assertTrue(kp.getKeys().isEmpty()); + + if (!ssl) { + String url = getKMSUrl().toString(); + url = url.replace("localhost", "127.0.0.1"); + uri = createKMSUri(new URL(url)); + if (multipleServerPrincipals) { + kp = new KMSClientProvider(uri, conf); + // getKeys() empty + Assert.assertTrue(kp.getKeys().isEmpty()); + } else { + kp = new KMSClientProvider(uri, conf); + try { + kp.getKeys().isEmpty(); + Assert.fail(); + } catch (IOException ex) { + Assert.assertEquals(AuthenticationException.class, + ex.getCause().getClass()); + } + } + } return null; } }); } } else { + URI uri = createKMSUri(getKMSUrl()); KeyProvider kp = new KMSClientProvider(uri, conf); // getKeys() empty Assert.assertTrue(kp.getKeys().isEmpty()); @@ -317,22 +345,27 @@ public Void run() throws Exception { @Test public void testStartStopHttpPseudo() throws Exception { - testStartStop(false, false); + testStartStop(false, false, false); } @Test public void testStartStopHttpsPseudo() throws Exception { - testStartStop(true, false); + testStartStop(true, false, false); } @Test public void testStartStopHttpKerberos() throws Exception { - testStartStop(false, true); + testStartStop(false, true, false); } @Test public void testStartStopHttpsKerberos() throws Exception { - testStartStop(true, true); + testStartStop(true, true, false); + } + + @Test + public void testStartStopHttpsKerberosMultiplePrincipals() throws Exception { + testStartStop(false, true, true); } @Test @@ -1340,7 +1373,8 @@ public Void call() throws Exception { KeyProvider kp = new KMSClientProvider(uri, conf); kp.createKey("kA", new KeyProvider.Options(conf)); } catch (IOException ex) { - System.out.println(ex.getMessage()); + Assert.assertEquals(AuthenticationException.class, + ex.getCause().getClass()); } doAs("client", new PrivilegedExceptionAction() {