SOLR-3854: Allows the test harness to perform two-way SSL handshakes, the two-way SSL will now be randomly selected during test runs. Also, cleaned up some of the code by breaking out the SSLConfig into a separate class. Also try and address failing the statics retained check.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1566515 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Mark Robert Miller 2014-02-10 06:08:37 +00:00
parent fcfc592785
commit f5a1519069
14 changed files with 226 additions and 137 deletions

View File

@ -18,16 +18,14 @@
package org.apache.solr.client.solrj.embedded;
import java.io.IOException;
import java.util.Collections;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicLong;
import java.net.URL;
import java.net.MalformedURLException;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
@ -200,15 +198,6 @@ public class JettySolrRunner {
this.init(solrHome, context, port, stopAtShutdown);
}
public static class SSLConfig {
public boolean useSsl;
public boolean clientAuth;
public String keyStore;
public String keyStorePassword;
public String trustStore;
public String trustStorePassword;
}
private void init(String solrHome, String context, int port, boolean stopAtShutdown) {
this.context = context;
server = new Server(port);
@ -232,7 +221,7 @@ public class JettySolrRunner {
// the server as well as any client actions taken by this JVM in
// talking to that server, but for the purposes of testing that should
// be good enough
final boolean useSsl = sslConfig == null ? false : sslConfig.useSsl;
final boolean useSsl = sslConfig == null ? false : sslConfig.isSSLMode();
final SslContextFactory sslcontext = new SslContextFactory(false);
sslInit(useSsl, sslcontext);
@ -356,20 +345,20 @@ public class JettySolrRunner {
private void sslInit(final boolean useSsl, final SslContextFactory sslcontext) {
if (useSsl && sslConfig != null) {
if (null != sslConfig.keyStore) {
sslcontext.setKeyStorePath(sslConfig.keyStore);
if (null != sslConfig.getKeyStore()) {
sslcontext.setKeyStorePath(sslConfig.getKeyStore());
}
if (null != sslConfig.keyStorePassword) {
sslcontext.setKeyStorePassword(sslConfig.keyStorePassword);
if (null != sslConfig.getKeyStorePassword()) {
sslcontext.setKeyStorePassword(sslConfig.getKeyStorePassword());
}
if (null != sslConfig.trustStore) {
if (null != sslConfig.getTrustStore()) {
sslcontext.setTrustStore(System
.getProperty(sslConfig.trustStore));
.getProperty(sslConfig.getTrustStore()));
}
if (null != sslConfig.trustStorePassword) {
sslcontext.setTrustStorePassword(sslConfig.trustStorePassword);
if (null != sslConfig.getTrustStorePassword()) {
sslcontext.setTrustStorePassword(sslConfig.getTrustStorePassword());
}
sslcontext.setNeedClientAuth(sslConfig.clientAuth);
sslcontext.setNeedClientAuth(sslConfig.isClientAuthMode());
} else {
boolean jettySsl = Boolean.getBoolean(System.getProperty("tests.jettySsl"));

View File

@ -0,0 +1,69 @@
package org.apache.solr.client.solrj.embedded;
/*
* 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 SSLConfig {
private boolean useSsl;
private boolean clientAuth;
private String keyStore;
private String keyStorePassword;
private String trustStore;
private String trustStorePassword;
public SSLConfig(boolean useSSL, boolean clientAuth, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) {
this.useSsl = useSSL;
this.clientAuth = clientAuth;
this.keyStore = keyStore;
this.keyStorePassword = keyStorePassword;
this.trustStore = trustStore;
this.trustStorePassword = trustStorePassword;
}
public void setUseSSL(boolean useSSL) {
this.useSsl = useSSL;
}
public void setClientAuth(boolean clientAuth) {
this.clientAuth = clientAuth;
}
public boolean isSSLMode() {
return useSsl;
}
public boolean isClientAuthMode() {
return clientAuth;
}
public String getKeyStore() {
return keyStore;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public String getTrustStore() {
return trustStore;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
}

View File

@ -45,7 +45,8 @@ public class OverseerRolesTest extends AbstractFullDistribZkTestBase{
private CloudSolrServer client;
static {
sslConfig = null;
// SSL does not work with this feature for some reason
ALLOW_SSL = false;
}
@BeforeClass

View File

@ -102,7 +102,7 @@ public class TestReplicationHandler extends SolrTestCaseJ4 {
static {
// does not yet work with ssl
sslConfig = null;
ALLOW_SSL = false;
}
@BeforeClass

View File

@ -70,8 +70,8 @@ public class DistributedDebugComponentTest extends SolrJettyTestBase {
String urlCollection1 = jetty.getBaseUrl().toString() + "/" + "collection1";
String urlCollection2 = jetty.getBaseUrl().toString() + "/" + "collection2";
shard1 = urlCollection1.replaceAll("http" + (sslConfig == null || !sslConfig.useSsl ? "" : "s") + "://", "");
shard2 = urlCollection2.replaceAll("http" + (sslConfig == null || !sslConfig.useSsl ? "" : "s") + "://", "");
shard1 = urlCollection1.replaceAll("https?://", "");
shard2 = urlCollection2.replaceAll("https?://", "");
//create second core
CoreAdminRequest.Create req = new CoreAdminRequest.Create();

View File

@ -50,8 +50,8 @@ public class TestRemoteStreaming extends SolrJettyTestBase {
private static final File solrHomeDirectory = new File(TEMP_DIR, "TestRemoteStreaming");
static {
// does not yet work with ssl
sslConfig = null;
// does not yet work with ssl - uses raw URL
ALLOW_SSL = false;
}
@BeforeClass

View File

@ -40,11 +40,6 @@ import com.google.common.base.Charsets;
*/
public class CacheHeaderTest extends CacheHeaderTestBase {
private static final File solrHomeDirectory = new File(TEMP_DIR, "CacheHeaderTest");
static {
// does not yet work with ssl
sslConfig = null;
}
@BeforeClass
public static void beforeTest() throws Exception {

View File

@ -62,7 +62,7 @@ public class SolrCmdDistributorTest extends BaseDistributedSearchTestCase {
static {
// no ssl currently because distrib updates read scheme from zk and no zk in this test
sslConfig = null;
ALLOW_SSL = false;
}
@BeforeClass

View File

@ -48,6 +48,7 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.util.ExternalPaths;
import org.apache.solr.util.SSLTestConfig;
import org.junit.BeforeClass;
import org.junit.Test;
@ -506,8 +507,8 @@ public class BasicHttpSolrServerTest extends SolrJettyTestBase {
/**
* A trivial test that verifies the example keystore used for SSL testing can be
* found using the base class. this helps future-proof against hte possibility of
* something moving/breaking thekeystore path in a way that results in the SSL
* found using the base class. this helps future-proof against the possibility of
* something moving/breaking the keystore path in a way that results in the SSL
* randomization logic being forced to silently never use SSL. (We can't enforce
* this type of check in the base class because then it would not be usable by client
* code depending on the test framework
@ -516,7 +517,7 @@ public class BasicHttpSolrServerTest extends SolrJettyTestBase {
assertNotNull("Example keystore is null, meaning that something has changed in the " +
"structure of the example configs and/or ExternalPaths.java - " +
"SSL randomization is broken",
getExampleKeystoreFile());
SSLTestConfig.TEST_KEYSTORE);
}

View File

@ -24,9 +24,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.embedded.JettySolrRunner.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.util.ExternalPaths;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;

View File

@ -24,10 +24,6 @@ import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@ -46,11 +42,6 @@ import javax.xml.xpath.XPathExpressionException;
import org.apache.commons.codec.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.analysis.MockTokenizer;
import org.apache.lucene.index.IndexWriterConfig;
@ -58,7 +49,6 @@ import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.QuickPatchThreadsFilter;
import org.apache.lucene.util._TestUtil;
import org.apache.solr.client.solrj.embedded.JettySolrRunner.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpClientConfigurer;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.util.ClientUtils;
@ -89,8 +79,8 @@ import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.servlet.DirectSolrConnection;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.solr.util.ExternalPaths;
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
import org.apache.solr.util.SSLTestConfig;
import org.apache.solr.util.TestHarness;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -123,7 +113,10 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
private static String coreName = ConfigSolrXmlOld.DEFAULT_DEFAULT_CORE_NAME;
public static int DEFAULT_CONNECTION_TIMEOUT = 45000; // default socket connection timeout in ms
protected static volatile SSLConfig sslConfig = new SSLConfig();
// these are meant to be accessed sequentially, but are volatile just to ensure any test
// thread will read the latest value
protected static volatile boolean ALLOW_SSL = true;
protected static volatile SSLTestConfig sslConfig;
@ClassRule
public static TestRule solrClassRules =
@ -146,31 +139,13 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
startTrackingZkClients();
ignoreException("ignore_exception");
newRandomConfig();
sslConfig = getSSLConfig();
if(sslConfig != null && sslConfig.useSsl) {
sslConfig = buildSSLConfig();
//will use ssl specific or default depending on sslConfig
HttpClientUtil.setConfigurer(sslConfig.getHttpClientConfigurer());
if(isSSLMode()) {
// SolrCloud tests should usually clear this
System.setProperty("urlScheme", "https");
// Turn off two-way SSL since it isn't configured below...
sslConfig.clientAuth = false;
HttpClientUtil.setConfigurer(new HttpClientConfigurer(){
@SuppressWarnings("deprecation")
protected void configure(DefaultHttpClient httpClient, SolrParams config) {
super.configure(httpClient, config);
SchemeRegistry registry = httpClient.getConnectionManager().getSchemeRegistry();
// Make sure no tests cheat by using HTTP
registry.unregister("http");
try {
// Don't complain that we are using self-signed certs during the test
registry.register(new Scheme("https", 443, new SSLSocketFactory(new TrustSelfSignedStrategy())));
} catch (KeyManagementException | UnrecoverableKeyException
| NoSuchAlgorithmException | KeyStoreException ex) {
throw new IllegalStateException("Unable to setup https scheme for HTTPClient to test SSL.", ex);
}
}
});
}
}
@ -189,41 +164,16 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
System.clearProperty("useCompoundFile");
System.clearProperty("urlScheme");
if(sslConfig != null && sslConfig.useSsl) {
if(isSSLMode()) {
HttpClientUtil.setConfigurer(new HttpClientConfigurer());
}
// clean up static
sslConfig = null;
IpTables.unblockAllPorts();
}
private static File TEST_KEYSTORE;
static {
TEST_KEYSTORE = (null == ExternalPaths.SOURCE_HOME)
? null : new File(ExternalPaths.SOURCE_HOME, "example/etc/solrtest.keystore");
}
protected boolean isSSLMode() {
return sslConfig != null && sslConfig.useSsl;
}
private static void initSSLConfig(SSLConfig sslConfig, String keystorePath) {
sslConfig.useSsl = false;
sslConfig.clientAuth = false;
sslConfig.keyStore = keystorePath;
sslConfig.keyStorePassword = "secret";
sslConfig.trustStore = keystorePath;
sslConfig.trustStorePassword = "secret";
}
/**
* Returns the File object for the example keystore used when this baseclass randomly
* uses SSL. May be null ifthis test does not appear to be running as part of the
* standard solr distribution and does not have access to the example configs.
*
* @lucene.internal
*/
protected static File getExampleKeystoreFile() {
return TEST_KEYSTORE;
protected static boolean isSSLMode() {
return sslConfig != null && sslConfig.isSSLMode();
}
private static boolean changedFactory = false;
@ -252,38 +202,19 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
}
}
private static SSLConfig getSSLConfig() {
// test has disabled
if (sslConfig == null) {
SSLConfig sslConfig = new SSLConfig();
return sslConfig;
private static SSLTestConfig buildSSLConfig() {
// test has been disabled
if (!ALLOW_SSL) {
return new SSLTestConfig();
}
// only randomize SSL if we are a solr test with access to the example keystore
if (null == getExampleKeystoreFile()) {
log.info("Solr's example keystore not defined (not a solr test?) skipping SSL randomization");
return null;
}
assertTrue("test keystore does not exist, randomized ssl testing broken: " +
getExampleKeystoreFile().getAbsolutePath(),
getExampleKeystoreFile().exists() );
SSLConfig sslConfig = new SSLConfig();
final boolean trySsl = random().nextBoolean();
final boolean trySslClientAuth = false; // TODO: random().nextBoolean();
final boolean trySslClientAuth = random().nextBoolean();
log.info("Randomized ssl ({}) and clientAuth ({})", trySsl,
trySslClientAuth);
String keystorePath = null == TEST_KEYSTORE ? null : TEST_KEYSTORE
.getAbsolutePath();
initSSLConfig(sslConfig, keystorePath);
sslConfig.useSsl = trySsl;
sslConfig.clientAuth = trySslClientAuth;
return sslConfig;
return new SSLTestConfig(trySsl, trySslClientAuth);
}
protected static MockTokenizer whitespaceMockTokenizer(Reader input) throws IOException {

View File

@ -41,13 +41,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.lucene.util.LuceneTestCase.Slow;
import org.apache.solr.SolrJettyTestBase;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.embedded.JettySolrRunner.SSLConfig;
import org.apache.solr.client.solrj.impl.CloudSolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;

View File

@ -29,7 +29,6 @@ import org.apache.solr.common.params.MultiMapSolrParams;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.servlet.SolrRequestParsers;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
@ -38,11 +37,6 @@ abstract public class RestTestBase extends SolrJettyTestBase {
private static final Logger log = LoggerFactory.getLogger(RestTestBase.class);
protected static RestTestHarness restTestHarness;
@BeforeClass
public static void beforeClass() throws Exception {
// sslConfig = null;
}
public static void createJettyAndHarness
(String solrHome, String configFile, String schemaFile, String context,
boolean stopAtShutdown, SortedMap<ServletHolder,String> extraServlets) throws Exception {

View File

@ -0,0 +1,113 @@
package org.apache.solr.util;
/*
* 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.File;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.solr.client.solrj.embedded.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpClientConfigurer;
import org.apache.solr.common.params.SolrParams;
import org.eclipse.jetty.util.security.CertificateUtils;
public class SSLTestConfig extends SSLConfig {
public static File TEST_KEYSTORE = ExternalPaths.SOURCE_HOME == null ? null
: new File(ExternalPaths.SOURCE_HOME, "example/etc/solrtest.keystore");
private static String TEST_KEYSTORE_PATH = TEST_KEYSTORE != null
&& TEST_KEYSTORE.exists() ? TEST_KEYSTORE.getAbsolutePath() : null;
private static String TEST_KEYSTORE_PASSWORD = "secret";
private static HttpClientConfigurer DEFAULT_CONFIGURER = new HttpClientConfigurer();
public SSLTestConfig() {
this(false, false);
}
public SSLTestConfig(boolean useSSL, boolean clientAuth) {
super(useSSL, clientAuth, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD);
}
public SSLTestConfig(boolean useSSL, boolean clientAuth, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) {
super(useSSL, clientAuth, keyStore, keyStorePassword, trustStore, trustStorePassword);
}
/**
* Will provide an HttpClientConfigurer for SSL support (adds https and
* removes http schemes) is SSL is enabled, otherwise return the default
* configurer
*/
public HttpClientConfigurer getHttpClientConfigurer() {
return isSSLMode() ? new SSLHttpClientConfigurer() : DEFAULT_CONFIGURER;
}
/**
* Builds a new SSLContext with the given configuration and allows the uses of
* self-signed certificates during testing.
*/
protected SSLContext buildSSLContext() throws KeyManagementException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
return SSLContexts.custom()
.loadKeyMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), getKeyStorePassword().toCharArray())
.loadTrustMaterial(buildKeyStore(getTrustStore(), getTrustStorePassword()), new TrustSelfSignedStrategy()).build();
}
protected static KeyStore buildKeyStore(String keyStoreLocation, String password) {
try {
return CertificateUtils.getKeyStore(null, keyStoreLocation, "JKS", null, password);
} catch (Exception ex) {
throw new IllegalStateException("Unable to build KeyStore from file: " + keyStoreLocation, ex);
}
}
private class SSLHttpClientConfigurer extends HttpClientConfigurer {
@SuppressWarnings("deprecation")
protected void configure(DefaultHttpClient httpClient, SolrParams config) {
super.configure(httpClient, config);
SchemeRegistry registry = httpClient.getConnectionManager().getSchemeRegistry();
// Make sure no tests cheat by using HTTP
registry.unregister("http");
try {
registry.register(new Scheme("https", 443, new SSLSocketFactory(buildSSLContext())));
} catch (KeyManagementException | UnrecoverableKeyException
| NoSuchAlgorithmException | KeyStoreException ex) {
throw new IllegalStateException("Unable to setup https scheme for HTTPClient to test SSL.", ex);
}
}
}
public static void cleanStatics() {
DEFAULT_CONFIGURER = null;
TEST_KEYSTORE = null;
TEST_KEYSTORE_PASSWORD = null;
TEST_KEYSTORE_PATH = null;
}
}