All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.druid.security.kerberos.DruidKerberosAuthenticationHandler Maven / Gradle / Ivy

The newest version!
/*
 * 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.druid.security.kerberos;

import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.server.AuthenticationToken;
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
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 javax.security.auth.Subject;
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.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;

public class DruidKerberosAuthenticationHandler extends KerberosAuthenticationHandler
{
  private static final Logger log = new Logger(DruidKerberosAuthenticationHandler.class);

  private String keytab;
  private GSSManager gssManager;
  private Subject serverSubject = new Subject();
  private List loginContexts = new ArrayList();

  @Override
  public void destroy()
  {
    keytab = null;
    serverSubject = null;
    for (LoginContext loginContext : loginContexts) {
      try {
        loginContext.logout();
      }
      catch (LoginException ex) {
        log.warn(ex, ex.getMessage());
      }
    }
    loginContexts.clear();
  }

  @Override
  public void init(Properties config) throws ServletException
  {
    try {
      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 ("*".equals(principal)) {
        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 %s, for principal %s", keytab, spnegoPrincipal);
        final KerberosAuthenticator.DruidKerberosConfiguration kerberosConfiguration =
            new KerberosAuthenticator.DruidKerberosConfiguration(keytab, spnegoPrincipal);
        final LoginContext loginContext =
            new LoginContext("", serverSubject, null, kerberosConfiguration);
        try {
          loginContext.login();
        }
        catch (LoginException le) {
          log.warn(le, "Failed to login as [%s]", spnegoPrincipal);
          throw new AuthenticationException(le);
        }
        loginContexts.add(loginContext);
      }
      try {
        gssManager = Subject.doAs(serverSubject, new PrivilegedExceptionAction()
        {

          @Override
          public GSSManager run()
          {
            return GSSManager.getInstance();
          }
        });
      }
      catch (PrivilegedActionException ex) {
        throw ex.getException();
      }
    }
    catch (Exception ex) {
      throw new ServletException(ex);
    }
  }

  @Override
  public AuthenticationToken authenticate(HttpServletRequest request, final HttpServletResponse response)
      throws IOException, AuthenticationException
  {
    AuthenticationToken token;
    String authorization = request
        .getHeader(org.apache.hadoop.security.authentication.client.KerberosAuthenticator.AUTHORIZATION);

    if (authorization == null ||
        !authorization.startsWith(org.apache.hadoop.security.authentication.client.KerberosAuthenticator.NEGOTIATE)) {
      return null;
    } else {
      authorization = authorization
          .substring(org.apache.hadoop.security.authentication.client.KerberosAuthenticator.NEGOTIATE.length())
          .trim();
      final byte[] clientToken = StringUtils.decodeBase64String(authorization);
      final String serverName = request.getServerName();
      try {
        token = Subject.doAs(serverSubject, new PrivilegedExceptionAction()
        {

          @Override
          public AuthenticationToken run() throws Exception
          {
            AuthenticationToken token = null;
            GSSContext gssContext = null;
            GSSCredential gssCreds = null;
            try {
              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) {
                String authenticate = StringUtils.encodeBase64String(serverToken);
                response.setHeader(
                    org.apache.hadoop.security.authentication.client.KerberosAuthenticator.WWW_AUTHENTICATE,
                    org.apache.hadoop.security.authentication.client.KerberosAuthenticator.NEGOTIATE
                    + " "
                    + authenticate
                );
              }
              if (!gssContext.isEstablished()) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                log.trace("SPNEGO in progress");
              } else {
                String clientPrincipal = gssContext.getSrcName().toString();
                KerberosName kerberosName = new KerberosName(clientPrincipal);
                String userName = kerberosName.getShortName();
                token = new AuthenticationToken(userName, clientPrincipal, getType());
                response.setStatus(HttpServletResponse.SC_OK);
                log.trace("SPNEGO completed for principal [%s]", clientPrincipal);
              }
            }
            finally {
              if (gssContext != null) {
                gssContext.dispose();
              }
              if (gssCreds != null) {
                gssCreds.dispose();
              }
            }
            return token;
          }
        });
      }
      catch (PrivilegedActionException ex) {
        if (ex.getException() instanceof IOException) {
          throw (IOException) ex.getException();
        } else {
          throw new AuthenticationException(ex.getException());
        }
      }
    }
    return token;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy