Author: kihwal
Date: Thu May 8 18:28:22 2014
New Revision: 1593363
URL: http://svn.apache.org/r1593363
Log:
svn merge -c 1593362 merging from trunk to branch-2 to fix:HADOOP-10158. SPNEGO
should work with multiple interfaces/SPNs. Contributed by Daryn Sharp.
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java?rev=1593363&r1=1593362&r2=1593363&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
Thu May 8 18:28:22 2014
@@ -34,16 +34,18 @@ import javax.security.auth.login.LoginEx
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import java.io.File;
import java.io.IOException;
-import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
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.regex.Pattern;
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
@@ -140,10 +142,10 @@ public class KerberosAuthenticationHandl
*/
public static final String NAME_RULES = TYPE + ".name.rules";
- private String principal;
private String keytab;
private GSSManager gssManager;
- private LoginContext loginContext;
+ private Subject serverSubject = new Subject();
+ private List<LoginContext> loginContexts = new ArrayList<LoginContext>();
/**
* Initializes the authentication handler instance.
@@ -159,7 +161,7 @@ public class KerberosAuthenticationHandl
@Override
public void init(Properties config) throws ServletException {
try {
- principal = config.getProperty(PRINCIPAL, principal);
+ String principal = config.getProperty(PRINCIPAL);
if (principal == null || principal.trim().length() == 0) {
throw new ServletException("Principal not defined in configuration");
}
@@ -170,23 +172,40 @@ public class KerberosAuthenticationHandl
if (!new File(keytab).exists()) {
throw new ServletException("Keytab does not exist: " + keytab);
}
+
+ // use all SPNEGO principals in the keytab if a principal isn't
+ // specifically configured
+ final String[] spnegoPrincipals;
+ if (principal.equals("*")) {
+ spnegoPrincipals = KerberosUtil.getPrincipalNames(
+ keytab, Pattern.compile("HTTP/.*"));
+ if (spnegoPrincipals.length == 0) {
+ throw new ServletException("Principals do not exist in the keytab");
+ }
+ } else {
+ spnegoPrincipals = new String[]{principal};
+ }
String nameRules = config.getProperty(NAME_RULES, null);
if (nameRules != null) {
KerberosName.setRules(nameRules);
}
- Set<Principal> principals = new HashSet<Principal>();
- principals.add(new KerberosPrincipal(principal));
- Subject subject = new Subject(false, principals, new HashSet<Object>(),
new HashSet<Object>());
-
- KerberosConfiguration kerberosConfiguration = new
KerberosConfiguration(keytab, principal);
-
- LOG.info("Login using keytab "+keytab+", for principal "+principal);
- loginContext = new LoginContext("", subject, null,
kerberosConfiguration);
- loginContext.login();
-
- Subject serverSubject = loginContext.getSubject();
+ for (String spnegoPrincipal : spnegoPrincipals) {
+ LOG.info("Login using keytab {}, for principal {}",
+ keytab, principal);
+ final KerberosConfiguration kerberosConfiguration =
+ new KerberosConfiguration(keytab, spnegoPrincipal);
+ final LoginContext loginContext =
+ new LoginContext("", serverSubject, null, kerberosConfiguration);
+ try {
+ loginContext.login();
+ } catch (LoginException le) {
+ LOG.warn("Failed to login as [{}]", spnegoPrincipal, le);
+ throw new AuthenticationException(le);
+ }
+ loginContexts.add(loginContext);
+ }
try {
gssManager = Subject.doAs(serverSubject, new
PrivilegedExceptionAction<GSSManager>() {
@@ -198,7 +217,6 @@ public class KerberosAuthenticationHandl
} catch (PrivilegedActionException ex) {
throw ex.getException();
}
- LOG.info("Initialized, principal [{}] from keytab [{}]", principal,
keytab);
} catch (Exception ex) {
throw new ServletException(ex);
}
@@ -211,14 +229,16 @@ public class KerberosAuthenticationHandl
*/
@Override
public void destroy() {
- try {
- if (loginContext != null) {
+ keytab = null;
+ serverSubject = null;
+ for (LoginContext loginContext : loginContexts) {
+ try {
loginContext.logout();
- loginContext = null;
+ } catch (LoginException ex) {
+ LOG.warn(ex.getMessage(), ex);
}
- } catch (LoginException ex) {
- LOG.warn(ex.getMessage(), ex);
}
+ loginContexts.clear();
}
/**
@@ -233,12 +253,12 @@ public class KerberosAuthenticationHandl
}
/**
- * Returns the Kerberos principal used by the authentication handler.
+ * Returns the Kerberos principals used by the authentication handler.
*
- * @return the Kerberos principal used by the authentication handler.
+ * @return the Kerberos principals used by the authentication handler.
*/
- protected String getPrincipal() {
- return principal;
+ protected Set<KerberosPrincipal> getPrincipals() {
+ return serverSubject.getPrincipals(KerberosPrincipal.class);
}
/**
@@ -304,7 +324,7 @@ public class KerberosAuthenticationHandl
authorization =
authorization.substring(KerberosAuthenticator.NEGOTIATE.length()).trim();
final Base64 base64 = new Base64(0);
final byte[] clientToken = base64.decode(authorization);
- Subject serverSubject = loginContext.getSubject();
+ final String serverName = request.getServerName();
try {
token = Subject.doAs(serverSubject, new
PrivilegedExceptionAction<AuthenticationToken>() {
@@ -314,15 +334,15 @@ public class KerberosAuthenticationHandl
GSSContext gssContext = null;
GSSCredential gssCreds = null;
try {
- if (IBM_JAVA) {
- // IBM JDK needs non-null credentials to be passed to
createContext here, with
- // SPNEGO mechanism specified, otherwise JGSS will use its
default mechanism
- // only, which is Kerberos V5.
- gssCreds = gssManager.createCredential(null,
GSSCredential.INDEFINITE_LIFETIME,
- new
Oid[]{KerberosUtil.getOidInstance("GSS_SPNEGO_MECH_OID"),
- KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID")},
- GSSCredential.ACCEPT_ONLY);
- }
+ gssCreds = gssManager.createCredential(
+ gssManager.createName(
+ KerberosUtil.getServicePrincipal("HTTP", serverName),
+ KerberosUtil.getOidInstance("NT_GSS_KRB5_PRINCIPAL")),
+ GSSCredential.INDEFINITE_LIFETIME,
+ new Oid[]{
+ KerberosUtil.getOidInstance("GSS_SPNEGO_MECH_OID"),
+ KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID")},
+ GSSCredential.ACCEPT_ONLY);
gssContext = gssManager.createContext(gssCreds);
byte[] serverToken = gssContext.acceptSecContext(clientToken, 0,
clientToken.length);
if (serverToken != null && serverToken.length > 0) {
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java?rev=1593363&r1=1593362&r2=1593363&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestKerberosAuthenticationHandler.java
Thu May 8 18:28:22 2014
@@ -18,6 +18,7 @@ import org.apache.hadoop.security.authen
import
org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.apache.hadoop.security.authentication.util.KerberosUtil;
import org.ietf.jgss.GSSContext;
@@ -30,10 +31,18 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.ietf.jgss.Oid;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import java.io.File;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
import java.util.Properties;
+import java.util.Set;
import java.util.concurrent.Callable;
public class TestKerberosAuthenticationHandler
@@ -110,8 +119,65 @@ public class TestKerberosAuthenticationH
@Test(timeout=60000)
public void testInit() throws Exception {
- Assert.assertEquals(KerberosTestUtils.getServerPrincipal(),
handler.getPrincipal());
Assert.assertEquals(KerberosTestUtils.getKeytabFile(),
handler.getKeytab());
+ Set<KerberosPrincipal> principals = handler.getPrincipals();
+ Principal expectedPrincipal =
+ new KerberosPrincipal(KerberosTestUtils.getServerPrincipal());
+ Assert.assertTrue(principals.contains(expectedPrincipal));
+ Assert.assertEquals(1, principals.size());
+ }
+
+ // dynamic configuration of HTTP principals
+ @Test(timeout=60000)
+ public void testDynamicPrincipalDiscovery() throws Exception {
+ String[] keytabUsers = new String[]{
+ "HTTP/host1", "HTTP/host2", "HTTP2/host1", "XHTTP/host"
+ };
+ String keytab = KerberosTestUtils.getKeytabFile();
+ getKdc().createPrincipal(new File(keytab), keytabUsers);
+
+ // destroy handler created in setUp()
+ handler.destroy();
+ Properties props = new Properties();
+ props.setProperty(KerberosAuthenticationHandler.KEYTAB, keytab);
+ props.setProperty(KerberosAuthenticationHandler.PRINCIPAL, "*");
+ handler = getNewAuthenticationHandler();
+ handler.init(props);
+
+ Assert.assertEquals(KerberosTestUtils.getKeytabFile(),
handler.getKeytab());
+
+ Set<KerberosPrincipal> loginPrincipals = handler.getPrincipals();
+ for (String user : keytabUsers) {
+ Principal principal = new KerberosPrincipal(
+ user + "@" + KerberosTestUtils.getRealm());
+ boolean expected = user.startsWith("HTTP/");
+ Assert.assertEquals("checking for "+user, expected,
+ loginPrincipals.contains(principal));
+ }
+ }
+
+ // dynamic configuration of HTTP principals
+ @Test(timeout=60000)
+ public void testDynamicPrincipalDiscoveryMissingPrincipals() throws
Exception {
+ String[] keytabUsers = new String[]{"hdfs/localhost"};
+ String keytab = KerberosTestUtils.getKeytabFile();
+ getKdc().createPrincipal(new File(keytab), keytabUsers);
+
+ // destroy handler created in setUp()
+ handler.destroy();
+ Properties props = new Properties();
+ props.setProperty(KerberosAuthenticationHandler.KEYTAB, keytab);
+ props.setProperty(KerberosAuthenticationHandler.PRINCIPAL, "*");
+ handler = getNewAuthenticationHandler();
+ try {
+ handler.init(props);
+ Assert.fail("init should have failed");
+ } catch (ServletException ex) {
+ Assert.assertEquals("Principals do not exist in the keytab",
+ ex.getCause().getMessage());
+ } catch (Throwable t) {
+ Assert.fail("wrong exception: "+t);
+ }
}
@Test(timeout=60000)
@@ -190,7 +256,8 @@ public class TestKerberosAuthenticationH
Mockito.when(request.getHeader(KerberosAuthenticator.AUTHORIZATION))
.thenReturn(KerberosAuthenticator.NEGOTIATE + " " + token);
-
+ Mockito.when(request.getServerName()).thenReturn("localhost");
+
AuthenticationToken authToken = handler.authenticate(request, response);
if (authToken != null) {
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt?rev=1593363&r1=1593362&r2=1593363&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
Thu May 8 18:28:22 2014
@@ -45,6 +45,9 @@ Release 2.5.0 - UNRELEASED
HADOOP-10467. Enable proxyuser specification to support list of users in
addition to list of groups (Benoy Antony via Arpit Agarwal)
+ HADOOP-10158. SPNEGO should work with multiple interfaces/SPNs.
+ (daryn via kihwal)
+
OPTIMIZATIONS
BUG FIXES