From 41b4386656596c8a568de877ad89190f335f21cf Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Mon, 13 Nov 2023 13:15:01 -0800 Subject: [PATCH 01/15] Add server side reload and bats test --- solr/bin/solr | 2 +- solr/bin/solr.cmd | 2 +- solr/packaging/test/test_ssl.bats | 59 ++++++++++++++++++++ solr/server/etc/jetty-ssl-context-reload.xml | 12 ++++ solr/server/modules/ssl-reload.mod | 12 ++++ 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 solr/server/etc/jetty-ssl-context-reload.xml create mode 100644 solr/server/modules/ssl-reload.mod diff --git a/solr/bin/solr b/solr/bin/solr index 8837f858d29..34fb21d954a 100755 --- a/solr/bin/solr +++ b/solr/bin/solr @@ -207,7 +207,7 @@ if [ -z "${SOLR_SSL_ENABLED:-}" ]; then fi fi if [ "$SOLR_SSL_ENABLED" == "true" ]; then - SOLR_JETTY_CONFIG+=("--module=https" "--lib=$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*") + SOLR_JETTY_CONFIG+=("--module=https,ssl-reload" "--lib=$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*") SOLR_URL_SCHEME=https if [ -n "$SOLR_SSL_KEY_STORE" ]; then SOLR_SSL_OPTS+=" -Dsolr.jetty.keystore=$SOLR_SSL_KEY_STORE" diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd index 3a19be2e366..c4ba16d1bfe 100755 --- a/solr/bin/solr.cmd +++ b/solr/bin/solr.cmd @@ -85,7 +85,7 @@ IF NOT DEFINED SOLR_SSL_ENABLED ( ) IF "%SOLR_SSL_ENABLED%"=="true" ( - set "SOLR_JETTY_CONFIG=--module=https --lib="%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*"" + set "SOLR_JETTY_CONFIG=--module=https,ssl-reload --lib="%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*"" set SOLR_URL_SCHEME=https IF DEFINED SOLR_SSL_KEY_STORE ( set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Dsolr.jetty.keystore=%SOLR_SSL_KEY_STORE%" diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 0af57e34420..5d98c8bde12 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -494,3 +494,62 @@ teardown() { run solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" assert_output --regexp '(unable to find valid certification path to requested target|Server refused connection)' } + +@test "test keystore reload" { + # Create a keystore + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + mkdir -p "$ssl_dir" + ( + cd "$ssl_dir" + rm -f cert1.keystore.p12 cert1.pem cert2.keystore.p12 cert2.pem + # cert and keystore 1 + keytool -genkeypair -alias cert1 -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore cert1.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + openssl pkcs12 -in cert1.keystore.p12 -out cert1.pem -passin pass:secret -passout pass:secret + + # cert and keystore 2 + keytool -genkeypair -alias cert2 -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore cert2.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + openssl pkcs12 -in cert2.keystore.p12 -out cert2.pem -passin pass:secret -passout pass:secret + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE=$ssl_dir/cert1.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/cert1.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_NEED_CLIENT_AUTH=true + export SOLR_SSL_WANT_CLIENT_AUTH=false + export SOLR_HOST=localhost + + solr start -c -a "-Dsolr.jetty.sslContext.reload.scanInterval=1" + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + + echo "Now run with cert 2" + + export SOLR_SSL_KEY_STORE=$ssl_dir/cert2.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/cert2.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + + run ! solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + + ( + cd "$ssl_dir" + # Replace server keystore with client's + cp cert2.keystore.p12 cert1.keystore.p12 + ) + # Give some time for the server reload + sleep 5 + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + +} \ No newline at end of file diff --git a/solr/server/etc/jetty-ssl-context-reload.xml b/solr/server/etc/jetty-ssl-context-reload.xml new file mode 100644 index 00000000000..a65b856882c --- /dev/null +++ b/solr/server/etc/jetty-ssl-context-reload.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/solr/server/modules/ssl-reload.mod b/solr/server/modules/ssl-reload.mod new file mode 100644 index 00000000000..340eae041e3 --- /dev/null +++ b/solr/server/modules/ssl-reload.mod @@ -0,0 +1,12 @@ +[description] +Enables the KeyStore to be reloaded when the KeyStore file changes. + +[tags] +connector +ssl + +[depend] +ssl + +[xml] +etc/jetty-ssl-context-reload.xml \ No newline at end of file From 50c1e00270e0397eace662a658dbc0fb3f420936 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Mon, 13 Nov 2023 15:29:08 -0800 Subject: [PATCH 02/15] Fix security manager with keystore reload --- solr/bin/solr | 13 ++++++++++++- solr/bin/solr.in.sh | 1 + solr/packaging/test/test_ssl.bats | 6 ++---- solr/server/etc/jetty-ssl-context-reload.xml | 3 ++- solr/server/etc/security.policy | 3 +++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/solr/bin/solr b/solr/bin/solr index 34fb21d954a..e979060beba 100755 --- a/solr/bin/solr +++ b/solr/bin/solr @@ -207,10 +207,17 @@ if [ -z "${SOLR_SSL_ENABLED:-}" ]; then fi fi if [ "$SOLR_SSL_ENABLED" == "true" ]; then - SOLR_JETTY_CONFIG+=("--module=https,ssl-reload" "--lib=$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*") + SOLR_JETTY_CONFIG+=("--module=https" "--lib=$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*") + if [ "${SOLR_SSL_RELOAD_ENABLED:-true}" == "true" ]; then + SOLR_JETTY_CONFIG+=("--module=ssl-reload") + fi SOLR_URL_SCHEME=https if [ -n "$SOLR_SSL_KEY_STORE" ]; then SOLR_SSL_OPTS+=" -Dsolr.jetty.keystore=$SOLR_SSL_KEY_STORE" + if [ "${SOLR_SSL_RELOAD_ENABLED:-true}" == "true" ] && [ "${SOLR_SECURITY_MANAGER_ENABLED:-true}" == "true" ]; then + # In this case we need to allow reads from the parent directory of the keystore + SOLR_SSL_OPTS+=" -Dsolr.jetty.keystoreParentPath=$SOLR_SSL_KEY_STORE/.." + fi fi if [ -n "$SOLR_SSL_KEY_STORE_PASSWORD" ]; then export SOLR_SSL_KEY_STORE_PASSWORD=$SOLR_SSL_KEY_STORE_PASSWORD @@ -249,6 +256,10 @@ if [ "$SOLR_SSL_ENABLED" == "true" ]; then if [ -n "$SOLR_SSL_CLIENT_KEY_STORE_TYPE" ]; then SOLR_SSL_OPTS+=" -Djavax.net.ssl.keyStoreType=$SOLR_SSL_CLIENT_KEY_STORE_TYPE" fi + if [ "${SOLR_SSL_RELOAD_ENABLED:-true}" == "true" ] && [ "${SOLR_SECURITY_MANAGER_ENABLED:-true}" == "true" ]; then + # In this case we need to allow reads from the parent directory of the keystore + SOLR_SSL_OPTS+=" -Djavax.net.ssl.keyStoreParentPath=$SOLR_SSL_CLIENT_KEY_STORE/.." + fi else if [ -n "$SOLR_SSL_KEY_STORE" ]; then SOLR_SSL_OPTS+=" -Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE" diff --git a/solr/bin/solr.in.sh b/solr/bin/solr.in.sh index f6da91c2f3b..31cc131dfd3 100644 --- a/solr/bin/solr.in.sh +++ b/solr/bin/solr.in.sh @@ -179,6 +179,7 @@ # Override Key/Trust Store types if necessary #SOLR_SSL_KEY_STORE_TYPE=PKCS12 #SOLR_SSL_TRUST_STORE_TYPE=PKCS12 +#SOLR_SSL_RELOAD_ENABLED=true # Uncomment if you want to override previously defined SSL values for HTTP client # otherwise keep them commented and the above values will automatically be set for HTTP clients diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 5d98c8bde12..6808451fd68 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -209,13 +209,13 @@ teardown() { run solr start -c + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + export SOLR_SSL_KEY_STORE= export SOLR_SSL_KEY_STORE_PASSWORD= export SOLR_SSL_TRUST_STORE= export SOLR_SSL_TRUST_STORE_PASSWORD= - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - run solr create -c test -s 2 assert_output --partial "Created collection 'test'" @@ -532,8 +532,6 @@ teardown() { run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - echo "Now run with cert 2" - export SOLR_SSL_KEY_STORE=$ssl_dir/cert2.keystore.p12 export SOLR_SSL_KEY_STORE_PASSWORD=secret export SOLR_SSL_TRUST_STORE=$ssl_dir/cert2.keystore.p12 diff --git a/solr/server/etc/jetty-ssl-context-reload.xml b/solr/server/etc/jetty-ssl-context-reload.xml index a65b856882c..2bdc4153346 100644 --- a/solr/server/etc/jetty-ssl-context-reload.xml +++ b/solr/server/etc/jetty-ssl-context-reload.xml @@ -1,4 +1,5 @@ - + + diff --git a/solr/server/etc/security.policy b/solr/server/etc/security.policy index aec2e2ddcfe..1dd9db3aef8 100644 --- a/solr/server/etc/security.policy +++ b/solr/server/etc/security.policy @@ -193,6 +193,9 @@ grant { permission java.io.FilePermission "${javax.net.ssl.trustStore}", "read,readlink"; + permission java.io.FilePermission "${solr.jetty.keystoreParentPath}", "read,readlink"; + permission java.io.FilePermission "${javax.net.ssl.keyStoreParentPath}", "read,readlink"; + permission java.io.FilePermission "${solr.install.dir}", "read,write,delete,readlink"; permission java.io.FilePermission "${solr.install.dir}${/}-", "read,write,delete,readlink"; permission java.io.FilePermission "${solr.install.symDir}", "read,write,delete,readlink"; From 3163b8c3e9bd8c1140f93f8b63e35fd32123499d Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Tue, 14 Nov 2023 16:47:22 -0800 Subject: [PATCH 03/15] WIP: Add client keystore reload --- .../component/HttpShardHandlerFactory.java | 11 + solr/packaging/test/test_ssl.bats | 506 ++---------------- .../solr/client/solrj/io/SolrClientCache.java | 4 + .../client/solrj/impl/Http2SolrClient.java | 46 ++ 4 files changed, 94 insertions(+), 473 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java index 5b227359701..8f59c2ea2c2 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java +++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java @@ -126,6 +126,9 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory // Configure if the threadpool favours fairness over throughput static final String INIT_FAIRNESS_POLICY = "fairnessPolicy"; + // Determines how often to check for key store changes in the client + static final String KEY_STORE_RELOAD_PERIOD_SECS = "keyStoreReloadPeriodSecs"; + /** Get {@link ShardHandler} that uses the default http client. */ @Override public ShardHandler getShardHandler() { @@ -279,12 +282,20 @@ public void init(PluginInfo info) { int soTimeout = getParameter(args, HttpClientUtil.PROP_SO_TIMEOUT, HttpClientUtil.DEFAULT_SO_TIMEOUT, sb); + int keystoreReloadIntervalSecs = + getParameter( + args, + KEY_STORE_RELOAD_PERIOD_SECS, + Integer.getInteger("solr.jetty.sslContext.reload.scanInterval", 30), + sb); + this.defaultClient = new Http2SolrClient.Builder() .withConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS) .withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS) .withExecutor(commExecutor) .withMaxConnectionsPerHost(maxConnectionsPerHost) + .withKeyStoreReloadInterval(keystoreReloadIntervalSecs, TimeUnit.SECONDS) .build(); this.defaultClient.addListenerFactory(this.httpListenerFactory); this.loadbalancer = new LBHttp2SolrClient.Builder(defaultClient).build(); diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 6808451fd68..452ae058e5a 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -29,472 +29,6 @@ teardown() { solr stop -all >/dev/null 2>&1 } -@test "start solr with ssl" { - # Create a keystore - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - mkdir -p "$ssl_dir" - ( - cd "$ssl_dir" - rm -f solr-ssl.keystore.p12 solr-ssl.pem - keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" - openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret - ) - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_TRUST_STORE_PASSWORD=secret - export SOLR_SSL_NEED_CLIENT_AUTH=false - export SOLR_SSL_WANT_CLIENT_AUTH=false - export SOLR_HOST=localhost - - solr start -c - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - - run solr create -c test -s 2 - assert_output --partial "Created collection 'test'" - - run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - run curl --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" -} - -@test "use different hostname when not checking peer-name" { - # Create a keystore - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - mkdir -p "$ssl_dir" - ( - cd "$ssl_dir" - rm -f solr-ssl.keystore.p12 solr-ssl.pem - # Using a CN that is not localhost, as we will not be checking peer-name - keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext "SAN=DNS:test.solr.apache.org,IP:127.0.0.1" -dname "CN=test.solr.apache.org, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" - openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret - ) - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_TRUST_STORE_PASSWORD=secret - export SOLR_SSL_NEED_CLIENT_AUTH=false - export SOLR_SSL_WANT_CLIENT_AUTH=false - export SOLR_SSL_CHECK_PEER_NAME=false - export SOLR_HOST=127.0.0.1 - - solr start -c - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - - run solr create -c test -s 2 - assert_output --partial "Created collection 'test'" - - run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - # Just test that curl can connect via insecure or via a custom host header - run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial 'no alternative certificate subject name matches target host name' - - # Just test that curl can connect via insecure or via a custom host header - run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" -k "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" --resolve "test.solr.apache.org:${SOLR_PORT}:127.0.0.1" "https://test.solr.apache.org:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - # This is a client setting, so we don't need to restart Solr to make sure that it fails - export SOLR_SSL_CHECK_PEER_NAME=true - - # This should fail the peername check - run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --regexp '(No subject alternative DNS name matching localhost found|Server refused connection)' - - # Restart the server enabling the SNI hostcheck - export SOLR_SSL_CHECK_PEER_NAME=false - export SOLR_OPTS="${SOLR_OPTS} -Dsolr.jetty.ssl.sniHostCheck=true" - solr restart -c - # This should fail the SNI Hostname check - run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" - assert_output --partial 'Invalid SNI' - - # Using the right hostname should not fail the SNI Hostname check - run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" --resolve "test.solr.apache.org:${SOLR_PORT}:127.0.0.1" "https://test.solr.apache.org:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" - assert_output --partial '"urlScheme":"https"' -} - -@test "start solr with ssl and auth" { - # Create a keystore - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - mkdir -p "$ssl_dir" - ( - cd "$ssl_dir" - rm -f solr-ssl.keystore.p12 solr-ssl.pem - keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" - openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret - ) - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_TRUST_STORE_PASSWORD=secret - export SOLR_SSL_NEED_CLIENT_AUTH=false - export SOLR_SSL_WANT_CLIENT_AUTH=false - export SOLR_SSL_CHECK_PEER_NAME=true - export SOLR_HOST=localhost - - solr start -c - solr auth enable -type basicAuth -credentials name:password - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - - run curl -u name:password --basic --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CREATE&collection.configName=_default&name=test&numShards=2&replicationFactor=1&router.name=compositeId&wt=json" - assert_output --partial '"status":0' - - run curl -u name:password --basic --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial '"numFound":0' - - # When the Jenkins box "curl" supports --fail-with-body, add "--fail-with-body" and change "run" to "run !", to expect a failure - run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" - assert_output --partial 'Error 401 Authentication' -} - -@test "start solr with client truststore and security manager" { - # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR - test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" - mkdir -p "${test_tmp_dir}" - test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" - - export SOLR_SECURITY_MANAGER_ENABLED=true - export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" - export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" - - # Create a keystore - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - export client_ssl_dir="${ssl_dir}-client" - mkdir -p "$ssl_dir" - ( - cd "$ssl_dir" - rm -f solr-ssl.keystore.p12 - keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" - ) - mkdir -p "$client_ssl_dir" - ( - cd "$client_ssl_dir" - rm -f * - keytool -export -alias solr-ssl -file solr-ssl.crt -keystore "$ssl_dir/solr-ssl.keystore.p12" -keypass secret -storepass secret - keytool -import -v -trustcacerts -alias solr-ssl -file solr-ssl.crt -storetype PKCS12 -keystore solr-ssl.truststore.p12 -keypass secret -storepass secret -noprompt - ) - cp -R "$ssl_dir" "$client_ssl_dir" - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 - export SOLR_SSL_TRUST_STORE_PASSWORD=secret - export SOLR_SSL_CLIENT_TRUST_STORE=$client_ssl_dir/solr-ssl.truststore.p12 - export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=secret - export SOLR_SSL_NEED_CLIENT_AUTH=false - export SOLR_SSL_WANT_CLIENT_AUTH=true - export SOLR_SSL_CHECK_PEER_NAME=true - export SOLR_HOST=localhost - export SOLR_SECURITY_MANAGER_ENABLED=true - - run solr start -c - - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - - export SOLR_SSL_KEY_STORE= - export SOLR_SSL_KEY_STORE_PASSWORD= - export SOLR_SSL_TRUST_STORE= - export SOLR_SSL_TRUST_STORE_PASSWORD= - - run solr create -c test -s 2 - assert_output --partial "Created collection 'test'" - - run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" - assert_output --partial '"urlScheme":"https"' -} - -@test "start solr with mTLS needed" { - # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR - test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" - mkdir -p "${test_tmp_dir}" - test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" - - export SOLR_SECURITY_MANAGER_ENABLED=true - export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" - export SOLR_LOG_LEVEL="DEBUG" - export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" # To debug further use: -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake - - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - export server_ssl_dir="${ssl_dir}/server" - export client_ssl_dir="${ssl_dir}/client" - - # Create a root & intermediary CA - echo "${ssl_dir}" - mkdir -p "${ssl_dir}" - ( - cd "$ssl_dir" - rm -f root.p12 root.pem ca.p12 ca.pem - - keytool -genkeypair -keystore root.p12 -storetype PKCS12 -keypass secret -storepass secret -alias root -ext bc:c -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - keytool -genkeypair -keystore ca.p12 -storetype PKCS12 -keypass secret -storepass secret -alias ca -ext bc:c -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - keytool -keystore root.p12 -storetype PKCS12 -storepass secret -alias root -exportcert -rfc > root.pem - - keytool -storepass secret -storetype PKCS12 -keystore ca.p12 -certreq -alias ca | \ - keytool -storepass secret -keystore root.p12 -storetype PKCS12 \ - -gencert -alias root -ext BC=0 -rfc > ca.pem - keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias root -file root.pem -noprompt - keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias ca -file ca.pem - ) - # Create a server keystore & truststore - mkdir -p "$server_ssl_dir" - ( - cd "$server_ssl_dir" - rm -f solr-server.keystore.p12 server.pem solr-server.truststore.p12 - - # Create a keystore and certificate - keytool -genkeypair -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -alias server -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - # Trust the keystore cert with the CA - keytool -storepass server-key -keystore solr-server.keystore.p12 -storetype PKCS12 -certreq -alias server | \ - keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ - -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=serverAuth -rfc > server.pem - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias server -file server.pem - - # Create a truststore with just the Root CA - keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - ) - # Create a client keystore & truststore - # The client cert will have a bogus DNS name and IP address, as we want the clientHostnameVerification to fail - mkdir -p "$client_ssl_dir" - ( - cd "$client_ssl_dir" - rm -f solr-client.keystore.p12 client.pem solr-client.truststore.p12 - - # Create a keystore and certificate - keytool -genkeypair -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -alias client -ext SAN=DNS:test.solr.apache.org,IP:127.0.0.2 -dname "CN=test.solr.apache.org, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - # Trust the keystore cert with the CA - keytool -storepass client-key -keystore solr-client.keystore.p12 -storetype PKCS12 -certreq -alias client | \ - keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ - -ext "SAN=DNS:test.solr.apache.org,IP:127.0.0.2" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=clientAuth -rfc > client.pem - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias client -file client.pem - - # Create a truststore with just the Root CA - keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - ) - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE="$server_ssl_dir/solr-server.keystore.p12" - export SOLR_SSL_KEY_STORE_PASSWORD=server-key - export SOLR_SSL_KEY_STORE_TYPE=PKCS12 - export SOLR_SSL_TRUST_STORE="$server_ssl_dir/solr-server.truststore.p12" - export SOLR_SSL_TRUST_STORE_PASSWORD=server-trust - export SOLR_SSL_TRUST_STORE_TYPE=PKCS12 - export SOLR_SSL_CLIENT_KEY_STORE="$client_ssl_dir/solr-client.keystore.p12" - export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD=client-key - export SOLR_SSL_CLIENT_KEY_STORE_TYPE=PKCS12 - export SOLR_SSL_CLIENT_TRUST_STORE="$client_ssl_dir/solr-client.truststore.p12" - export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=client-trust - export SOLR_SSL_CLIENT_TRUST_STORE_TYPE=PKCS12 - export SOLR_SSL_NEED_CLIENT_AUTH=true - export SOLR_SSL_WANT_CLIENT_AUTH=false - export SOLR_SSL_CHECK_PEER_NAME=true - # Cannot set this to true, because the client certificate does not have the right hostname ("localhost") or IP - export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=false - export SOLR_HOST=localhost - - solr start -c - solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} - - # Test Client connections, which do not need the server keystore/truststore - ( - export SOLR_SSL_KEY_STORE= - export SOLR_SSL_KEY_STORE_PASSWORD= - export SOLR_SSL_TRUST_STORE= - export SOLR_SSL_TRUST_STORE_PASSWORD= - - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 - - run solr create -c test -s 2 - assert_output --partial "Created collection 'test'" - - run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" - assert_output --partial '"urlScheme":"https"' - - run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --partial '"numFound":0' - - ( - # Try connecting with just the client truststore, which should fail because mTLS requires a keystore and mTLS is needed - export SOLR_SSL_CLIENT_KEY_STORE= - export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD= - - run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --regexp '(bad_certificate|Server refused connection)' - ) - ) - - # Turn on client hostname verification, and start a new Solr node since the property is a server setting. - # Test that it fails because the client cert does not use "localhost" - export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=true - solr start -c -z localhost:${ZK_PORT} -p ${SOLR3_PORT} - - # We can't check if the server has come up, because we can't connect to it, so just wait - sleep 5 - - run ! solr api -verbose -get "https://localhost:${SOLR3_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --regexp '(certificate_unknown|java.nio.channels.ClosedChannelException|Server refused connection)' -} - -@test "start solr with mTLS wanted" { - # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR - test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" - mkdir -p "${test_tmp_dir}" - test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" - - export SOLR_SECURITY_MANAGER_ENABLED=true - export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" - export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" # To debug further use: -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake - - export ssl_dir="${BATS_TEST_TMPDIR}/ssl" - export server_ssl_dir="${ssl_dir}/server" - export client_ssl_dir="${ssl_dir}/client" - - # Create a root & intermediary CA - echo "${ssl_dir}" - mkdir -p "${ssl_dir}" - ( - cd "$ssl_dir" - rm -f root.p12 root.pem ca.p12 ca.pem - - keytool -genkeypair -keystore root.p12 -storetype PKCS12 -keypass secret -storepass secret -alias root -ext bc:c -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - keytool -genkeypair -keystore ca.p12 -storetype PKCS12 -keypass secret -storepass secret -alias ca -ext bc:c -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - keytool -keystore root.p12 -storetype PKCS12 -storepass secret -alias root -exportcert -rfc > root.pem - - keytool -storepass secret -storetype PKCS12 -keystore ca.p12 -certreq -alias ca | \ - keytool -storepass secret -keystore root.p12 -storetype PKCS12 \ - -gencert -alias root -ext BC=0 -rfc > ca.pem - keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias root -file root.pem -noprompt - keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias ca -file ca.pem - ) - # Create a server keystore & truststore - mkdir -p "$server_ssl_dir" - ( - cd "$server_ssl_dir" - rm -f solr-server.keystore.p12 server.pem solr-server.truststore.p12 - - # Create a keystore and certificate - keytool -genkeypair -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -alias server -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - # Trust the keystore cert with the CA - keytool -storepass server-key -keystore solr-server.keystore.p12 -storetype PKCS12 -certreq -alias server | \ - keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ - -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=serverAuth -rfc > server.pem - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias server -file server.pem - - # Create a truststore with just the Root CA - keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - ) - # Create a client keystore & truststore - mkdir -p "$client_ssl_dir" - ( - cd "$client_ssl_dir" - rm -f solr-client.keystore.p12 client.pem solr-client.truststore.p12 - - # Create a keystore and certificate - keytool -genkeypair -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -alias client -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa - - # Trust the keystore cert with the CA - keytool -storepass client-key -keystore solr-client.keystore.p12 -storetype PKCS12 -certreq -alias client | \ - keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ - -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=clientAuth -rfc > client.pem - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias client -file client.pem - - # Create a truststore with just the Root CA - keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt - keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt - ) - - # Set ENV_VARs so that Solr uses this keystore - export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE="$server_ssl_dir/solr-server.keystore.p12" - export SOLR_SSL_KEY_STORE_PASSWORD=server-key - export SOLR_SSL_KEY_STORE_TYPE=PKCS12 - export SOLR_SSL_TRUST_STORE="$server_ssl_dir/solr-server.truststore.p12" - export SOLR_SSL_TRUST_STORE_PASSWORD=server-trust - export SOLR_SSL_TRUST_STORE_TYPE=PKCS12 - export SOLR_SSL_CLIENT_KEY_STORE="$client_ssl_dir/solr-client.keystore.p12" - export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD=client-key - export SOLR_SSL_CLIENT_KEY_STORE_TYPE=PKCS12 - export SOLR_SSL_CLIENT_TRUST_STORE="$client_ssl_dir/solr-client.truststore.p12" - export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=client-trust - export SOLR_SSL_CLIENT_TRUST_STORE_TYPE=PKCS12 - export SOLR_SSL_NEED_CLIENT_AUTH=false - export SOLR_SSL_WANT_CLIENT_AUTH=true - export SOLR_SSL_CHECK_PEER_NAME=true - export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=true - export SOLR_HOST=localhost - - solr start -c - solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} - - export SOLR_SSL_KEY_STORE= - export SOLR_SSL_KEY_STORE_PASSWORD= - export SOLR_SSL_TRUST_STORE= - export SOLR_SSL_TRUST_STORE_PASSWORD= - - solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 - solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 - - run solr create -c test -s 2 - assert_output --partial "Created collection 'test'" - - run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" - assert_output --partial '"urlScheme":"https"' - - run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --partial '"numFound":0' - - export SOLR_SSL_CLIENT_KEY_STORE= - export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD= - - # mTLS requires a keyStore, so just using the truststore would fail if mTLS was "NEED"ed, however it is only "WANT"ed, so its ok - run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --partial '"numFound":0' - - export SOLR_SSL_CLIENT_TRUST_STORE= - export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD= - - # TLS cannot work if a truststore and keystore are not provided (either Server or Client) - run solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" - assert_output --regexp '(unable to find valid certification path to requested target|Server refused connection)' -} - @test "test keystore reload" { # Create a keystore export ssl_dir="${BATS_TEST_TMPDIR}/ssl" @@ -509,28 +43,40 @@ teardown() { # cert and keystore 2 keytool -genkeypair -alias cert2 -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore cert2.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" openssl pkcs12 -in cert2.keystore.p12 -out cert2.pem -passin pass:secret -passout pass:secret + + cp cert1.keystore.p12 server1.keystore.p12 + cp cert1.keystore.p12 server2.keystore.p12 ) # Set ENV_VARs so that Solr uses this keystore export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/cert1.keystore.p12 + export SOLR_SSL_KEY_STORE=$ssl_dir/server1.keystore.p12 export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/cert1.keystore.p12 + export SOLR_SSL_TRUST_STORE=$ssl_dir/server1.keystore.p12 export SOLR_SSL_TRUST_STORE_PASSWORD=secret export SOLR_SSL_NEED_CLIENT_AUTH=true export SOLR_SSL_WANT_CLIENT_AUTH=false export SOLR_HOST=localhost - solr start -c -a "-Dsolr.jetty.sslContext.reload.scanInterval=1" + solr start -c -a "-Dsolr.jetty.sslContext.reload.scanInterval=1 -DsocketTimeout=5000" solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + export SOLR_SSL_KEY_STORE=$ssl_dir/server2.keystore.p12 + export SOLR_SSL_TRUST_STORE=$ssl_dir/server2.keystore.p12 + solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} -a "-Dsolr.jetty.sslContext.reload.scanInterval=1 -DsocketTimeout=5000" + solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 + run solr create -c test -s 2 assert_output --partial "Created collection 'test'" run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" assert_output --partial '"numFound":0' + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + run ! curl "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*" export SOLR_SSL_KEY_STORE=$ssl_dir/cert2.keystore.p12 export SOLR_SSL_KEY_STORE_PASSWORD=secret @@ -541,13 +87,27 @@ teardown() { ( cd "$ssl_dir" - # Replace server keystore with client's - cp cert2.keystore.p12 cert1.keystore.p12 + # Replace server1 keystore with client's + cp cert2.keystore.p12 server1.keystore.p12 ) # Give some time for the server reload - sleep 5 + sleep 6 - run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + # run ! solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=query2" + run ! solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query2" + + ( + cd "$ssl_dir" + # Replace server2 keystore with client's + cp cert2.keystore.p12 server2.keystore.p12 + ) + # Give some time for the server reload + sleep 6 + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=query3" + assert_output --partial '"numFound":0' + + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query3" assert_output --partial '"numFound":0' } \ No newline at end of file diff --git a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java index e56d1a55c13..f3da49bb0c1 100644 --- a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java +++ b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java @@ -53,6 +53,9 @@ public class SolrClientCache implements Closeable { private static final int minSocketTimeout = Math.max(Integer.getInteger(HttpClientUtil.PROP_SO_TIMEOUT, MIN_TIMEOUT), MIN_TIMEOUT); + private static final int keyStoreReloadInterval = + Integer.getInteger("solr.jetty.sslContext.reload.scanInterval", 30); + private final Map solrClients = new HashMap<>(); private final HttpClient apacheHttpClient; private final Http2SolrClient http2SolrClient; @@ -159,6 +162,7 @@ private static Http2SolrClient.Builder newHttp2SolrClientBuilder( connTimeout = Math.max(idleTimeout, builder.getConnectionTimeout()); } builder.withConnectionTimeout(connTimeout, TimeUnit.MILLISECONDS); + builder.withKeyStoreReloadInterval(keyStoreReloadInterval, TimeUnit.SECONDS); return builder; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index 0f060000d30..d7eeafa71c0 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -105,6 +105,7 @@ import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.HttpCookieStore; +import org.eclipse.jetty.util.ssl.KeyStoreScanner; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -157,6 +158,8 @@ public class Http2SolrClient extends SolrClient { final String basicAuthAuthorizationStr; private AuthenticationStoreHolder authenticationStore; + private KeyStoreScanner scanner; + protected Http2SolrClient(String serverBaseUrl, Builder builder) { if (serverBaseUrl != null) { if (!serverBaseUrl.equals("/") && serverBaseUrl.endsWith("/")) { @@ -237,6 +240,17 @@ private HttpClient createHttpClient(Builder builder) { sslContextFactory = builder.sslConfig.createClientContextFactory(); } + if (sslContextFactory != null + && sslContextFactory.getKeyStoreResource() != null + && builder.keyStoreReloadInterval > 0) { + scanner = new KeyStoreScanner(sslContextFactory); + try { + scanner.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + ClientConnector clientConnector = new ClientConnector(); clientConnector.setReuseAddress(true); clientConnector.setSslContextFactory(sslContextFactory); @@ -334,6 +348,15 @@ public void close() { } } + if (scanner != null) { + try { + scanner.stop(); + } catch (Exception e) { + // TODO + throw new RuntimeException(e); + } + } + assert ObjectReleaseTracker.release(this); } @@ -1042,6 +1065,7 @@ public static class Builder { private int proxyPort; private boolean proxyIsSocks4; private boolean proxyIsSecure; + private long keyStoreReloadInterval = 30; public Builder() {} @@ -1057,6 +1081,11 @@ public Http2SolrClient build() { connectionTimeoutMillis = (long) HttpClientUtil.DEFAULT_CONNECT_TIMEOUT; } + if (keyStoreReloadInterval > 0 && this.httpClient != null) { + log.warn("keyStoreReloadInterval can't be set when using external httpClient"); + keyStoreReloadInterval = 0; + } + Http2SolrClient client = new Http2SolrClient(baseSolrUrl, this); try { httpClientBuilderSetup(client); @@ -1208,6 +1237,23 @@ public Builder withMaxConnectionsPerHost(int max) { return this; } + /** + * Set the scanning interval to check for updates in the Key Store used by this client. If the + * interval is 0 or less, then the Key Store Scanner is not created, and the client will not + * attempt to update key stores. The minimum value between checks is 1 second. + * + * @param interval Interval between checks + * @param unit The unit for the interval + * @return This builder + */ + public Builder withKeyStoreReloadInterval(long interval, TimeUnit unit) { + this.keyStoreReloadInterval = unit.toSeconds(interval); + if (this.keyStoreReloadInterval == 0 && interval > 0) { + this.keyStoreReloadInterval = 1; + } + return this; + } + /** * @deprecated Please use {@link #withIdleTimeout(long, TimeUnit)} */ From f327b79c1c575bca197f799c8e888c631a2af499 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Wed, 15 Nov 2023 12:13:57 -0800 Subject: [PATCH 04/15] Restore original tests --- solr/packaging/test/test_ssl.bats | 466 ++++++++++++++++++++++++++++++ 1 file changed, 466 insertions(+) diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 452ae058e5a..047cd49fe84 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -29,6 +29,472 @@ teardown() { solr stop -all >/dev/null 2>&1 } +@test "start solr with ssl" { + # Create a keystore + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + mkdir -p "$ssl_dir" + ( + cd "$ssl_dir" + rm -f solr-ssl.keystore.p12 solr-ssl.pem + keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_NEED_CLIENT_AUTH=false + export SOLR_SSL_WANT_CLIENT_AUTH=false + export SOLR_HOST=localhost + + solr start -c + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + run curl --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" +} + +@test "use different hostname when not checking peer-name" { + # Create a keystore + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + mkdir -p "$ssl_dir" + ( + cd "$ssl_dir" + rm -f solr-ssl.keystore.p12 solr-ssl.pem + # Using a CN that is not localhost, as we will not be checking peer-name + keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext "SAN=DNS:test.solr.apache.org,IP:127.0.0.1" -dname "CN=test.solr.apache.org, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_NEED_CLIENT_AUTH=false + export SOLR_SSL_WANT_CLIENT_AUTH=false + export SOLR_SSL_CHECK_PEER_NAME=false + export SOLR_HOST=127.0.0.1 + + solr start -c + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + # Just test that curl can connect via insecure or via a custom host header + run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial 'no alternative certificate subject name matches target host name' + + # Just test that curl can connect via insecure or via a custom host header + run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" -k "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" --resolve "test.solr.apache.org:${SOLR_PORT}:127.0.0.1" "https://test.solr.apache.org:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + # This is a client setting, so we don't need to restart Solr to make sure that it fails + export SOLR_SSL_CHECK_PEER_NAME=true + + # This should fail the peername check + run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --regexp '(No subject alternative DNS name matching localhost found|Server refused connection)' + + # Restart the server enabling the SNI hostcheck + export SOLR_SSL_CHECK_PEER_NAME=false + export SOLR_OPTS="${SOLR_OPTS} -Dsolr.jetty.ssl.sniHostCheck=true" + solr restart -c + # This should fail the SNI Hostname check + run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" + assert_output --partial 'Invalid SNI' + + # Using the right hostname should not fail the SNI Hostname check + run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" --resolve "test.solr.apache.org:${SOLR_PORT}:127.0.0.1" "https://test.solr.apache.org:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" + assert_output --partial '"urlScheme":"https"' +} + +@test "start solr with ssl and auth" { + # Create a keystore + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + mkdir -p "$ssl_dir" + ( + cd "$ssl_dir" + rm -f solr-ssl.keystore.p12 solr-ssl.pem + keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + openssl pkcs12 -in solr-ssl.keystore.p12 -out solr-ssl.pem -passin pass:secret -passout pass:secret + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_NEED_CLIENT_AUTH=false + export SOLR_SSL_WANT_CLIENT_AUTH=false + export SOLR_SSL_CHECK_PEER_NAME=true + export SOLR_HOST=localhost + + solr start -c + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + solr auth enable -type basicAuth -credentials name:password + + run curl -u name:password --basic --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CREATE&collection.configName=_default&name=test&numShards=2&replicationFactor=1&router.name=compositeId&wt=json" + assert_output --partial '"status":0' + + run curl -u name:password --basic --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":0' + + # When the Jenkins box "curl" supports --fail-with-body, add "--fail-with-body" and change "run" to "run !", to expect a failure + run curl --http2 --cacert "$ssl_dir/solr-ssl.pem" "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial 'Error 401 Authentication' +} + +@test "start solr with client truststore and security manager" { + # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR + test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" + mkdir -p "${test_tmp_dir}" + test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" + + export SOLR_SECURITY_MANAGER_ENABLED=true + export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" + export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" + + # Create a keystore + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + export client_ssl_dir="${ssl_dir}-client" + mkdir -p "$ssl_dir" + ( + cd "$ssl_dir" + rm -f solr-ssl.keystore.p12 + keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.p12 -storetype PKCS12 -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" + ) + mkdir -p "$client_ssl_dir" + ( + cd "$client_ssl_dir" + rm -f * + keytool -export -alias solr-ssl -file solr-ssl.crt -keystore "$ssl_dir/solr-ssl.keystore.p12" -keypass secret -storepass secret + keytool -import -v -trustcacerts -alias solr-ssl -file solr-ssl.crt -storetype PKCS12 -keystore solr-ssl.truststore.p12 -keypass secret -storepass secret -noprompt + ) + cp -R "$ssl_dir" "$client_ssl_dir" + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_KEY_STORE_PASSWORD=secret + export SOLR_SSL_TRUST_STORE=$ssl_dir/solr-ssl.keystore.p12 + export SOLR_SSL_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_CLIENT_TRUST_STORE=$client_ssl_dir/solr-ssl.truststore.p12 + export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=secret + export SOLR_SSL_NEED_CLIENT_AUTH=false + export SOLR_SSL_WANT_CLIENT_AUTH=true + export SOLR_SSL_CHECK_PEER_NAME=true + export SOLR_HOST=localhost + export SOLR_SECURITY_MANAGER_ENABLED=true + + run solr start -c + + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + + export SOLR_SSL_KEY_STORE= + export SOLR_SSL_KEY_STORE_PASSWORD= + export SOLR_SSL_TRUST_STORE= + export SOLR_SSL_TRUST_STORE_PASSWORD= + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" + assert_output --partial '"urlScheme":"https"' +} + +@test "start solr with mTLS needed" { + # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR + test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" + mkdir -p "${test_tmp_dir}" + test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" + + export SOLR_SECURITY_MANAGER_ENABLED=true + export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" + export SOLR_LOG_LEVEL="DEBUG" + export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" # To debug further use: -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake + + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + export server_ssl_dir="${ssl_dir}/server" + export client_ssl_dir="${ssl_dir}/client" + + # Create a root & intermediary CA + echo "${ssl_dir}" + mkdir -p "${ssl_dir}" + ( + cd "$ssl_dir" + rm -f root.p12 root.pem ca.p12 ca.pem + + keytool -genkeypair -keystore root.p12 -storetype PKCS12 -keypass secret -storepass secret -alias root -ext bc:c -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + keytool -genkeypair -keystore ca.p12 -storetype PKCS12 -keypass secret -storepass secret -alias ca -ext bc:c -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + keytool -keystore root.p12 -storetype PKCS12 -storepass secret -alias root -exportcert -rfc > root.pem + + keytool -storepass secret -storetype PKCS12 -keystore ca.p12 -certreq -alias ca | \ + keytool -storepass secret -keystore root.p12 -storetype PKCS12 \ + -gencert -alias root -ext BC=0 -rfc > ca.pem + keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias root -file root.pem -noprompt + keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias ca -file ca.pem + ) + # Create a server keystore & truststore + mkdir -p "$server_ssl_dir" + ( + cd "$server_ssl_dir" + rm -f solr-server.keystore.p12 server.pem solr-server.truststore.p12 + + # Create a keystore and certificate + keytool -genkeypair -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -alias server -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + # Trust the keystore cert with the CA + keytool -storepass server-key -keystore solr-server.keystore.p12 -storetype PKCS12 -certreq -alias server | \ + keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ + -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=serverAuth -rfc > server.pem + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias server -file server.pem + + # Create a truststore with just the Root CA + keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + ) + # Create a client keystore & truststore + # The client cert will have a bogus DNS name and IP address, as we want the clientHostnameVerification to fail + mkdir -p "$client_ssl_dir" + ( + cd "$client_ssl_dir" + rm -f solr-client.keystore.p12 client.pem solr-client.truststore.p12 + + # Create a keystore and certificate + keytool -genkeypair -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -alias client -ext SAN=DNS:test.solr.apache.org,IP:127.0.0.2 -dname "CN=test.solr.apache.org, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + # Trust the keystore cert with the CA + keytool -storepass client-key -keystore solr-client.keystore.p12 -storetype PKCS12 -certreq -alias client | \ + keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ + -ext "SAN=DNS:test.solr.apache.org,IP:127.0.0.2" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=clientAuth -rfc > client.pem + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias client -file client.pem + + # Create a truststore with just the Root CA + keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE="$server_ssl_dir/solr-server.keystore.p12" + export SOLR_SSL_KEY_STORE_PASSWORD=server-key + export SOLR_SSL_KEY_STORE_TYPE=PKCS12 + export SOLR_SSL_TRUST_STORE="$server_ssl_dir/solr-server.truststore.p12" + export SOLR_SSL_TRUST_STORE_PASSWORD=server-trust + export SOLR_SSL_TRUST_STORE_TYPE=PKCS12 + export SOLR_SSL_CLIENT_KEY_STORE="$client_ssl_dir/solr-client.keystore.p12" + export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD=client-key + export SOLR_SSL_CLIENT_KEY_STORE_TYPE=PKCS12 + export SOLR_SSL_CLIENT_TRUST_STORE="$client_ssl_dir/solr-client.truststore.p12" + export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=client-trust + export SOLR_SSL_CLIENT_TRUST_STORE_TYPE=PKCS12 + export SOLR_SSL_NEED_CLIENT_AUTH=true + export SOLR_SSL_WANT_CLIENT_AUTH=false + export SOLR_SSL_CHECK_PEER_NAME=true + # Cannot set this to true, because the client certificate does not have the right hostname ("localhost") or IP + export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=false + export SOLR_HOST=localhost + + solr start -c + solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} + + # Test Client connections, which do not need the server keystore/truststore + ( + export SOLR_SSL_KEY_STORE= + export SOLR_SSL_KEY_STORE_PASSWORD= + export SOLR_SSL_TRUST_STORE= + export SOLR_SSL_TRUST_STORE_PASSWORD= + + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" + assert_output --partial '"urlScheme":"https"' + + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --partial '"numFound":0' + + ( + # Try connecting with just the client truststore, which should fail because mTLS requires a keystore and mTLS is needed + export SOLR_SSL_CLIENT_KEY_STORE= + export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD= + + run ! solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --regexp '(bad_certificate|Server refused connection)' + ) + ) + + # Turn on client hostname verification, and start a new Solr node since the property is a server setting. + # Test that it fails because the client cert does not use "localhost" + export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=true + solr start -c -z localhost:${ZK_PORT} -p ${SOLR3_PORT} + + # We can't check if the server has come up, because we can't connect to it, so just wait + sleep 5 + + run ! solr api -verbose -get "https://localhost:${SOLR3_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --regexp '(certificate_unknown|java.nio.channels.ClosedChannelException|Server refused connection)' +} + +@test "start solr with mTLS wanted" { + # Make a test tmp dir, as the security policy includes TMP, so that might already contain the BATS_TEST_TMPDIR + test_tmp_dir="${BATS_TEST_TMPDIR}/tmp" + mkdir -p "${test_tmp_dir}" + test_tmp_dir="$(cd -P "${test_tmp_dir}" && pwd)" + + export SOLR_SECURITY_MANAGER_ENABLED=true + export SOLR_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" + export SOLR_TOOL_OPTS="-Djava.io.tmpdir=${test_tmp_dir}" # To debug further use: -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake + + export ssl_dir="${BATS_TEST_TMPDIR}/ssl" + export server_ssl_dir="${ssl_dir}/server" + export client_ssl_dir="${ssl_dir}/client" + + # Create a root & intermediary CA + echo "${ssl_dir}" + mkdir -p "${ssl_dir}" + ( + cd "$ssl_dir" + rm -f root.p12 root.pem ca.p12 ca.pem + + keytool -genkeypair -keystore root.p12 -storetype PKCS12 -keypass secret -storepass secret -alias root -ext bc:c -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + keytool -genkeypair -keystore ca.p12 -storetype PKCS12 -keypass secret -storepass secret -alias ca -ext bc:c -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + keytool -keystore root.p12 -storetype PKCS12 -storepass secret -alias root -exportcert -rfc > root.pem + + keytool -storepass secret -storetype PKCS12 -keystore ca.p12 -certreq -alias ca | \ + keytool -storepass secret -keystore root.p12 -storetype PKCS12 \ + -gencert -alias root -ext BC=0 -rfc > ca.pem + keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias root -file root.pem -noprompt + keytool -keystore ca.p12 -importcert -storetype PKCS12 -storepass secret -alias ca -file ca.pem + ) + # Create a server keystore & truststore + mkdir -p "$server_ssl_dir" + ( + cd "$server_ssl_dir" + rm -f solr-server.keystore.p12 server.pem solr-server.truststore.p12 + + # Create a keystore and certificate + keytool -genkeypair -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -alias server -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + # Trust the keystore cert with the CA + keytool -storepass server-key -keystore solr-server.keystore.p12 -storetype PKCS12 -certreq -alias server | \ + keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ + -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=serverAuth -rfc > server.pem + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + keytool -keystore solr-server.keystore.p12 -storetype PKCS12 -keypass server-key -storepass server-key -importcert -alias server -file server.pem + + # Create a truststore with just the Root CA + keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-server.truststore.p12 -storetype PKCS12 -keypass server-trust -storepass server-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + ) + # Create a client keystore & truststore + mkdir -p "$client_ssl_dir" + ( + cd "$client_ssl_dir" + rm -f solr-client.keystore.p12 client.pem solr-client.truststore.p12 + + # Create a keystore and certificate + keytool -genkeypair -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -alias client -ext "SAN=DNS:localhost,IP:127.0.0.1" -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country" -keyalg rsa + + # Trust the keystore cert with the CA + keytool -storepass client-key -keystore solr-client.keystore.p12 -storetype PKCS12 -certreq -alias client | \ + keytool -storepass secret -keystore "$ssl_dir/ca.p12" -storetype PKCS12 -gencert -alias ca \ + -ext "SAN=DNS:localhost,IP:127.0.0.1" -ext "ku:c=nonRepudiation,digitalSignature,keyEncipherment" -ext eku:c=clientAuth -rfc > client.pem + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + keytool -keystore solr-client.keystore.p12 -storetype PKCS12 -keypass client-key -storepass client-key -importcert -alias client -file client.pem + + # Create a truststore with just the Root CA + keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias root -file "$ssl_dir/root.pem" -noprompt + keytool -keystore solr-client.truststore.p12 -storetype PKCS12 -keypass client-trust -storepass client-trust -importcert -alias ca -file "$ssl_dir/ca.pem" -noprompt + ) + + # Set ENV_VARs so that Solr uses this keystore + export SOLR_SSL_ENABLED=true + export SOLR_SSL_KEY_STORE="$server_ssl_dir/solr-server.keystore.p12" + export SOLR_SSL_KEY_STORE_PASSWORD=server-key + export SOLR_SSL_KEY_STORE_TYPE=PKCS12 + export SOLR_SSL_TRUST_STORE="$server_ssl_dir/solr-server.truststore.p12" + export SOLR_SSL_TRUST_STORE_PASSWORD=server-trust + export SOLR_SSL_TRUST_STORE_TYPE=PKCS12 + export SOLR_SSL_CLIENT_KEY_STORE="$client_ssl_dir/solr-client.keystore.p12" + export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD=client-key + export SOLR_SSL_CLIENT_KEY_STORE_TYPE=PKCS12 + export SOLR_SSL_CLIENT_TRUST_STORE="$client_ssl_dir/solr-client.truststore.p12" + export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD=client-trust + export SOLR_SSL_CLIENT_TRUST_STORE_TYPE=PKCS12 + export SOLR_SSL_NEED_CLIENT_AUTH=false + export SOLR_SSL_WANT_CLIENT_AUTH=true + export SOLR_SSL_CHECK_PEER_NAME=true + export SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION=true + export SOLR_HOST=localhost + + solr start -c + solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} + + export SOLR_SSL_KEY_STORE= + export SOLR_SSL_KEY_STORE_PASSWORD= + export SOLR_SSL_TRUST_STORE= + export SOLR_SSL_TRUST_STORE_PASSWORD= + + solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 + + run solr create -c test -s 2 + assert_output --partial "Created collection 'test'" + + run solr api -get "https://localhost:${SOLR_PORT}/solr/admin/collections?action=CLUSTERSTATUS" + assert_output --partial '"urlScheme":"https"' + + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --partial '"numFound":0' + + export SOLR_SSL_CLIENT_KEY_STORE= + export SOLR_SSL_CLIENT_KEY_STORE_PASSWORD= + + # mTLS requires a keyStore, so just using the truststore would fail if mTLS was "NEED"ed, however it is only "WANT"ed, so its ok + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --partial '"numFound":0' + + export SOLR_SSL_CLIENT_TRUST_STORE= + export SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD= + + # TLS cannot work if a truststore and keystore are not provided (either Server or Client) + run solr api -verbose -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*&rows=0" + assert_output --regexp '(unable to find valid certification path to requested target|Server refused connection)' +} + @test "test keystore reload" { # Create a keystore export ssl_dir="${BATS_TEST_TMPDIR}/ssl" From 243a5147b046f9d6b25311dd94503c9ef7d43067 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Wed, 15 Nov 2023 15:42:58 -0800 Subject: [PATCH 05/15] Update Windows start script --- solr/bin/solr.cmd | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd index c4ba16d1bfe..172ed2c24bf 100755 --- a/solr/bin/solr.cmd +++ b/solr/bin/solr.cmd @@ -84,11 +84,28 @@ IF NOT DEFINED SOLR_SSL_ENABLED ( ) ) +IF NOT DEFINED SOLR_SSL_RELOAD_ENABLED ( + set "SOLR_SSL_RELOAD_ENABLED=true" +) + +REM Enable java security manager by default (limiting filesystem access and other things) +IF NOT DEFINED SOLR_SECURITY_MANAGER_ENABLED ( + set SOLR_SECURITY_MANAGER_ENABLED=true +) + IF "%SOLR_SSL_ENABLED%"=="true" ( - set "SOLR_JETTY_CONFIG=--module=https,ssl-reload --lib="%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*"" + set "SOLR_JETTY_CONFIG=--module=https --lib="%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*"" set SOLR_URL_SCHEME=https + IF "%SOLR_SSL_RELOAD_ENABLED%"=="true" ( + set "SOLR_JETTY_CONFIG=!SOLR_JETTY_CONFIG! --module=ssl-reload" + ) IF DEFINED SOLR_SSL_KEY_STORE ( set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Dsolr.jetty.keystore=%SOLR_SSL_KEY_STORE%" + IF "%SOLR_SSL_RELOAD_ENABLED%"=="true" ( + IF "%SOLR_SECURITY_MANAGER_ENABLED%"=="true" ( + set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Dsolr.jetty.keystoreParentPath=%SOLR_SSL_KEY_STORE%/.." + ) + ) ) IF DEFINED SOLR_SSL_KEY_STORE_TYPE ( @@ -122,6 +139,11 @@ IF "%SOLR_SSL_ENABLED%"=="true" ( IF DEFINED SOLR_SSL_CLIENT_KEY_STORE_TYPE ( set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Djavax.net.ssl.keyStoreType=%SOLR_SSL_CLIENT_KEY_STORE_TYPE%" ) + IF "%SOLR_SSL_RELOAD_ENABLED%"=="true" ( + IF "%SOLR_SECURITY_MANAGER_ENABLED%"=="true" ( + set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Djavax.net.ssl.keyStoreParentPath=%SOLR_SSL_CLIENT_KEY_STORE_TYPE%/.." + ) + ) ) ELSE ( IF DEFINED SOLR_SSL_KEY_STORE ( set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Djavax.net.ssl.keyStore=%SOLR_SSL_KEY_STORE%" @@ -1077,11 +1099,6 @@ IF "%ENABLE_REMOTE_JMX_OPTS%"=="true" ( set REMOTE_JMX_OPTS= ) -REM Enable java security manager by default (limiting filesystem access and other things) -IF NOT DEFINED SOLR_SECURITY_MANAGER_ENABLED ( - set SOLR_SECURITY_MANAGER_ENABLED=true -) - IF "%SOLR_SECURITY_MANAGER_ENABLED%"=="true" ( set SECURITY_MANAGER_OPTS=-Djava.security.manager ^ -Djava.security.policy="%SOLR_SERVER_DIR%\etc\security.policy" ^ From 3be96f677e6245f393ecde83aa385bf059cd9422 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Thu, 16 Nov 2023 11:14:47 -0800 Subject: [PATCH 06/15] Some cleanup and exception handling --- .../solr/client/solrj/io/SolrClientCache.java | 4 --- .../client/solrj/impl/Http2SolrClient.java | 32 +++++++++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java index f3da49bb0c1..e56d1a55c13 100644 --- a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java +++ b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java @@ -53,9 +53,6 @@ public class SolrClientCache implements Closeable { private static final int minSocketTimeout = Math.max(Integer.getInteger(HttpClientUtil.PROP_SO_TIMEOUT, MIN_TIMEOUT), MIN_TIMEOUT); - private static final int keyStoreReloadInterval = - Integer.getInteger("solr.jetty.sslContext.reload.scanInterval", 30); - private final Map solrClients = new HashMap<>(); private final HttpClient apacheHttpClient; private final Http2SolrClient http2SolrClient; @@ -162,7 +159,6 @@ private static Http2SolrClient.Builder newHttp2SolrClientBuilder( connTimeout = Math.max(idleTimeout, builder.getConnectionTimeout()); } builder.withConnectionTimeout(connTimeout, TimeUnit.MILLISECONDS); - builder.withKeyStoreReloadInterval(keyStoreReloadInterval, TimeUnit.SECONDS); return builder; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index d7eeafa71c0..a5518326137 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -242,12 +242,22 @@ private HttpClient createHttpClient(Builder builder) { if (sslContextFactory != null && sslContextFactory.getKeyStoreResource() != null - && builder.keyStoreReloadInterval > 0) { + && builder.keyStoreReloadIntervalSecs > 0) { scanner = new KeyStoreScanner(sslContextFactory); try { scanner.start(); + if (log.isDebugEnabled()) { + log.debug("Key Store Scanner started"); + } } catch (Exception e) { - throw new RuntimeException(e); + RuntimeException startException = + new RuntimeException("Unable to start key store scanner", e); + try { + scanner.stop(); + } catch (Exception stopException) { + startException.addSuppressed(stopException); + } + throw startException; } } @@ -351,9 +361,11 @@ public void close() { if (scanner != null) { try { scanner.stop(); + if (log.isDebugEnabled()) { + log.debug("Key Store Scanner stopped"); + } } catch (Exception e) { - // TODO - throw new RuntimeException(e); + throw new RuntimeException("Unable top stop key store scanner", e); } } @@ -1065,7 +1077,7 @@ public static class Builder { private int proxyPort; private boolean proxyIsSocks4; private boolean proxyIsSecure; - private long keyStoreReloadInterval = 30; + private long keyStoreReloadIntervalSecs = 0; public Builder() {} @@ -1081,9 +1093,9 @@ public Http2SolrClient build() { connectionTimeoutMillis = (long) HttpClientUtil.DEFAULT_CONNECT_TIMEOUT; } - if (keyStoreReloadInterval > 0 && this.httpClient != null) { + if (keyStoreReloadIntervalSecs > 0 && this.httpClient != null) { log.warn("keyStoreReloadInterval can't be set when using external httpClient"); - keyStoreReloadInterval = 0; + keyStoreReloadIntervalSecs = 0; } Http2SolrClient client = new Http2SolrClient(baseSolrUrl, this); @@ -1247,9 +1259,9 @@ public Builder withMaxConnectionsPerHost(int max) { * @return This builder */ public Builder withKeyStoreReloadInterval(long interval, TimeUnit unit) { - this.keyStoreReloadInterval = unit.toSeconds(interval); - if (this.keyStoreReloadInterval == 0 && interval > 0) { - this.keyStoreReloadInterval = 1; + this.keyStoreReloadIntervalSecs = unit.toSeconds(interval); + if (this.keyStoreReloadIntervalSecs == 0 && interval > 0) { + this.keyStoreReloadIntervalSecs = 1; } return this; } From 0bf531a43cd4bb6abe5c20daa5358998c4ad1bd3 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Mon, 27 Nov 2023 12:01:02 -0800 Subject: [PATCH 07/15] Remove commented out code from bats test --- solr/packaging/test/test_ssl.bats | 1 - 1 file changed, 1 deletion(-) diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 047cd49fe84..99e5efa7d6d 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -559,7 +559,6 @@ teardown() { # Give some time for the server reload sleep 6 - # run ! solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=query2" run ! solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query2" ( From bdeaeb2efe3e2cb19b2f158a4792561e27624ebe Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Mon, 27 Nov 2023 17:24:43 -0800 Subject: [PATCH 08/15] Some more tests --- solr/packaging/test/test_ssl.bats | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 99e5efa7d6d..6a49e66f1b9 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -29,6 +29,7 @@ teardown() { solr stop -all >/dev/null 2>&1 } + @test "start solr with ssl" { # Create a keystore export ssl_dir="${BATS_TEST_TMPDIR}/ssl" @@ -516,34 +517,48 @@ teardown() { # Set ENV_VARs so that Solr uses this keystore export SOLR_SSL_ENABLED=true - export SOLR_SSL_KEY_STORE=$ssl_dir/server1.keystore.p12 export SOLR_SSL_KEY_STORE_PASSWORD=secret - export SOLR_SSL_TRUST_STORE=$ssl_dir/server1.keystore.p12 export SOLR_SSL_TRUST_STORE_PASSWORD=secret export SOLR_SSL_NEED_CLIENT_AUTH=true export SOLR_SSL_WANT_CLIENT_AUTH=false export SOLR_HOST=localhost + # server1 will run on $SOLR_PORT and will use server1.keystore + export SOLR_SSL_KEY_STORE=$ssl_dir/server1.keystore.p12 + export SOLR_SSL_TRUST_STORE=$ssl_dir/server1.keystore.p12 solr start -c -a "-Dsolr.jetty.sslContext.reload.scanInterval=1 -DsocketTimeout=5000" solr assert --started https://localhost:${SOLR_PORT}/solr --timeout 5000 + # server2 will run on $SOLR2_PORT and will use server2.keystore. Initially, this is the same as server1.keystore export SOLR_SSL_KEY_STORE=$ssl_dir/server2.keystore.p12 export SOLR_SSL_TRUST_STORE=$ssl_dir/server2.keystore.p12 solr start -c -z localhost:${ZK_PORT} -p ${SOLR2_PORT} -a "-Dsolr.jetty.sslContext.reload.scanInterval=1 -DsocketTimeout=5000" solr assert --started https://localhost:${SOLR2_PORT}/solr --timeout 5000 + # "test" collection is two shards, meaning there must be communication between shards for queries (handled by http shard handler factory) run solr create -c test -s 2 assert_output --partial "Created collection 'test'" + # "test-single-shard" is one shard and one replica, this means that one of the nodes will have to forward requests to the other + run solr create -c test-single-shard -s 1 + assert_output --partial "Created collection 'test-single-shard'" + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" assert_output --partial '"numFound":0' - run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*" assert_output --partial '"numFound":0' + run solr api -get "https://localhost:${SOLR_PORT}/solr/test-single-shard/select?q=*:*" + assert_output --partial '"numFound":0' + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test-single-shard/select?q=*:*" + assert_output --partial '"numFound":0' + run ! curl "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" run ! curl "https://localhost:${SOLR2_PORT}/solr/test/select?q=*:*" + run ! curl "https://localhost:${SOLR_PORT}/solr/test-single-shard/select?q=*:*" + run ! curl "https://localhost:${SOLR2_PORT}/solr/test-single-shard/select?q=*:*" + export SOLR_SSL_KEY_STORE=$ssl_dir/cert2.keystore.p12 export SOLR_SSL_KEY_STORE_PASSWORD=secret export SOLR_SSL_TRUST_STORE=$ssl_dir/cert2.keystore.p12 @@ -559,6 +574,7 @@ teardown() { # Give some time for the server reload sleep 6 + # Server 2 still uses the cert1, so this request should fail run ! solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query2" ( @@ -575,4 +591,9 @@ teardown() { run solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query3" assert_output --partial '"numFound":0' + run solr api -get "https://localhost:${SOLR_PORT}/solr/test-single-shard/select?q=query4" + assert_output --partial '"numFound":0' + + run solr api -get "https://localhost:${SOLR2_PORT}/solr/test-single-shard/select?q=query4" + assert_output --partial '"numFound":0' } \ No newline at end of file From 539dbcb312ec6c01248fb20fecf1acaab56dbd48 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Tue, 28 Nov 2023 11:03:55 -0800 Subject: [PATCH 09/15] Add newline --- solr/server/modules/ssl-reload.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/server/modules/ssl-reload.mod b/solr/server/modules/ssl-reload.mod index 340eae041e3..60e52537733 100644 --- a/solr/server/modules/ssl-reload.mod +++ b/solr/server/modules/ssl-reload.mod @@ -9,4 +9,4 @@ ssl ssl [xml] -etc/jetty-ssl-context-reload.xml \ No newline at end of file +etc/jetty-ssl-context-reload.xml From 6aa315b5ae8b850e862aa57e6d2fc0ee6152b17e Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Tue, 28 Nov 2023 12:05:12 -0800 Subject: [PATCH 10/15] Minor improvement to the bat test --- solr/packaging/test/test_ssl.bats | 7 +++++++ solr/server/etc/jetty-ssl-context-reload.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 6a49e66f1b9..686307d6838 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -574,9 +574,13 @@ teardown() { # Give some time for the server reload sleep 6 + run solr healthcheck -solrUrl https://localhost:${SOLR_PORT} + # Server 2 still uses the cert1, so this request should fail run ! solr api -get "https://localhost:${SOLR2_PORT}/solr/test/select?q=query2" + run ! solr healthcheck -solrUrl https://localhost:${SOLR2_PORT} + ( cd "$ssl_dir" # Replace server2 keystore with client's @@ -585,6 +589,9 @@ teardown() { # Give some time for the server reload sleep 6 + run solr healthcheck -solrUrl https://localhost:${SOLR_PORT} + run solr healthcheck -solrUrl https://localhost:${SOLR2_PORT} + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=query3" assert_output --partial '"numFound":0' diff --git a/solr/server/etc/jetty-ssl-context-reload.xml b/solr/server/etc/jetty-ssl-context-reload.xml index 2bdc4153346..827d80c3529 100644 --- a/solr/server/etc/jetty-ssl-context-reload.xml +++ b/solr/server/etc/jetty-ssl-context-reload.xml @@ -10,4 +10,4 @@ - \ No newline at end of file + From b8b2e8907531dbf487cdf4bf8faa06965faed759 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Tue, 28 Nov 2023 14:49:35 -0800 Subject: [PATCH 11/15] Initialize scanner in http client if reload system property is present --- solr/bin/solr | 1 + solr/bin/solr.cmd | 1 + .../java/org/apache/solr/cli/CreateTool.java | 1 + .../java/org/apache/solr/cli/DeleteTool.java | 1 + .../org/apache/solr/cli/PostLogsTool.java | 5 ++- .../src/java/org/apache/solr/cli/SolrCLI.java | 1 + .../component/HttpShardHandlerFactory.java | 11 ------ solr/packaging/test/test_ssl.bats | 5 +++ .../client/solrj/impl/Http2SolrClient.java | 37 ++++++++++--------- 9 files changed, 33 insertions(+), 30 deletions(-) diff --git a/solr/bin/solr b/solr/bin/solr index e979060beba..0ad6b8abcc5 100755 --- a/solr/bin/solr +++ b/solr/bin/solr @@ -210,6 +210,7 @@ if [ "$SOLR_SSL_ENABLED" == "true" ]; then SOLR_JETTY_CONFIG+=("--module=https" "--lib=$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*") if [ "${SOLR_SSL_RELOAD_ENABLED:-true}" == "true" ]; then SOLR_JETTY_CONFIG+=("--module=ssl-reload") + SOLR_SSL_OPTS+=" -Dsolr.keyStoreReload.enabled=true" fi SOLR_URL_SCHEME=https if [ -n "$SOLR_SSL_KEY_STORE" ]; then diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd index 172ed2c24bf..124ef286c14 100755 --- a/solr/bin/solr.cmd +++ b/solr/bin/solr.cmd @@ -98,6 +98,7 @@ IF "%SOLR_SSL_ENABLED%"=="true" ( set SOLR_URL_SCHEME=https IF "%SOLR_SSL_RELOAD_ENABLED%"=="true" ( set "SOLR_JETTY_CONFIG=!SOLR_JETTY_CONFIG! --module=ssl-reload" + set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Dsolr.keyStoreReload.enabled=true" ) IF DEFINED SOLR_SSL_KEY_STORE ( set "SOLR_SSL_OPTS=!SOLR_SSL_OPTS! -Dsolr.jetty.keystore=%SOLR_SSL_KEY_STORE%" diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index 5eca1c272ed..80db530072e 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -202,6 +202,7 @@ protected void createCollection(CommandLine cli) throws Exception { new Http2SolrClient.Builder() .withIdleTimeout(30, TimeUnit.SECONDS) .withConnectionTimeout(15, TimeUnit.SECONDS) + .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials( cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt())); String zkHost = SolrCLI.getZkHost(cli); diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java index f709c9defa2..26df3563675 100644 --- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java @@ -107,6 +107,7 @@ protected void deleteCollection(CommandLine cli) throws Exception { new Http2SolrClient.Builder() .withIdleTimeout(30, TimeUnit.SECONDS) .withConnectionTimeout(15, TimeUnit.SECONDS) + .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials(cli.getOptionValue(("credentials"))); String zkHost = SolrCLI.getZkHost(cli); diff --git a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java index 7a6c21649f2..3995de32fd5 100644 --- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java +++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -90,7 +91,9 @@ public void runImpl(CommandLine cli) throws Exception { public void runCommand(String baseUrl, String root, String credentials) throws IOException { Http2SolrClient.Builder builder = - new Http2SolrClient.Builder(baseUrl).withOptionalBasicAuthCredentials(credentials); + new Http2SolrClient.Builder(baseUrl) + .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) + .withOptionalBasicAuthCredentials(credentials); try (SolrClient client = builder.build()) { int rec = 0; UpdateRequest request = new UpdateRequest(); diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java index 14d9ab7f7c9..c30b972f9aa 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -416,6 +416,7 @@ public static SolrClient getSolrClient(String solrUrl, String credentials, boole Http2SolrClient.Builder builder = new Http2SolrClient.Builder(solrUrl) .withMaxConnectionsPerHost(32) + .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials(credentials); return builder.build(); diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java index 8f59c2ea2c2..5b227359701 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java +++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java @@ -126,9 +126,6 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory // Configure if the threadpool favours fairness over throughput static final String INIT_FAIRNESS_POLICY = "fairnessPolicy"; - // Determines how often to check for key store changes in the client - static final String KEY_STORE_RELOAD_PERIOD_SECS = "keyStoreReloadPeriodSecs"; - /** Get {@link ShardHandler} that uses the default http client. */ @Override public ShardHandler getShardHandler() { @@ -282,20 +279,12 @@ public void init(PluginInfo info) { int soTimeout = getParameter(args, HttpClientUtil.PROP_SO_TIMEOUT, HttpClientUtil.DEFAULT_SO_TIMEOUT, sb); - int keystoreReloadIntervalSecs = - getParameter( - args, - KEY_STORE_RELOAD_PERIOD_SECS, - Integer.getInteger("solr.jetty.sslContext.reload.scanInterval", 30), - sb); - this.defaultClient = new Http2SolrClient.Builder() .withConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS) .withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS) .withExecutor(commExecutor) .withMaxConnectionsPerHost(maxConnectionsPerHost) - .withKeyStoreReloadInterval(keystoreReloadIntervalSecs, TimeUnit.SECONDS) .build(); this.defaultClient.addListenerFactory(this.httpListenerFactory); this.loadbalancer = new LBHttp2SolrClient.Builder(defaultClient).build(); diff --git a/solr/packaging/test/test_ssl.bats b/solr/packaging/test/test_ssl.bats index 686307d6838..7d02ebebe03 100644 --- a/solr/packaging/test/test_ssl.bats +++ b/solr/packaging/test/test_ssl.bats @@ -603,4 +603,9 @@ teardown() { run solr api -get "https://localhost:${SOLR2_PORT}/solr/test-single-shard/select?q=query4" assert_output --partial '"numFound":0' + + run solr post -url https://localhost:${SOLR_PORT}/solr/test/update -commit ${SOLR_TIP}/example/exampledocs/books.csv + + run solr api -get "https://localhost:${SOLR_PORT}/solr/test/select?q=*:*" + assert_output --partial '"numFound":10' } \ No newline at end of file diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index a5518326137..7222846f7c7 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -242,6 +242,7 @@ private HttpClient createHttpClient(Builder builder) { if (sslContextFactory != null && sslContextFactory.getKeyStoreResource() != null + && builder.keyStoreReloadIntervalSecs != null && builder.keyStoreReloadIntervalSecs > 0) { scanner = new KeyStoreScanner(sslContextFactory); try { @@ -349,6 +350,14 @@ public void close() { if (closeClient) { httpClient.stop(); httpClient.destroy(); + + if (scanner != null) { + scanner.stop(); + if (log.isDebugEnabled()) { + log.debug("Key Store Scanner stopped"); + } + scanner = null; + } } } catch (Exception e) { throw new RuntimeException("Exception on closing client", e); @@ -357,18 +366,6 @@ public void close() { ExecutorUtil.shutdownAndAwaitTermination(executor); } } - - if (scanner != null) { - try { - scanner.stop(); - if (log.isDebugEnabled()) { - log.debug("Key Store Scanner stopped"); - } - } catch (Exception e) { - throw new RuntimeException("Unable top stop key store scanner", e); - } - } - assert ObjectReleaseTracker.release(this); } @@ -1077,7 +1074,7 @@ public static class Builder { private int proxyPort; private boolean proxyIsSocks4; private boolean proxyIsSecure; - private long keyStoreReloadIntervalSecs = 0; + private Long keyStoreReloadIntervalSecs; public Builder() {} @@ -1093,9 +1090,13 @@ public Http2SolrClient build() { connectionTimeoutMillis = (long) HttpClientUtil.DEFAULT_CONNECT_TIMEOUT; } - if (keyStoreReloadIntervalSecs > 0 && this.httpClient != null) { + if (keyStoreReloadIntervalSecs != null && keyStoreReloadIntervalSecs > 0 && this.httpClient != null) { log.warn("keyStoreReloadInterval can't be set when using external httpClient"); - keyStoreReloadIntervalSecs = 0; + keyStoreReloadIntervalSecs = null; + } else if (keyStoreReloadIntervalSecs == null + && this.httpClient == null + && Boolean.getBoolean("solr.keyStoreReload.enabled")) { + keyStoreReloadIntervalSecs = Long.getLong("solr.jetty.sslContext.reload.scanInterval", 30); } Http2SolrClient client = new Http2SolrClient(baseSolrUrl, this); @@ -1251,8 +1252,8 @@ public Builder withMaxConnectionsPerHost(int max) { /** * Set the scanning interval to check for updates in the Key Store used by this client. If the - * interval is 0 or less, then the Key Store Scanner is not created, and the client will not - * attempt to update key stores. The minimum value between checks is 1 second. + * interval is unset, 0 or less, then the Key Store Scanner is not created, and the client will + * not attempt to update key stores. The minimum value between checks is 1 second. * * @param interval Interval between checks * @param unit The unit for the interval @@ -1261,7 +1262,7 @@ public Builder withMaxConnectionsPerHost(int max) { public Builder withKeyStoreReloadInterval(long interval, TimeUnit unit) { this.keyStoreReloadIntervalSecs = unit.toSeconds(interval); if (this.keyStoreReloadIntervalSecs == 0 && interval > 0) { - this.keyStoreReloadIntervalSecs = 1; + this.keyStoreReloadIntervalSecs = 1L; } return this; } From 9bf138e00b9f338baca59292942260174ba71215 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Tue, 28 Nov 2023 14:57:46 -0800 Subject: [PATCH 12/15] tidy --- .../org/apache/solr/client/solrj/impl/Http2SolrClient.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index 7222846f7c7..d7b9db2c9f0 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -1090,7 +1090,9 @@ public Http2SolrClient build() { connectionTimeoutMillis = (long) HttpClientUtil.DEFAULT_CONNECT_TIMEOUT; } - if (keyStoreReloadIntervalSecs != null && keyStoreReloadIntervalSecs > 0 && this.httpClient != null) { + if (keyStoreReloadIntervalSecs != null + && keyStoreReloadIntervalSecs > 0 + && this.httpClient != null) { log.warn("keyStoreReloadInterval can't be set when using external httpClient"); keyStoreReloadIntervalSecs = null; } else if (keyStoreReloadIntervalSecs == null From a910a022c7710720943d6e31cf2a991ec3d15ecf Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Wed, 29 Nov 2023 09:58:33 -0800 Subject: [PATCH 13/15] Add documentation to reference guide --- .../deployment-guide/pages/enabling-ssl.adoc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc index 685acdb5da4..38ca9cd9e2f 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc @@ -323,6 +323,23 @@ C:\> bin\solr.cmd -cloud -s cloud\node1 -z server1:2181,server2:2181,server3:218 ==== -- +== Automatically reloading KeyStore/TrustStore +=== Solr Server +Solr can automatically reload KeyStore/TrustStore when certificates are updated without restarting. This is enabled by default +when using SSL, but can be disabled by setting the environment variable `SOLR_SSL_RELOAD_ENABLED` to `false`. By +default, Solr will check for updates in the KeyStore every 30 seconds, but this interval can be updated by passing the +system property `solr.jetty.sslContext.reload.scanInterval` with the new interval in seconds on startup. +Note that the truststore file is not actively monitored, so if you need to apply changes to the truststore, you need +to update it and after that touch the keystore to trigger a reload. + +=== SolrJ client +Http2SolrClient builder has a method `withKeyStoreReloadInterval(long interval, TimeUnit unit)` to initialize a scanner +that will watch and update the keystore and truststore for changes. If you are using CloudHttp2SolrClient, you can use +the `withInternalClientBuilder(Http2SolrClient.Builder internalClientBuilder)` to configure the internal http client +with a keystore reload interval. The minimum reload interval is 1 second. If not set (or set to 0 or a negative value), +the keystore/truststore won't be updated in the client. + + == Example Client Actions [IMPORTANT] From 2f7bbf7ceafd0a2945d6e6d6d2edd8cc712c83c6 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Wed, 29 Nov 2023 14:54:20 -0800 Subject: [PATCH 14/15] Set the interval to the client scanner --- .../org/apache/solr/client/solrj/impl/Http2SolrClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index d7b9db2c9f0..0ff998c35fd 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -246,6 +246,7 @@ private HttpClient createHttpClient(Builder builder) { && builder.keyStoreReloadIntervalSecs > 0) { scanner = new KeyStoreScanner(sslContextFactory); try { + scanner.setScanInterval((int) Math.min(builder.keyStoreReloadIntervalSecs, Integer.MAX_VALUE)); scanner.start(); if (log.isDebugEnabled()) { log.debug("Key Store Scanner started"); @@ -1093,7 +1094,7 @@ public Http2SolrClient build() { if (keyStoreReloadIntervalSecs != null && keyStoreReloadIntervalSecs > 0 && this.httpClient != null) { - log.warn("keyStoreReloadInterval can't be set when using external httpClient"); + log.warn("keyStoreReloadIntervalSecs can't be set when using external httpClient"); keyStoreReloadIntervalSecs = null; } else if (keyStoreReloadIntervalSecs == null && this.httpClient == null From 216f11925a0ac36e8cb6834785be276ba4f1e715 Mon Sep 17 00:00:00 2001 From: Tomas Fernandez Lobbe Date: Wed, 29 Nov 2023 15:07:51 -0800 Subject: [PATCH 15/15] Add CHANGES entry and tidy --- solr/CHANGES.txt | 3 +++ .../org/apache/solr/client/solrj/impl/Http2SolrClient.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 4fefa529f97..2325b72a2bd 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -85,6 +85,9 @@ New Features * SOLR-17079: Allow to declare replica placement plugins in solr.xml (Vincent Primault) +* SOLR-16743: When using TLS, Solr can now auto-reload the keystore and truststore without the need to restart the process. + This is enabled by default when running with TLS and can be disabled or configured in solr.in.sh (Houston Putman, Tomás Fernández Löbbe) + Improvements --------------------- * SOLR-16924: RESTORECORE now sets the UpdateLog to ACTIVE state instead of requiring a separate diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index 0ff998c35fd..00e94715cfd 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -246,7 +246,8 @@ private HttpClient createHttpClient(Builder builder) { && builder.keyStoreReloadIntervalSecs > 0) { scanner = new KeyStoreScanner(sslContextFactory); try { - scanner.setScanInterval((int) Math.min(builder.keyStoreReloadIntervalSecs, Integer.MAX_VALUE)); + scanner.setScanInterval( + (int) Math.min(builder.keyStoreReloadIntervalSecs, Integer.MAX_VALUE)); scanner.start(); if (log.isDebugEnabled()) { log.debug("Key Store Scanner started");