From 76063648ae05a935459f2ea5ed53c4df1caa713d Mon Sep 17 00:00:00 2001 From: Chris Hostetter Date: Wed, 11 May 2016 16:28:07 -0700 Subject: [PATCH] SOLR-8970: Change SSLTestConfig to use a keystore file that is included as a resource in the test-framework jar so users subclassing SolrTestCaseJ4 don't need to preserve magic paths --- solr/CHANGES.txt | 3 + solr/test-framework/build.xml | 6 ++ .../org/apache/solr/util/SSLTestConfig.java | 95 +++++++++++++++--- .../resources/SSLTestConfig.testing.keystore | Bin 0 -> 2208 bytes 4 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 solr/test-framework/src/resources/SSLTestConfig.testing.keystore diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index f45794d856d..5f52cf89b29 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -203,6 +203,9 @@ Bug Fixes * SOLR-9093: Fix NullPointerException in TopGroupsShardResponseProcessor. (Christine Poerschke) +* SOLR-8970: Change SSLTestConfig to use a keystore file that is included as a resource in the + test-framework jar so users subclassing SolrTestCaseJ4 don't need to preserve magic paths (hossman) + Optimizations ---------------------- * SOLR-8722: Don't force a full ZkStateReader refresh on every Overseer operation. diff --git a/solr/test-framework/build.xml b/solr/test-framework/build.xml index 021076dba73..1e0d7b51ad7 100644 --- a/solr/test-framework/build.xml +++ b/solr/test-framework/build.xml @@ -38,9 +38,15 @@ + + + + + + diff --git a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java index 052589064fd..1f880a89709 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java +++ b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java @@ -27,6 +27,7 @@ import java.security.SecureRandomSpi; import java.security.UnrecoverableKeyException; import javax.net.ssl.SSLContext; +import java.net.MalformedURLException; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; @@ -48,24 +49,92 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.security.CertificateUtils; import org.eclipse.jetty.util.ssl.SslContextFactory; +/** + * An {@link SSLConfig} that supports reading key/trust store information directly from resource + * files provided with the Solr test-framework classes + */ public class SSLTestConfig extends SSLConfig { + + /** @deprecated No longer used except by {@link #setSSLSystemProperties} */ public static File TEST_KEYSTORE = ExternalPaths.SERVER_HOME == null ? null - : new File(ExternalPaths.SERVER_HOME, "../etc/test/solrtest.keystore"); + : new File(ExternalPaths.SERVER_HOME, "../etc/test/solrtest.keystore"); + /** @deprecated No longer used except by {@link #setSSLSystemProperties} */ private static String TEST_KEYSTORE_PATH = TEST_KEYSTORE != null - && TEST_KEYSTORE.exists() ? TEST_KEYSTORE.getAbsolutePath() : null; - private static String TEST_KEYSTORE_PASSWORD = "secret"; + && TEST_KEYSTORE.exists() ? TEST_KEYSTORE.getAbsolutePath() : null; + + private static final String TEST_KEYSTORE_RESOURCE = "SSLTestConfig.testing.keystore"; + private static final String TEST_KEYSTORE_PASSWORD = "secret"; + + private final Resource keyStore; + private final Resource trustStore; + /** Creates an SSLTestConfig that does not use SSL or client authentication */ public SSLTestConfig() { this(false, false); } - + + /** + * Create an SSLTestConfig based on a few caller specified options. As needed, + * keystore/truststore information will be pulled from a hardocded resource file provided + * by the solr test-framework. + * + * @param useSSL - wether SSL should be required. + * @param clientAuth - whether client authentication should be required. + */ public SSLTestConfig(boolean useSSL, boolean clientAuth) { - this(useSSL, clientAuth, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD); + super(useSSL, clientAuth, null, TEST_KEYSTORE_PASSWORD, null, TEST_KEYSTORE_PASSWORD); + trustStore = keyStore = Resource.newClassPathResource(TEST_KEYSTORE_RESOURCE); + if (null == keyStore || ! keyStore.exists() ) { + throw new IllegalStateException("Unable to locate keystore resource file in classpath: " + + TEST_KEYSTORE_RESOURCE); + } } - + + /** + * Create an SSLTestConfig using explicit paths for files + * @deprecated - use {@link SSLConfig} directly + */ + @Deprecated public SSLTestConfig(boolean useSSL, boolean clientAuth, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) { super(useSSL, clientAuth, keyStore, keyStorePassword, trustStore, trustStorePassword); + this.keyStore = tryNewResource(keyStore, "KeyStore"); + this.trustStore = tryNewResource(trustStore, "TrustStore"); + } + + /** + * Helper utility for building resources from arbitrary user input paths/urls + * if input is null, returns null; otherwise attempts to build Resource and verifies that Resource exists. + */ + private static final Resource tryNewResource(String userInput, String type) { + if (null == userInput) { + return null; + } + Resource result; + try { + result = Resource.newResource(userInput); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Can't build " + type + " Resource: " + e.getMessage(), e); + } + if (! result.exists()) { + throw new IllegalArgumentException(type + " Resource does not exist " + result.getName()); + } + return result; + } + + /** NOTE: This method is meaningless unless you explicitly provide paths when constructing this instance + * @see #SSLTestConfig(boolean,boolean,String,String,String,String) + */ + @Override + public String getKeyStore() { + return super.getKeyStore(); + } + /** NOTE: This method is meaningless unless you explicitly provide paths when constructing this instance + * @see #SSLTestConfig(boolean,boolean,String,String,String,String) + */ + @Override + public String getTrustStore() { + return super.getTrustStore(); } /** @@ -103,10 +172,10 @@ public class SSLTestConfig extends SSLConfig { // NOTE: KeyStore & TrustStore are swapped because they are from configured from server perspective... // we are a client - our keystore contains the keys the server trusts, and vice versa - builder.loadTrustMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), new TrustSelfSignedStrategy()).build(); + builder.loadTrustMaterial(buildKeyStore(keyStore, getKeyStorePassword()), new TrustSelfSignedStrategy()).build(); if (isClientAuthMode()) { - builder.loadKeyMaterial(buildKeyStore(getTrustStore(), getTrustStorePassword()), getTrustStorePassword().toCharArray()); + builder.loadKeyMaterial(buildKeyStore(trustStore, getTrustStorePassword()), getTrustStorePassword().toCharArray()); } @@ -130,10 +199,10 @@ public class SSLTestConfig extends SSLConfig { SSLContextBuilder builder = SSLContexts.custom(); builder.setSecureRandom(NotSecurePsuedoRandom.INSTANCE); - builder.loadKeyMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), getKeyStorePassword().toCharArray()); + builder.loadKeyMaterial(buildKeyStore(keyStore, getKeyStorePassword()), getKeyStorePassword().toCharArray()); if (isClientAuthMode()) { - builder.loadTrustMaterial(buildKeyStore(getTrustStore(), getTrustStorePassword()), new TrustSelfSignedStrategy()).build(); + builder.loadTrustMaterial(buildKeyStore(trustStore, getTrustStorePassword()), new TrustSelfSignedStrategy()).build(); } @@ -164,11 +233,11 @@ public class SSLTestConfig extends SSLConfig { /** * Constructs a KeyStore using the specified filename and password */ - protected static KeyStore buildKeyStore(String keyStoreLocation, String password) { + protected static KeyStore buildKeyStore(Resource resource, String password) { try { - return CertificateUtils.getKeyStore(Resource.newResource(keyStoreLocation), "JKS", null, password); + return CertificateUtils.getKeyStore(resource, "JKS", null, password); } catch (Exception ex) { - throw new IllegalStateException("Unable to build KeyStore from file: " + keyStoreLocation, ex); + throw new IllegalStateException("Unable to build KeyStore from resource: " + resource.getName(), ex); } } diff --git a/solr/test-framework/src/resources/SSLTestConfig.testing.keystore b/solr/test-framework/src/resources/SSLTestConfig.testing.keystore new file mode 100644 index 0000000000000000000000000000000000000000..bcc6ec05d7155d511ae40a0d2b019018caabd4bf GIT binary patch literal 2208 zcmcJP`8(8$7sux_GsZf@7}>@)Op+L%$#zqgY?C!zO+pB9ZJ8Oy79m8IT)P&bp}1p9 zmUv{D6q0x-8Ea#!tfjcNp|9?9f4Kj^_lNfn=Y4)S&v~Eoy!Kc2S0E4w^gzHr;tQq( z1kt^M=?BKC;e!242!sa!j8GAn-W&La=EX*;85a&hC#ZHa;6+ zwHBFu3TC*YMM8z2H)d%}UF60_sZT(TN>pFe_1F9F#OI~O*8Z-p))_GN+$J>R)uty7 z<4Ixsp`dxT8}6OD?CEZ)*80nK2I(j_)78+5X!8?k^@Lm=v5dW2%OPFd=ts|n_OtUJ-L z+(|V^_lZ3;*%&K$#COwwNNA&I4%0_6 zc13Pv%fr_tyTlcL%ObIXX4&{zYViVE4UwVoQ?YVHO?C-c>!Mls=$eZBXA5J}#aoHu z=Y4a7N|wJ)#3o%TikMddOVr9gvPL+IIkPiTbVjay{Em%WAfswClv|ln5E{S5j?A9R zQZic7(vJ@&`H9I!#+y-W8IEPdzFyECntJYCbv#2k8-1gK zs%CL;7a{V*fOUd1li_;&Li^|k?w1>3L(bpw1T<}W9k{-Yrsr-5wteM%OYp5|GCmCF zt(T(w8J_C?$DnOAJlyv&W!rTO9k%yjRL9yaiF{b<) zpFP+j#Uwee;I!t^q|Ccmq?uo{-47}vyUSvnd0VQUonNnwDY+i+)D9ak*U_vQH;cN- zb?qbpJLvKp;Izf1tf529t*OAbT3AVjyQU*-ti0P{QK1v%5P3q12l0&ikazk2k``Mv&cPA|-B@ z-!{r@j`_0IFY-S5xAPxNz_aeH4y=aQ?0N+}KlJM2bAu3;qyA?7DS=P1H#erDXqOOr zVp`+m0q*9<>G_!Cmn@ZKgQWLNN!-@U@Z??W0);}MhVAk-H{_omv&At;{$b;-20Mw* z&sT&`2dx=0Q1;ndfrrIIKeoo>NzCAgS2lfY7e(VS-TI2QwC`6U4TrLXEVDCPGO&#m zmf0_5o@YKwOaB=)o42l{OjT3$@NX*=9XO^Y{jsemYb$qrmk=c^!p0tJJ9;kk^mfhg z7?jzjAMK*h&F$}}M!j@B`B$>^S{a`)N*|{FNht!2r&aFl_PpvDUE)l`_3c{r%sYEQ z;U@Nw@8^})Csk-6?>CJYi9%g5D~^xvpbMi^Qc;wDq|&J@!TZtsoI22>;B`t0Ak<&N zUijKw6fDnNk(k_J>E7=SSr(#pT%LF(*gyr*4upXy2U*d|!thL%Nw7S5CG;l>=3Bed z78wB+Do^>YelVL6ToL`QW_sjI?Z-90xGyujZxqvBsbYr<4U5BZCmU!lOLIGL720=? zXy~0ReEd{nVh}lZYTaETO(E*B)2M67_h3=80`9$4rU;8~*@IX&A0avot9uJgBEGxd zQ8P2H<(6e(!Ma)br^!T$M7fNPP-V3UAGp8X%H~#69~=%)%DKx&KY-x@9(gVjw80Y) z2h9^|MW6)zK}3VXzj}fgKKldqox~+9Lh7^|a)7;+ZCzZ;wV0Z1I-)qy+PvfHi;j8w6HF5~rXIeM zVj-t>w%zSV=jvnS=3cKd&m?3*5!Oi7#O7-3&ZO}DFfuj)Zh^duUJzpQD5s$r|gOSib%KW$#egd15l zYvW{&)hWOjyhbafhcuSxn1?}UXv#g|1#j+C=7)QAPCa?D>{&X&6k8gQ&kgm%fC9=% z{SV{a>JS4#cfQ3Msi+TgU)N%5<@7?%Rj$2ok@R`XAr#)plrT`J$Y1J8=m|z