diff --git a/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosAuthenticationHandler.java b/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosAuthenticationHandler.java new file mode 100644 index 0000000..8c73717 --- /dev/null +++ b/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosAuthenticationHandler.java @@ -0,0 +1,682 @@ +package org.apache.hadoop.yarn.ats; + +/** + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.URLEncoder; +import java.util.Calendar; +import java.util.Hashtable; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.security.auth.Subject; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +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.hadoop.security.authentication.server.AuthenticationHandler; +import org.apache.hadoop.security.authentication.server.AuthenticationToken; +import org.apache.hadoop.security.authentication.util.KerberosName; +import org.apache.hadoop.security.authentication.util.KerberosUtil; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +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.List; +import java.util.Locale; +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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + + + /** + * The {@link AltKerberosAuthenticationHandler} behaves exactly the same way as + * the {@link KerberosAuthenticationHandler}, except that it allows for an + * alternative form of authentication for browsers while still using Kerberos + * for Java access. This is an abstract class that should be subclassed + * to allow a developer to implement their own custom authentication for browser + * access. The alternateAuthenticate method will be called whenever a request + * comes from a browser. + *
+ */ +public class AltATSLdapKerberosAuthenticationHandler + implements AuthenticationHandler { + + private static Logger LOG = LoggerFactory.getLogger(AltATSLdapKerberosAuthenticationHandler.class); + + /** + * Constant that identifies the authentication mechanism. + */ + public static final String TYPE = "kerberos"; + + /** + * Constant for the configuration property that indicates which user agents + * are not considered browsers (comma separated) + */ + public static final String NON_BROWSER_USER_AGENTS = + "alt-kerberos.non-browser.user-agents"; + private static final String NON_BROWSER_USER_AGENTS_DEFAULT = + "java,curl,wget,perl"; + + private String[] nonBrowserUserAgents; + + + /** + * Constant for the configuration property that indicates the kerberos principal. + */ + public static final String PRINCIPAL = TYPE + ".principal"; + + /** + * Constant for the configuration property that indicates the keytab file path. + */ + public static final String KEYTAB = TYPE + ".keytab"; + + /** + * Constant for the configuration property that indicates the Kerberos name + * rules for the Kerberos principals. + */ + public static final String NAME_RULES = TYPE + ".name.rules"; + + /** + * HTTP header used by the LDAPAuth server endpoint during an authentication sequence. + */ + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + /** + * HTTP header used by the SPNEGO client endpoint during an authentication sequence. + */ + public static final String AUTHORIZATION = "Authorization"; + + /** + * HTTP header prefix used by the SPNEGO client/server endpoints during an authentication sequence. + */ + + private static final String BASIC_AUTH = "BASIC realm=\"Hadoop Auth\""; + + private static String ldapUrl = null; + + private static String baseDn = null; + + private static String ldapDomain = null; + private static String bindIdDn = null; + private static String bindPasswd = null; + + + public static final String REDIRECT_URL = + "alt-kerberos.redirect.url"; + + public static final String LDAP_URL = + "alt-kerberos.ldap.url"; + + public static final String LDAP_DOMAIN = + "alt-kerberos.ldap.domain"; + + public static final String LDAP_BASEDN = + "alt-kerberos.ldap.basedn"; + + public static final String LDAPBINDDN = + "alt-kerberos.ldap.binddn"; + public static final String LDAPBINDPASSWD = + "alt-kerberos.ldap.bindpasswd"; + + /** + * Kerberos context configuration for the JDK GSS library. + */ + private static class KerberosConfiguration extends Configuration { + private String keytab; + private String principal; + + public KerberosConfiguration(String keytab, String principal) { + this.keytab = keytab; + this.principal = principal; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Mapkerberos.
+ */
+ public AltATSLdapKerberosAuthenticationHandler() {
+ this(TYPE);
+ }
+
+ /**
+ * Creates a Kerberos SPNEGO authentication handler with a custom auth-token
+ * type.
+ *
+ * @param type auth-token type.
+ */
+ public AltATSLdapKerberosAuthenticationHandler(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Initializes the authentication handler instance.
+ * + * It creates a Kerberos context using the principal and keytab specified in the configuration. + *
+ * This method is invoked by the {@link AuthenticationFilter#init} method.
+ *
+ * @param config configuration properties to initialize the handler.
+ *
+ * @throws ServletException thrown if the handler could not be initialized.
+ */
+
+ //@Override
+ public void init(Properties config) throws ServletException {
+ try {
+ nonBrowserUserAgents = config.getProperty(
+ NON_BROWSER_USER_AGENTS, NON_BROWSER_USER_AGENTS_DEFAULT)
+ .split("\\W*,\\W*");
+ for (int i = 0; i < nonBrowserUserAgents.length; i++) {
+ nonBrowserUserAgents[i] =
+ nonBrowserUserAgents[i].toLowerCase(Locale.ENGLISH);
+ }
+
+ String principal = config.getProperty(PRINCIPAL);
+ if (principal == null || principal.trim().length() == 0) {
+ throw new ServletException("Principal not defined in configuration");
+ }
+ keytab = config.getProperty(KEYTAB, keytab);
+ if (keytab == null || keytab.trim().length() == 0) {
+ throw new ServletException("Keytab not defined in configuration");
+ }
+ 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);
+ }
+
+ for (String spnegoPrincipal : spnegoPrincipals) {
+ LOG.info("Login using keytab {}, for principal {}",
+ keytab, spnegoPrincipal);
+ 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
+ * It destroys the Kerberos context.
+ */
+ //@Override
+ public void destroy() {
+ keytab = null;
+ serverSubject = null;
+ for (LoginContext loginContext : loginContexts) {
+ try {
+ loginContext.logout();
+ } catch (LoginException ex) {
+ LOG.warn(ex.getMessage(), ex);
+ }
+ }
+ loginContexts.clear();
+ }
+
+ /**
+ * Returns the authentication type of the authentication handler, 'kerberos'.
+ *
+ *
+ * @return the authentication type of the authentication handler, 'kerberos'.
+ */
+ //@Override
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the Kerberos principals used by the authentication handler.
+ *
+ * @return the Kerberos principals used by the authentication handler.
+ */
+ protected Set
+ * A User-Agent String is considered to be a browser if it does not contain
+ * any of the values from alt-kerberos.non-browser.user-agents; the default
+ * behavior is to consider everything a browser unless it contains one of:
+ * "java", "curl", "wget", or "perl". Subclasses can optionally override
+ * this method to use different behavior.
+ *
+ * @param userAgent The User-Agent String, or null if there isn't one
+ * @return true if the User-Agent String refers to a browser, false if not
+ */
+ protected boolean isBrowser(String userAgent) {
+ if (userAgent == null) {
+ return false;
+ }
+ userAgent = userAgent.toLowerCase(Locale.ENGLISH);
+ boolean isBrowser = true;
+ for (String nonBrowserUserAgent : nonBrowserUserAgents) {
+ if (userAgent.contains(nonBrowserUserAgent)) {
+ isBrowser = false;
+ break;
+ }
+ }
+ return isBrowser;
+ }
+
+
+}
diff --git a/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosDelegationAuthHandler.java b/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosDelegationAuthHandler.java
new file mode 100644
index 0000000..8242a71
--- /dev/null
+++ b/src/main/java/org/apache/hadoop/yarn/ats/AltATSLdapKerberosDelegationAuthHandler.java
@@ -0,0 +1,37 @@
+package org.apache.hadoop.yarn.ats;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
+import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
+import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
+
+ /**
+ * An {@link AuthenticationHandler} that implements Kerberos SPNEGO mechanism
+ * for HTTP and supports Delegation Token functionality.
+ * TRUE.
+ *
+ *
+ *
+ * @param token the authentication token if any, otherwise NULL.
+ * @param request the HTTP client request.
+ * @param response the HTTP client response.
+ *
+ * @return TRUE
+ * @throws IOException it is never thrown.
+ * @throws AuthenticationException it is never thrown.
+ */
+ //@Override
+ public boolean managementOperation(AuthenticationToken token,
+ HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, AuthenticationException {
+ return true;
+ }
+
+ /**
+ * It enforces the the Kerberos SPNEGO authentication sequence returning an {@link AuthenticationToken} only
+ * after the Kerberos SPNEGO sequence has completed successfully.
+ *
+ * @param request the HTTP client request.
+ * @param response the HTTP client response.
+ *
+ * @return an authentication token if the Kerberos SPNEGO sequence is complete and valid,
+ * null if it is in progress (in this case the handler handles the response to the client).
+ *
+ * @throws IOException thrown if an IO error occurred.
+ * @throws AuthenticationException thrown if Kerberos SPNEGO sequence failed.
+ */
+ //@Override
+ public AuthenticationToken authenticate(HttpServletRequest request, final HttpServletResponse response)
+ throws IOException, AuthenticationException {
+ AuthenticationToken token = null;
+ if (isBrowser(request.getHeader("User-Agent"))) {
+ LOG.info("AltKerberos Browser Authentication");
+ token = alternateAuthenticate(request, response);
+
+ return token;
+ }
+ LOG.info("Kerberos Normal Authentication");
+
+
+ String authorization = request.getHeader(KerberosAuthenticator.AUTHORIZATION);
+
+ if (authorization == null || !authorization.startsWith(KerberosAuthenticator.NEGOTIATE)) {
+ response.setHeader(WWW_AUTHENTICATE, KerberosAuthenticator.NEGOTIATE);
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ if (authorization == null) {
+ LOG.info("SPNEGO starting");
+ } else {
+ LOG.info("'" + KerberosAuthenticator.AUTHORIZATION + "' does not start with '" +
+ KerberosAuthenticator.NEGOTIATE + "' : {}", authorization);
+ }
+ } else {
+ authorization = authorization.substring(KerberosAuthenticator.NEGOTIATE.length()).trim();
+ final Base64 base64 = new Base64(0);
+ final byte[] clientToken = base64.decode(authorization);
+ final String serverName = request.getServerName();
+ LOG.info("Run Initial serverName Object: "+serverName);
+ LOG.info("Run Initial clientToken Object: "+clientToken);
+
+
+ try {
+ LOG.info(serverSubject.getPrincipals().toString());
+ for ( Principal principal : serverSubject.getPrincipals()) {
+ LOG.info(principal.getName());
+ }
+ token = Subject.doAs(serverSubject, new PrivilegedExceptionAction
+ *
+ */
+ @InterfaceAudience.Private
+ @InterfaceStability.Evolving
+ public class AltATSLdapKerberosDelegationAuthHandler
+ extends DelegationTokenAuthenticationHandler {
+
+ public AltATSLdapKerberosDelegationAuthHandler() {
+ super(new AltATSLdapKerberosAuthenticationHandler(AltATSLdapKerberosAuthenticationHandler.TYPE +
+ TYPE_POSTFIX));
+ }
+}