You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by an...@apache.org on 2015/05/22 08:26:10 UTC

svn commit: r1681001 - in /lucene/dev/trunk: lucene/tools/junit4/ solr/ solr/core/src/java/org/apache/solr/security/ solr/core/src/test/org/apache/solr/cloud/ solr/solrj/src/java/org/apache/solr/client/solrj/impl/

Author: anshum
Date: Fri May 22 06:26:10 2015
New Revision: 1681001

URL: http://svn.apache.org/r1681001
Log:
SOLR-7468: Kerberos plugin for authentication framework. This will enable using Kerberos for authentication in Solr.

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosFilter.java   (with props)
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterKerberos.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithKerberos.java   (with props)
    lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java   (with props)
Modified:
    lucene/dev/trunk/lucene/tools/junit4/solr-tests.policy
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/KerberosTestUtil.java

Modified: lucene/dev/trunk/lucene/tools/junit4/solr-tests.policy
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/tools/junit4/solr-tests.policy?rev=1681001&r1=1681000&r2=1681001&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/tools/junit4/solr-tests.policy (original)
+++ lucene/dev/trunk/lucene/tools/junit4/solr-tests.policy Fri May 22 06:26:10 2015
@@ -88,4 +88,7 @@ grant {
   permission javax.security.auth.kerberos.ServicePermission "krbtgt/EXAMPLE.COM@EXAMPLE.COM", "initiate";
   permission javax.security.auth.kerberos.ServicePermission "zookeeper/127.0.0.1@EXAMPLE.COM", "initiate";
   permission javax.security.auth.kerberos.ServicePermission "zookeeper/127.0.0.1@EXAMPLE.COM", "accept";
+  permission javax.security.auth.kerberos.ServicePermission "HTTP/127.0.0.1@EXAMPLE.COM", "initiate";
+  permission javax.security.auth.kerberos.ServicePermission "HTTP/127.0.0.1@EXAMPLE.COM", "accept";
+
 };

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1681001&r1=1681000&r2=1681001&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Fri May 22 06:26:10 2015
@@ -219,6 +219,9 @@ New Features
 
 * SOLR-7465: New file indexing example, under example/files.  (Esther Quansah, Erik Hatcher)
 
+* SOLR-7468: Kerberos authenticaion plugin for Solr. This would allow running a Kerberized Solr.
+  (Noble Paul, Ishan Chattopadhyaya, Gregory Chanan, Anshum Gupta)
+
 Bug Fixes
 ----------------------
 

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosFilter.java?rev=1681001&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosFilter.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosFilter.java Fri May 22 06:26:10 2015
@@ -0,0 +1,52 @@
+package org.apache.solr.security;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * 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.
+ */
+
+public class KerberosFilter extends AuthenticationFilter {
+  
+  static final Logger log = LoggerFactory.getLogger(KerberosFilter.class);
+  
+  @Override
+  public void init(FilterConfig conf) throws ServletException {
+    super.init(conf);
+  }
+
+  @Override
+  protected void doFilter(FilterChain filterChain, HttpServletRequest request,
+      HttpServletResponse response) throws IOException, ServletException {
+    super.doFilter(filterChain, request, response);
+  }
+  
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response,
+      FilterChain filterChain) throws IOException, ServletException {
+    super.doFilter(request, response, filterChain);
+  }
+}

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java?rev=1681001&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java Fri May 22 06:26:10 2015
@@ -0,0 +1,359 @@
+package org.apache.solr.security;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.FilterRegistration;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.descriptor.JspConfigDescriptor;
+
+import org.apache.commons.collections.iterators.IteratorEnumeration;
+import org.apache.solr.client.solrj.impl.HttpClientConfigurer;
+import org.apache.solr.client.solrj.impl.Krb5HttpClientConfigurer;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * 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.
+ */
+
+public class KerberosPlugin extends AuthenticationPlugin {
+  static final Logger log = LoggerFactory.getLogger(KerberosPlugin.class);
+
+  HttpClientConfigurer kerberosConfigurer = new Krb5HttpClientConfigurer();
+  Filter kerberosFilter = new KerberosFilter();
+  
+  @Override
+  public void init(Map<String, Object> pluginConfig) {
+    try {
+      Map<String, String> params = new HashMap();
+      params.put("type", "kerberos");
+      params.put("kerberos.name.rules", System.getProperty("solr.kerberos.name.rules", "DEFAULT"));
+      params.put("token.valid", System.getProperty("solr.kerberos.token.valid", "30"));
+      params.put("cookie.domain", System.getProperty("solr.kerberos.cookie.domain"));
+      params.put("cookie.path", System.getProperty("solr.kerberos.cookie.path", "/"));
+      params.put("kerberos.principal", System.getProperty("solr.kerberos.principal"));
+      params.put("kerberos.keytab", System.getProperty("solr.kerberos.keytab"));
+
+      log.info("Params: "+params);
+
+      FilterConfig conf = new FilterConfig() {
+        @Override
+        public ServletContext getServletContext() {
+          return noContext;
+        }
+
+        @Override
+        public Enumeration<String> getInitParameterNames() {
+          return new IteratorEnumeration(params.keySet().iterator());
+        }
+
+        @Override
+        public String getInitParameter(String param) {
+          return params.get(param);
+        }
+
+        @Override
+        public String getFilterName() {
+          return "KerberosFilter";
+        }
+      };
+
+      kerberosFilter.init(conf);
+    } catch (ServletException e) {
+      throw new SolrException(ErrorCode.SERVER_ERROR, "Error initializing kerberos authentication plugin: "+e);
+    }
+  }
+
+  @Override
+  public void doAuthenticate(ServletRequest req, ServletResponse rsp,
+      FilterChain chain) throws Exception {
+    log.debug("Request to authenticate using kerberos: "+req);
+    kerberosFilter.doFilter(req, rsp, chain);    
+  }
+
+  @Override
+  public HttpClientConfigurer getDefaultConfigurer() {
+    return kerberosConfigurer;
+  }
+
+  public void close() {
+    kerberosFilter.destroy();
+  }
+
+  protected static ServletContext noContext = new ServletContext() {
+    
+    @Override
+    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {}
+    
+    @Override
+    public boolean setInitParameter(String name, String value) {
+      return false;
+    }
+    
+    @Override
+    public void setAttribute(String name, Object object) {}
+    
+    @Override
+    public void removeAttribute(String name) {}
+    
+    @Override
+    public void log(String message, Throwable throwable) {}
+    
+    @Override
+    public void log(Exception exception, String msg) {}
+    
+    @Override
+    public void log(String msg) {}
+    
+    @Override
+    public String getVirtualServerName() {
+      return null;
+    }
+    
+    @Override
+    public SessionCookieConfig getSessionCookieConfig() {
+      return null;
+    }
+    
+    @Override
+    public Enumeration<Servlet> getServlets() {
+      return null;
+    }
+    
+    @Override
+    public Map<String,? extends ServletRegistration> getServletRegistrations() {
+      return null;
+    }
+    
+    @Override
+    public ServletRegistration getServletRegistration(String servletName) {
+      return null;
+    }
+    
+    @Override
+    public Enumeration<String> getServletNames() {
+      return null;
+    }
+    
+    @Override
+    public String getServletContextName() {
+      return null;
+    }
+    
+    @Override
+    public Servlet getServlet(String name) throws ServletException {
+      return null;
+    }
+    
+    @Override
+    public String getServerInfo() {
+      return null;
+    }
+    
+    @Override
+    public Set<String> getResourcePaths(String path) {
+      return null;
+    }
+    
+    @Override
+    public InputStream getResourceAsStream(String path) {
+      return null;
+    }
+    
+    @Override
+    public URL getResource(String path) throws MalformedURLException {
+      return null;
+    }
+    
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path) {
+      return null;
+    }
+    
+    @Override
+    public String getRealPath(String path) {
+      return null;
+    }
+    
+    @Override
+    public RequestDispatcher getNamedDispatcher(String name) {
+      return null;
+    }
+    
+    @Override
+    public int getMinorVersion() {
+      return 0;
+    }
+    
+    @Override
+    public String getMimeType(String file) {
+      return null;
+    }
+    
+    @Override
+    public int getMajorVersion() {
+      return 0;
+    }
+    
+    @Override
+    public JspConfigDescriptor getJspConfigDescriptor() {
+      return null;
+    }
+    
+    @Override
+    public Enumeration<String> getInitParameterNames() {
+      return null;
+    }
+    
+    @Override
+    public String getInitParameter(String name) {
+      return null;
+    }
+    
+    @Override
+    public Map<String,? extends FilterRegistration> getFilterRegistrations() {
+      return null;
+    }
+    
+    @Override
+    public FilterRegistration getFilterRegistration(String filterName) {
+      return null;
+    }
+    
+    @Override
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+      return null;
+    }
+    
+    @Override
+    public int getEffectiveMinorVersion() {
+      return 0;
+    }
+    
+    @Override
+    public int getEffectiveMajorVersion() {
+      return 0;
+    }
+    
+    @Override
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+      return null;
+    }
+    
+    @Override
+    public String getContextPath() {
+      return null;
+    }
+    
+    @Override
+    public ServletContext getContext(String uripath) {
+      return null;
+    }
+    
+    @Override
+    public ClassLoader getClassLoader() {
+      return null;
+    }
+    
+    @Override
+    public Enumeration<String> getAttributeNames() {
+      return null;
+    }
+    
+    @Override
+    public Object getAttribute(String name) {
+      return null;
+    }
+    
+    @Override
+    public void declareRoles(String... roleNames) {}
+    
+    @Override
+    public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException {
+      return null;
+    }
+    
+    @Override
+    public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException {
+      return null;
+    }
+    
+    @Override
+    public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException {
+      return null;
+    }
+    
+    @Override
+    public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {
+      return null;
+    }
+    
+    @Override
+    public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
+      return null;
+    }
+    
+    @Override
+    public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className) {
+      return null;
+    }
+    
+    @Override
+    public void addListener(Class<? extends EventListener> listenerClass) {}
+    
+    @Override
+    public <T extends EventListener> void addListener(T t) {}
+    
+    @Override
+    public void addListener(String className) {}
+    
+    @Override
+    public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {
+      return null;
+    }
+    
+    @Override
+    public Dynamic addFilter(String filterName, Filter filter) {
+      return null;
+    }
+    
+    @Override
+    public Dynamic addFilter(String filterName, String className) {
+      return null;
+    }
+  };
+}

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/KerberosTestUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/KerberosTestUtil.java?rev=1681001&r1=1681000&r2=1681001&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/KerberosTestUtil.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/KerberosTestUtil.java Fri May 22 06:26:10 2015
@@ -46,6 +46,7 @@ public class KerberosTestUtil {
 
     private static AppConfigurationEntry[] clientEntry;
     private static AppConfigurationEntry[] serverEntry;
+    private String clientAppName = "Client", serverAppName = "Server";
 
     /**
      * Add an entry to the jaas configuration with the passed in name,
@@ -58,7 +59,7 @@ public class KerberosTestUtil {
      */
     public JaasConfiguration(String clientPrincipal, File clientKeytab,
         String serverPrincipal, File serverKeytab) {
-      Map<String, String> clientOptions = new HashMap<String, String>();
+      Map<String, String> clientOptions = new HashMap();
       clientOptions.put("principal", clientPrincipal);
       clientOptions.put("keyTab", clientKeytab.getAbsolutePath());
       clientOptions.put("useKeyTab", "true");
@@ -73,20 +74,36 @@ public class KerberosTestUtil {
         new AppConfigurationEntry(getKrb5LoginModuleName(),
         AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
         clientOptions)};
-      Map<String, String> serverOptions = new HashMap<String, String>(clientOptions);
-      serverOptions.put("principal", serverPrincipal);
-      serverOptions.put("keytab", serverKeytab.getAbsolutePath());
-      serverEntry =  new AppConfigurationEntry[]{
-        new AppConfigurationEntry(getKrb5LoginModuleName(),
-        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
-        serverOptions)};
+      if(serverPrincipal!=null && serverKeytab!=null) {
+        Map<String, String> serverOptions = new HashMap(clientOptions);
+        serverOptions.put("principal", serverPrincipal);
+        serverOptions.put("keytab", serverKeytab.getAbsolutePath());
+        serverEntry =  new AppConfigurationEntry[]{
+            new AppConfigurationEntry(getKrb5LoginModuleName(),
+                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                serverOptions)};
+      }
+    }
+
+    /**
+     * Add an entry to the jaas configuration with the passed in principal and keytab, 
+     * along with the app name.
+     * 
+     * @param principal The principal
+     * @param keytab The keytab containing credentials for the principal
+     * @param appName The app name of the configuration
+     */
+    public JaasConfiguration(String principal, File keytab, String appName) {
+      this(principal, keytab, null, null);
+      clientAppName = appName;
+      serverAppName = null;
     }
 
     @Override
     public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-      if ("Client".equals(name)) {
+      if (name.equals(clientAppName)) {
         return clientEntry;
-      } else if ("Server".equals(name)) {
+      } else if (name.equals(serverAppName)) {
         return serverEntry;
       }
       return null;

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterKerberos.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterKerberos.java?rev=1681001&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterKerberos.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterKerberos.java Fri May 22 06:26:10 2015
@@ -0,0 +1,176 @@
+package org.apache.solr.cloud;
+
+/*
+ * 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.
+ */
+
+import javax.security.auth.login.Configuration;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
+import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.LuceneTestCase.SuppressSysoutChecks;
+import org.apache.solr.util.BadZookeeperThreadsFilter;
+import org.apache.solr.util.RevertDefaultThreadHandlerRule;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
+/**
+ * Test 5 nodes Solr cluster with Kerberos plugin enabled.
+ * This test is Ignored right now as Mini KDC has a known bug that
+ * doesn't allow us to run multiple nodes on the same host.
+ * https://issues.apache.org/jira/browse/HADOOP-9893
+ */
+@ThreadLeakFilters(defaultFilters = true, filters = {
+    BadZookeeperThreadsFilter.class // Zookeeper login leaks TGT renewal threads
+})
+
+@Ignore
+@LuceneTestCase.Slow
+@SuppressSysoutChecks(bugUrl = "Solr logs to JUL")
+public class TestMiniSolrCloudClusterKerberos extends TestMiniSolrCloudCluster {
+
+  private final Configuration originalConfig = Configuration.getConfiguration();
+
+  public TestMiniSolrCloudClusterKerberos () {
+    NUM_SERVERS = 5;
+    NUM_SHARDS = 2;
+    REPLICATION_FACTOR = 2;
+  }
+  
+  protected final static List<String> brokenLocales =
+      Arrays.asList(
+        "th_TH_TH_#u-nu-thai",
+        "ja_JP_JP_#u-ca-japanese",
+        "hi_IN");
+
+  private MiniKdc kdc;
+
+  @Rule
+  public TestRule solrTestRules = RuleChain
+      .outerRule(new SystemPropertiesRestoreRule());
+
+  @ClassRule
+  public static TestRule solrClassRules = RuleChain.outerRule(
+      new SystemPropertiesRestoreRule()).around(
+      new RevertDefaultThreadHandlerRule());
+
+  @Override
+  public void setUp() throws Exception {
+    if (brokenLocales.contains(Locale.getDefault().toString())) {
+      Locale.setDefault(Locale.US);
+    }
+    super.setUp();
+    setupMiniKdc();
+  }
+  
+  private void setupMiniKdc() throws Exception {
+    String kdcDir = createTempDir()+File.separator+"minikdc";
+    kdc = KerberosTestUtil.getKdc(new File(kdcDir));
+    File keytabFile = new File(kdcDir, "keytabs");
+    String principal = "HTTP/127.0.0.1";
+    String zkServerPrincipal = "zookeeper/127.0.0.1";
+
+    kdc.start();
+    kdc.createPrincipal(keytabFile, principal, zkServerPrincipal);
+
+    String jaas = "Client {\n"
+        + " com.sun.security.auth.module.Krb5LoginModule required\n"
+        + " useKeyTab=true\n"
+        + " keyTab=\""+keytabFile.getAbsolutePath()+"\"\n"
+        + " storeKey=true\n"
+        + " useTicketCache=false\n"
+        + " doNotPrompt=true\n"
+        + " debug=true\n"
+        + " principal=\""+principal+"\";\n" 
+        + "};\n"
+        + "Server {\n"
+        + " com.sun.security.auth.module.Krb5LoginModule required\n"
+        + " useKeyTab=true\n"
+        + " keyTab=\""+keytabFile.getAbsolutePath()+"\"\n"
+        + " storeKey=true\n"
+        + " doNotPrompt=true\n"
+        + " useTicketCache=false\n"
+        + " debug=true\n"
+        + " principal=\""+zkServerPrincipal+"\";\n" 
+        + "};\n";
+    
+    Configuration conf = new KerberosTestUtil.JaasConfiguration(principal, keytabFile, zkServerPrincipal, keytabFile);
+    javax.security.auth.login.Configuration.setConfiguration(conf);
+    
+    String jaasFilePath = kdcDir+File.separator + "jaas-client.conf";
+    FileUtils.write(new File(jaasFilePath), jaas);
+    System.setProperty("java.security.auth.login.config", jaasFilePath);
+    System.setProperty("solr.kerberos.cookie.domain", "127.0.0.1");
+    System.setProperty("solr.kerberos.principal", principal);
+    System.setProperty("solr.kerberos.keytab", keytabFile.getAbsolutePath());
+    System.setProperty("authenticationPlugin", "org.apache.solr.security.KerberosPlugin");
+
+    // more debugging, if needed
+    /*System.setProperty("sun.security.jgss.debug", "true");
+    System.setProperty("sun.security.krb5.debug", "true");
+    System.setProperty("sun.security.jgss.debug", "true");
+    System.setProperty("java.security.debug", "logincontext,policy,scl,gssloginconfig");*/
+  }
+  
+  @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/HADOOP-9893")
+  @Test
+  @Override
+  public void testBasics() throws Exception {
+    testCollectionCreateSearchDelete();
+    // sometimes run a second test e.g. to test collection create-delete-create scenario
+    if (random().nextBoolean()) testCollectionCreateSearchDelete();
+  }
+
+  @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/HADOOP-9893")
+  @Test
+  @Override
+  public void testErrorsInShutdown() throws Exception {
+    super.testErrorsInShutdown();
+  }
+
+  @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/HADOOP-9893")
+  @Test
+  @Override
+  public void testErrorsInStartup() throws Exception {
+    super.testErrorsInStartup();
+  }
+  
+  @Override
+  public void tearDown() throws Exception {
+    System.clearProperty("java.security.auth.login.config");
+    System.clearProperty("cookie.domain");
+    System.clearProperty("kerberos.principal");
+    System.clearProperty("kerberos.keytab");
+    System.clearProperty("authenticationPlugin");
+    Configuration.setConfiguration(this.originalConfig);
+    if (kdc != null) {
+      kdc.stop();
+    }
+    super.tearDown();
+  }
+}

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithKerberos.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithKerberos.java?rev=1681001&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithKerberos.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithKerberos.java Fri May 22 06:26:10 2015
@@ -0,0 +1,183 @@
+package org.apache.solr.cloud;
+
+/*
+ * 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.
+ */
+
+import javax.security.auth.login.Configuration;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.commons.io.Charsets;
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.zookeeper.CreateMode;
+import org.junit.Test;
+
+@SolrTestCaseJ4.SuppressSSL
+@LuceneTestCase.Slow
+public class TestSolrCloudWithKerberos extends AbstractFullDistribZkTestBase {
+
+  static final int TIMEOUT = 10000;
+  private MiniKdc kdc;
+
+  protected final static List<String> brokenLocales =
+      Arrays.asList(
+          "th_TH_TH_#u-nu-thai",
+          "ja_JP_JP_#u-ca-japanese",
+          "hi_IN");
+
+  Configuration originalConfig = Configuration.getConfiguration();
+
+  @Override
+  public void distribSetUp() throws Exception {
+    //SSLTestConfig.setSSLSystemProperties();
+    if (brokenLocales.contains(Locale.getDefault().toString())) {
+      Locale.setDefault(Locale.US);
+    }
+    setupMiniKdc();
+    super.distribSetUp();
+    //useExternalKdc();
+    try (ZkStateReader zkStateReader = new ZkStateReader(zkServer.getZkAddress(), TIMEOUT, TIMEOUT)) {
+      zkStateReader.getZkClient().create(ZkStateReader.SOLR_SECURITY_CONF_PATH,
+          "{\"authentication\":{\"class\":\"org.apache.solr.security.KerberosPlugin\"}}".getBytes(Charsets.UTF_8),
+          CreateMode.PERSISTENT, true);
+    }
+  }
+
+  private void setupMiniKdc() throws Exception {
+    String kdcDir = createTempDir()+File.separator+"minikdc";
+    kdc = KerberosTestUtil.getKdc(new File(kdcDir));
+    File keytabFile = new File(kdcDir, "keytabs");
+    String solrServerPrincipal = "HTTP/127.0.0.1";
+    String zkServerPrincipal = "zookeeper/127.0.0.1";
+
+    kdc.start();
+    kdc.createPrincipal(keytabFile, solrServerPrincipal, zkServerPrincipal);
+
+    String jaas = "SolrClient {\n"
+        + " com.sun.security.auth.module.Krb5LoginModule required\n"
+        + " useKeyTab=true\n"
+        + " keyTab=\"" + keytabFile.getAbsolutePath() + "\"\n"
+        + " storeKey=true\n"
+        + " useTicketCache=false\n"
+        + " doNotPrompt=true\n"
+        + " debug=true\n"
+        + " principal=\"" + solrServerPrincipal + "\";\n"
+        + "};";
+
+    Configuration conf = new KerberosTestUtil.JaasConfiguration(solrServerPrincipal, keytabFile, "SolrClient");
+    Configuration.setConfiguration(conf);
+
+    String jaasFilePath = kdcDir+File.separator+"jaas-client.conf";
+    FileUtils.write(new File(jaasFilePath), jaas);
+    System.setProperty("java.security.auth.login.config", jaasFilePath);
+    System.setProperty("solr.kerberos.jaas.appname", "SolrClient"); // Get this app name from the jaas file
+    System.setProperty("solr.kerberos.cookie.domain", "127.0.0.1");
+    System.setProperty("solr.kerberos.principal", solrServerPrincipal);
+    System.setProperty("solr.kerberos.keytab", keytabFile.getAbsolutePath());
+    
+    // more debugging, if needed
+    /*System.setProperty("sun.security.jgss.debug", "true");
+    System.setProperty("sun.security.krb5.debug", "true");
+    System.setProperty("sun.security.jgss.debug", "true");
+    System.setProperty("java.security.debug", "logincontext,policy,scl,gssloginconfig");*/
+  }
+  
+  //This method can be used for debugging i.e. to use an external KDC for the test.
+  private void useExternalKdc() throws Exception {
+
+    String jaas = "Client {\n"
+        +"  com.sun.security.auth.module.Krb5LoginModule required\n"
+        +"  useKeyTab=true\n"
+        +"  keyTab=\"/tmp/127.keytab\"\n"
+        +"  storeKey=true\n"
+        +"  useTicketCache=false\n"
+        +"  debug=true\n"
+        +"  principal=\"HTTP/127.0.0.1\";\n"
+        +"};\n"
+        + "\n"
+        + "Server {\n"
+        +"  com.sun.security.auth.module.Krb5LoginModule optional\n"
+        +"  useKeyTab=true\n"
+        +"  keyTab=\"/tmp/127.keytab\"\n"
+        +"  storeKey=true\n"
+        +"  useTicketCache=false\n"
+        +"  debug=true\n"
+        +"  principal=\"zookeeper/127.0.0.1\";\n"
+        +"};";
+
+    String tmpDir = createTempDir().toString();
+    FileUtils.write(new File(tmpDir + File.separator + "jaas.conf"), jaas);
+
+    System.setProperty("java.security.auth.login.config", tmpDir + File.separator + "jaas.conf");
+    System.setProperty("solr.kerberos.jaas.appname", "Client");
+    System.setProperty("solr.kerberos.cookie.domain", "127.0.0.1");
+    System.setProperty("solr.kerberos.principal", "HTTP/127.0.0.1@EXAMPLE.COM");
+    System.setProperty("solr.kerberos.keytab", "/tmp/127.keytab");
+    System.setProperty("authenticationPlugin", "org.apache.solr.security.KerberosPlugin");
+  }
+  
+  @Test
+  public void testKerberizedSolr() throws Exception {
+    CollectionAdminRequest.Create create = new CollectionAdminRequest.Create();
+    create.setCollectionName("testcollection");
+    create.setConfigName("conf1");
+    create.setNumShards(1);
+    create.setReplicationFactor(1);
+    create.process(cloudClient);
+    
+    waitForCollection(cloudClient.getZkStateReader(), "testcollection", 1);
+    CollectionAdminRequest.List list = new CollectionAdminRequest.List();
+    
+    CollectionAdminResponse response = list.process(cloudClient);
+    assertTrue("Expected to see testcollection but it doesn't exist",
+        ((ArrayList) response.getResponse().get("collections")).contains("testcollection"));
+    
+    cloudClient.setDefaultCollection("testcollection");
+    indexDoc(cloudClient, params("commit", "true"), getDoc("id", 1));
+    //cloudClient.commit();
+
+    QueryResponse queryResponse = cloudClient.query(new SolrQuery("*:*"));
+    assertEquals("Expected #docs and actual isn't the same", 1, queryResponse.getResults().size());
+  }
+  
+  @Override
+  public void distribTearDown() throws Exception {
+    System.clearProperty("java.security.auth.login.config");
+    System.clearProperty("solr.kerberos.jaas.appname");
+    System.clearProperty("solr.cookie.domain");
+    System.clearProperty("solr.kerberos.principal");
+    System.clearProperty("solr.kerberos.keytab");
+    Configuration.setConfiguration(originalConfig);
+    if (kdc != null) {
+      kdc.stop();
+    }
+    //SSLTestConfig.clearSSLSystemProperties();
+    super.distribTearDown();
+  }
+}

Added: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java?rev=1681001&view=auto
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java (added)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java Fri May 22 06:26:10 2015
@@ -0,0 +1,144 @@
+package org.apache.solr.client.solrj.impl;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.client.config.AuthSchemes;
+import org.apache.solr.common.params.SolrParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.http.entity.BufferedHttpEntity;
+
+/**
+ * Kerberos-enabled HttpClientConfigurer
+ */
+public class Krb5HttpClientConfigurer extends HttpClientConfigurer {
+  
+  public static final String LOGIN_CONFIG_PROP = "java.security.auth.login.config";
+  private static final Logger logger = LoggerFactory.getLogger(Krb5HttpClientConfigurer.class);
+  
+  private static final Configuration jaasConfig = new SolrJaasConfiguration();
+
+  public void configure(DefaultHttpClient httpClient, SolrParams config) {
+    super.configure(httpClient, config);
+
+    if (System.getProperty(LOGIN_CONFIG_PROP) != null) {
+      String configValue = System.getProperty(LOGIN_CONFIG_PROP);
+
+      if (configValue != null) {
+        logger.info("Setting up SPNego auth with config: " + configValue);
+        final String useSubjectCredsProp = "javax.security.auth.useSubjectCredsOnly";
+        String useSubjectCredsVal = System.getProperty(useSubjectCredsProp);
+
+        // "javax.security.auth.useSubjectCredsOnly" should be false so that the underlying
+        // authentication mechanism can load the credentials from the JAAS configuration.
+        if (useSubjectCredsVal == null) {
+          System.setProperty(useSubjectCredsProp, "false");
+        }
+        else if (!useSubjectCredsVal.toLowerCase(Locale.ROOT).equals("false")) {
+          // Don't overwrite the prop value if it's already been written to something else,
+          // but log because it is likely the Credentials won't be loaded correctly.
+          logger.warn("System Property: " + useSubjectCredsProp + " set to: " + useSubjectCredsVal
+              + " not false.  SPNego authentication may not be successful.");
+        }
+
+        javax.security.auth.login.Configuration.setConfiguration(jaasConfig);
+        httpClient.getAuthSchemes().register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, false));
+        // Get the credentials from the JAAS configuration rather than here
+        Credentials useJaasCreds = new Credentials() {
+          public String getPassword() {
+            return null;
+          }
+          public Principal getUserPrincipal() {
+            return null;
+          }
+        };
+
+        httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, useJaasCreds);
+
+        httpClient.addRequestInterceptor(bufferedEntityInterceptor);
+      } else {
+        httpClient.getCredentialsProvider().clear();
+      }
+    }
+  }
+
+  // Set a buffered entity based request interceptor
+  private HttpRequestInterceptor bufferedEntityInterceptor = new HttpRequestInterceptor() {
+    @Override
+    public void process(HttpRequest request, HttpContext context) throws HttpException,
+        IOException {
+      if(request instanceof HttpEntityEnclosingRequest) {
+        HttpEntityEnclosingRequest enclosingRequest = ((HttpEntityEnclosingRequest) request);  
+        HttpEntity requestEntity = enclosingRequest.getEntity();
+        enclosingRequest.setEntity(new BufferedHttpEntity(requestEntity));
+      }
+    }
+  };
+
+  private static class SolrJaasConfiguration extends javax.security.auth.login.Configuration {
+
+    private javax.security.auth.login.Configuration baseConfig;
+
+    // the com.sun.security.jgss appNames
+    private Set<String> initiateAppNames = new HashSet(
+      Arrays.asList("com.sun.security.jgss.krb5.initiate", "com.sun.security.jgss.initiate"));
+
+    public SolrJaasConfiguration() {
+      try {
+        
+        this.baseConfig = javax.security.auth.login.Configuration.getConfiguration();
+      } catch (SecurityException e) {
+        this.baseConfig = null;
+      }
+    }
+
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
+      if (baseConfig == null) return null;
+
+      logger.debug("Login prop: "+System.getProperty(LOGIN_CONFIG_PROP));
+
+      String clientAppName = System.getProperty("solr.kerberos.jaas.appname", "Client");
+      if (initiateAppNames.contains(appName)) {
+        logger.debug("Using AppConfigurationEntry for appName '"+clientAppName+"' instead of: " + appName);
+        return baseConfig.getAppConfigurationEntry(clientAppName);
+      }
+      return baseConfig.getAppConfigurationEntry(appName);
+    }
+  }
+}