diff --git a/README.md b/README.md
index fd53fedd..029801dc 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,8 @@ public class Item {
// Init a database instance and choose the binding (builtin). Configure connection pool size and connection
// timeout. Database should be created explicitly via reindexer_tool.
+ // To connect to Reindexer with TLS, use cprotos:// protocol with default port 6535.
+ // Use ReindexerConfiguration#sslSocketFactory to provide a custom SSLSocketFactory.
Reindexer db = ReindexerConfiguration.builder()
.url("cproto://localhost:6534/testdb")
.connectionPoolSize(1)
@@ -354,3 +356,21 @@ Depends on amount changes in transaction there are 2 possible Commit strategies:
2. Transaction object holds Reindexer's resources, therefore application should explicitly call `tx.rollback` or `tx.commit`, otherwise resources will leak.
3. It is safe to call `tx.rollback` after `tx.commit`.
4. It is possible to call Query from transaction by call `tx.query().execute(); ...`. Only read-committed isolation is available. Changes made in active transaction is invisible to current and another transactions.
+
+### Development notes
+
+To run tests locally, you need to install Reindexer using a package manager for your OS.
+Cprotos protocol tests require Reindexer to be built with TLS support, and ssl certificate and key must be placed in
+src/test/resources, to generate a new valid certificate run the following commands:
+
+```bash
+cd src/test/resources
+# Generates a certificate key (not needed if you already have one).
+openssl genrsa -out builtin-server.key 2048
+# Generates a self-signed certificate using the provided key;
+# Prompts to fill certificate information e.g. CN=localhost;
+# The certificate will be valid for 10 years.
+openssl req -new -x509 -key builtin-server.key -out builtin-server.crt -days 3650
+# Import the certificate into the Java keystore and save it as a JKS file.
+keytool -importcert -alias builtin-server -file builtin-server.crt -keystore builtin-server.jks -storepass password -noprompt
+```
diff --git a/builtin-adapter/CMakeLists.txt b/builtin-adapter/CMakeLists.txt
index 615a0a9d..d48cd2bb 100644
--- a/builtin-adapter/CMakeLists.txt
+++ b/builtin-adapter/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(builtin-adapter)
@@ -17,3 +17,17 @@ add_library(${TARGET} SHARED BuiltinAdapter.cpp)
include_directories(${JNI_INCLUDE_DIRS} ${REINDEXER_INCLUDE_DIRS})
target_link_libraries(${TARGET} reindexer_server_library reindexer_server_resources ${REINDEXER_LIBRARIES})
+
+# Workaround for macOS to propagate a correct OpenSSL lib path to Reindexer.
+if(APPLE)
+ find_package(OpenSSL)
+ if(OpenSSL_FOUND)
+ if(NOT DEFINED OPENSSL_LIB_PATH)
+ get_filename_component(OPENSSL_LIB_PATH ${OPENSSL_CRYPTO_LIBRARY} DIRECTORY)
+ endif()
+ set_target_properties(${TARGET} PROPERTIES
+ BUILD_WITH_INSTALL_RPATH TRUE
+ INSTALL_RPATH ${OPENSSL_LIB_PATH}
+ )
+ endif()
+endif()
diff --git a/pom.xml b/pom.xml
index c1b8a1c6..dd7c0c2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -139,6 +139,10 @@
${tests}
+
+ ${project.basedir}/src/test/resources/builtin-server.jks
+ password
+
@@ -175,13 +179,12 @@
- com.googlecode.cmake-maven-project
+ io.github.cmake-maven-plugin
cmake-maven-plugin
- 3.19.2-b1
+ 4.0.3-b1
Unix Makefiles
${cmake.source.directory}
- ${cmake.binary.directory}
${cmake.binary.directory}
diff --git a/src/main/java/ru/rt/restream/reindexer/ReindexerConfiguration.java b/src/main/java/ru/rt/restream/reindexer/ReindexerConfiguration.java
index a74ab593..fe9b2258 100644
--- a/src/main/java/ru/rt/restream/reindexer/ReindexerConfiguration.java
+++ b/src/main/java/ru/rt/restream/reindexer/ReindexerConfiguration.java
@@ -33,6 +33,8 @@
import java.util.Objects;
import java.util.function.Consumer;
+import javax.net.ssl.SSLSocketFactory;
+
/**
* Represents approach for bootstrapping Reindexer.
*/
@@ -54,6 +56,8 @@ public final class ReindexerConfiguration {
private String serverConfigFile = "default-builtin-server-config.yml";
+ private SSLSocketFactory sslSocketFactory;
+
private ReindexerConfiguration() {
}
@@ -161,6 +165,17 @@ public ReindexerConfiguration serverConfigFile(String serverConfigFile) {
return this;
}
+ /**
+ * Configure an {@link SSLSocketFactory}.
+ *
+ * @param sslSocketFactory the {@link SSLSocketFactory} to use
+ * @return the {@link ReindexerConfiguration} for further customizations
+ */
+ public ReindexerConfiguration sslSocketFactory(SSLSocketFactory sslSocketFactory) {
+ this.sslSocketFactory = sslSocketFactory;
+ return this;
+ }
+
/**
* Build and return reindexer connector instance.
*
@@ -186,10 +201,15 @@ public Reindexer getReindexer() {
private Binding getBinding(String protocol, List uris) {
switch (protocol) {
+ case "cprotos":
+ if (sslSocketFactory == null) {
+ sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ }
case "cproto":
DataSourceConfiguration dataSourceConfig = DataSourceConfiguration.builder()
.urls(urls)
.allowUnlistedDataSource(allowUnlistedDataSource)
+ .sslSocketFactory(sslSocketFactory)
.build();
return new Cproto(dataSourceFactory, dataSourceConfig, connectionPoolSize, requestTimeout);
case "builtin":
@@ -197,7 +217,7 @@ private Binding getBinding(String protocol, List uris) {
case "builtinserver":
return new BuiltinServer(uris.get(0), serverConfigFile, serverStartupTimeout, requestTimeout);
default:
- throw new UnimplementedException("Protocol: '" + protocol + "' is not suppored");
+ throw new UnimplementedException("Protocol: '" + protocol + "' is not supported");
}
}
diff --git a/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceConfiguration.java b/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceConfiguration.java
index 443a5b81..a0e9ad01 100644
--- a/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceConfiguration.java
+++ b/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceConfiguration.java
@@ -22,6 +22,8 @@
import java.util.List;
import java.util.Objects;
+import javax.net.ssl.SSLSocketFactory;
+
/**
* A {@link DataSource} configuration.
*/
@@ -37,6 +39,11 @@ public class DataSourceConfiguration {
*/
private final boolean allowUnlistedDataSource;
+ /**
+ * An {@link SSLSocketFactory} to connect to Reindexer using cprotos (SSL/TLS) protocol.
+ */
+ private final SSLSocketFactory sslSocketFactory;
+
/**
* An index of the current active data source.
*/
@@ -46,6 +53,7 @@ private DataSourceConfiguration(Builder builder) {
urls = builder.urls;
allowUnlistedDataSource = builder.allowUnlistedDataSource;
active = builder.active;
+ sslSocketFactory = builder.sslSocketFactory;
}
public static Builder builder() {
@@ -69,6 +77,15 @@ public boolean isAllowUnlistedDataSource() {
return allowUnlistedDataSource;
}
+ /**
+ * Returns an {@link SSLSocketFactory} to connect to Reindexer using cprotos (SSL/TLS) protocol.
+ *
+ * @return the {@link SSLSocketFactory} to use
+ */
+ public SSLSocketFactory getSslSocketFactory() {
+ return sslSocketFactory;
+ }
+
/**
* Returns the index of the current active data source.
*
@@ -102,6 +119,11 @@ public static class Builder {
*/
private boolean allowUnlistedDataSource = true;
+ /**
+ * An {@link SSLSocketFactory} to connect to Reindexer using cprotos (SSL/TLS) protocol.
+ */
+ private SSLSocketFactory sslSocketFactory;
+
/**
* An index of the current active data source.
*/
@@ -157,6 +179,17 @@ public Builder allowUnlistedDataSource(boolean allowUnlistedDataSource) {
return this;
}
+ /**
+ * Configure an {@link SSLSocketFactory} to connect to Reindexer using cprotos (TLS) protocol.
+ *
+ * @param sslSocketFactory the {@link SSLSocketFactory} to use
+ * @return the {@link Builder} for further customizations
+ */
+ public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
+ this.sslSocketFactory = sslSocketFactory;
+ return this;
+ }
+
/**
* Build and return a {@link DataSource} configuration.
*
diff --git a/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceFactoryStrategy.java b/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceFactoryStrategy.java
index 201268ab..e8032c28 100644
--- a/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceFactoryStrategy.java
+++ b/src/main/java/ru/rt/restream/reindexer/binding/cproto/DataSourceFactoryStrategy.java
@@ -43,7 +43,7 @@ public enum DataSourceFactoryStrategy implements DataSourceFactory {
public DataSource getDataSource(DataSourceConfiguration configuration) {
List urls = configuration.getUrls();
configuration.setActive((configuration.getActive() + 1) % urls.size());
- return new PhysicalDataSource(urls.get(configuration.getActive()));
+ return new PhysicalDataSource(urls.get(configuration.getActive()), configuration.getSslSocketFactory());
}
},
@@ -55,7 +55,7 @@ public DataSource getDataSource(DataSourceConfiguration configuration) {
public DataSource getDataSource(DataSourceConfiguration configuration) {
List urls = configuration.getUrls();
configuration.setActive(ThreadLocalRandom.current().nextInt(urls.size()));
- return new PhysicalDataSource(urls.get(configuration.getActive()));
+ return new PhysicalDataSource(urls.get(configuration.getActive()), configuration.getSslSocketFactory());
}
},
diff --git a/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalConnection.java b/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalConnection.java
index 048a232b..966650fd 100644
--- a/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalConnection.java
+++ b/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalConnection.java
@@ -46,6 +46,9 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
import static ru.rt.restream.reindexer.binding.Consts.APP_PROPERTY_NAME;
import static ru.rt.restream.reindexer.binding.Consts.BINDING_CAPABILITY_COMPLEX_RANK;
import static ru.rt.restream.reindexer.binding.Consts.BINDING_CAPABILITY_NAMESPACE_INCARNATIONS;
@@ -106,9 +109,18 @@ public class PhysicalConnection implements Connection {
private final ScheduledFuture> writeTaskFuture;
public PhysicalConnection(String host, int port, String user, String password, String database,
+ SSLSocketFactory sslSocketFactory,
Duration requestTimeout, ScheduledExecutorService scheduler) {
try {
- clientSocket = new Socket(host, port);
+ if (sslSocketFactory != null) {
+ LOGGER.debug("rx: using SSL/TLS connection to {}:{}", host, port);
+ SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port);
+ // Fail fast if SSL/TLS handshake fails.
+ sslSocket.startHandshake();
+ clientSocket = sslSocket;
+ } else {
+ clientSocket = new Socket(host, port);
+ }
output = new DataOutputStream(clientSocket.getOutputStream());
input = new DataInputStream(clientSocket.getInputStream());
timeout = requestTimeout;
diff --git a/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalDataSource.java b/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalDataSource.java
index 9ed79f7e..4ac34558 100644
--- a/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalDataSource.java
+++ b/src/main/java/ru/rt/restream/reindexer/binding/cproto/PhysicalDataSource.java
@@ -20,6 +20,8 @@
import java.time.Duration;
import java.util.concurrent.ScheduledThreadPoolExecutor;
+import javax.net.ssl.SSLSocketFactory;
+
/**
* A {@link DataSource} that creates a {@link PhysicalConnection}.
*/
@@ -35,12 +37,27 @@ public class PhysicalDataSource implements DataSource {
private final String database;
+ private final SSLSocketFactory sslSocketFactory;
+
/**
* Creates an instance.
*
* @param url the URL to use
+ * @deprecated Use {@link #PhysicalDataSource(String, SSLSocketFactory)}
+ * to connect to Reindexer using cprotos (SSL/TLS) protocol.
*/
+ @Deprecated
public PhysicalDataSource(String url) {
+ this(url, null);
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * @param url the URL to use
+ * @param sslSocketFactory the {@link SSLSocketFactory} socket factory to use
+ */
+ public PhysicalDataSource(String url, SSLSocketFactory sslSocketFactory) {
URI uri = URI.create(url);
host = uri.getHost();
port = uri.getPort();
@@ -58,11 +75,12 @@ public PhysicalDataSource(String url) {
password = "";
}
database = uri.getPath().substring(1);
+ this.sslSocketFactory = sslSocketFactory;
}
@Override
public Connection getConnection(Duration timeout, ScheduledThreadPoolExecutor scheduler) {
- return new PhysicalConnection(host, port, user, password, database, timeout, scheduler);
+ return new PhysicalConnection(host, port, user, password, database, sslSocketFactory, timeout, scheduler);
}
@Override
diff --git a/src/test/java/ru/rt/restream/reindexer/connector/CprotosReindexerTest.java b/src/test/java/ru/rt/restream/reindexer/connector/CprotosReindexerTest.java
new file mode 100644
index 00000000..c3c00dbd
--- /dev/null
+++ b/src/test/java/ru/rt/restream/reindexer/connector/CprotosReindexerTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Restream
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.rt.restream.reindexer.connector;
+
+import org.junit.jupiter.api.BeforeAll;
+import ru.rt.restream.category.CprotoTest;
+import ru.rt.restream.reindexer.db.DbLocator;
+import ru.rt.restream.reindexer.db.DbLocator.Type;
+
+/**
+ * Tests for Cprotos (SSL/TLS) protocol implementation.
+ */
+@CprotoTest
+public class CprotosReindexerTest extends ReindexerTest {
+
+ @BeforeAll
+ @Override
+ protected void initDb() {
+ db = DbLocator.getDb(Type.CPROTOS);
+ }
+
+}
diff --git a/src/test/java/ru/rt/restream/reindexer/db/DbBaseTest.java b/src/test/java/ru/rt/restream/reindexer/db/DbBaseTest.java
index cca1b0f3..12f5dcfe 100644
--- a/src/test/java/ru/rt/restream/reindexer/db/DbBaseTest.java
+++ b/src/test/java/ru/rt/restream/reindexer/db/DbBaseTest.java
@@ -41,7 +41,7 @@ public abstract class DbBaseTest {
* Initializes the Reindexer instance before all tests.
*/
@BeforeAll
- void initDb() {
+ protected void initDb() {
if (this.getClass().isAnnotationPresent(CprotoTest.class)) {
db = DbLocator.getDb(CPROTO);
} else if (this.getClass().isAnnotationPresent(BuiltinTest.class)) {
diff --git a/src/test/java/ru/rt/restream/reindexer/db/DbLocator.java b/src/test/java/ru/rt/restream/reindexer/db/DbLocator.java
index 9f07bf50..2bbeae39 100644
--- a/src/test/java/ru/rt/restream/reindexer/db/DbLocator.java
+++ b/src/test/java/ru/rt/restream/reindexer/db/DbLocator.java
@@ -23,6 +23,12 @@
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.security.KeyStore;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -30,6 +36,10 @@
import java.util.List;
import java.util.Map;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
/**
* Creates a Reindexer instances for tests.
* This class is not thread safe.
@@ -39,7 +49,7 @@ public class DbLocator {
private static final String BUILTIN_DB_PATH = "/tmp/reindex/builtin_test";
// if change the path, need to synchronized it with path in default-builtin-server-config.yml
- private static final String BUILTINSERVER_DB_PATH = "/tmp/reindex/server";
+ private static final String BUILTINSERVER_DB_PATH = "/tmp/reindex";
private static final Map instancesForUse = new HashMap<>();
@@ -50,6 +60,20 @@ public class DbLocator {
*/
private static final String CPROTO_DSNS_PROPERTY = "CprotoDsns";
+ private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+
+ // A certificate that is valid for 10 years, used to test CPROTOS (SSL/TLS) connection.
+ private static final String SSL_CERT_PATH = "builtin-server.crt";
+
+ private static final String SSL_CERT_KEY_PATH = "builtin-server.key";
+
+ private static final String TRUST_STORE_PATH = "builtin-server.jks";
+
+ private static final String TRUST_STORE_PASSWORD = "password";
+
+ // Ensures that the builtin server is started only once.
+ private static boolean serverStarted = false;
+
public static ClearDbReindexer getDb(Type type) {
ClearDbReindexer db = instancesForUse.get(type);
if (db == null) {
@@ -71,6 +95,7 @@ static void closeAllDbInstances() throws IOException {
}
instancesForClose.clear();
instancesForUse.clear();
+ serverStarted = false;
}
private static ClearDbReindexer addDbInstance(Type type) {
@@ -82,15 +107,17 @@ private static ClearDbReindexer addDbInstance(Type type) {
instancesForUse.put(Type.BUILTIN, builtinDb);
instancesForClose.put(builtinDb, BUILTIN_DB_PATH);
return builtinDb;
+ case CPROTOS:
case CPROTO:
ReindexerConfiguration cprotoConfig = ReindexerConfiguration.builder()
.connectionPoolSize(4)
+ .sslSocketFactory(getSslSocketFactory(type))
.requestTimeout(Duration.ofSeconds(30L));
List urls = getCprotoDbUrlsFromProperty();
if (urls.isEmpty()) {
// run builtinserver, not for own use, only for use in cproto datasource
- String builtinServerCprotoUrl = runDefaultBuiltinServerDbInstance();
+ String builtinServerCprotoUrl = runDefaultBuiltinServerDbInstance(type);
urls.add(builtinServerCprotoUrl);
}
urls.forEach(cprotoConfig::url);
@@ -104,6 +131,33 @@ private static ClearDbReindexer addDbInstance(Type type) {
}
}
+ private static SSLSocketFactory getSslSocketFactory(Type type) {
+ // Return null if protocol is not cprotos.
+ if (type != Type.CPROTOS) {
+ return null;
+ }
+ // Return null if trust store is specified then default SSLSocketFactory is used.
+ String trustStore = System.getProperty(TRUST_STORE_PROPERTY, null);
+ if (trustStore != null) {
+ return null;
+ }
+ // Load KeyStore from the classpath and build SSLSocketFactory.
+ try (InputStream is = DbLocator.class.getClassLoader().getResourceAsStream(TRUST_STORE_PATH)) {
+ if (is == null) {
+ throw new IllegalArgumentException(TRUST_STORE_PATH + " not found in classpath");
+ }
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(is, TRUST_STORE_PASSWORD.toCharArray());
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(keyStore);
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, tmf.getTrustManagers(), null);
+ return sslContext.getSocketFactory();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static List getCprotoDbUrlsFromProperty() {
List urls = new ArrayList<>();
@@ -111,22 +165,46 @@ private static List getCprotoDbUrlsFromProperty() {
if (cprotoUrls != null) {
Arrays.stream(cprotoUrls.split(","))
.filter(s -> !s.isEmpty())
- .filter(s -> s.startsWith("cproto://"))
+ .filter(s -> s.startsWith("cproto://") || s.startsWith("cprotos://"))
.forEach(urls::add);
}
return urls;
}
- private static String runDefaultBuiltinServerDbInstance() {
- ClearDbReindexer server = new ClearDbReindexer(ReindexerConfiguration.builder()
- .url("builtinserver://items")
- .getReindexer().getBinding());
- instancesForClose.put(server, BUILTINSERVER_DB_PATH);
+ private static String runDefaultBuiltinServerDbInstance(Type type) {
+ if (!serverStarted) {
+ // Copy the SSL certificate and key to the Reindexer directory.
+ copyResourceToReindexerDirectory(SSL_CERT_PATH);
+ copyResourceToReindexerDirectory(SSL_CERT_KEY_PATH);
+ ClearDbReindexer server = new ClearDbReindexer(ReindexerConfiguration.builder()
+ .url("builtinserver://items")
+ .getReindexer().getBinding());
+ instancesForClose.put(server, BUILTINSERVER_DB_PATH);
+ serverStarted = true;
+ }
+ if (type == Type.CPROTOS) {
+ return "cprotos://localhost:6535/items";
+ }
return "cproto://localhost:6534/items";
}
+ private static void copyResourceToReindexerDirectory(String fileName) {
+ try (InputStream is = DbLocator.class.getClassLoader().getResourceAsStream(fileName)) {
+ if (is == null) {
+ throw new IllegalArgumentException(fileName + " not found in classpath");
+ }
+ Path reindexerDir = Paths.get(BUILTINSERVER_DB_PATH);
+ Files.createDirectories(reindexerDir);
+ Path target = reindexerDir.resolve(fileName);
+ Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public enum Type {
BUILTIN,
+ CPROTOS,
CPROTO
}
diff --git a/src/test/resources/builtin-server.crt b/src/test/resources/builtin-server.crt
new file mode 100644
index 00000000..32e5fc42
--- /dev/null
+++ b/src/test/resources/builtin-server.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3zCCAsegAwIBAgIUdXlVASWl+7dSBtvACMVSeI2Jq4gwDQYJKoZIhvcNAQEL
+BQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcM
+CVZhbmNvdXZlcjEmMCQGA1UECgwdUmVpbmRleGVyIEJ1aWx0aW4gU2VydmVyIFRl
+c3QxCzAJBgNVBAsMAklUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjUwOTE2MDE1
+ODA2WhcNMzUwOTE0MDE1ODA2WjB/MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz
+aGluZ3RvbjESMBAGA1UEBwwJVmFuY291dmVyMSYwJAYDVQQKDB1SZWluZGV4ZXIg
+QnVpbHRpbiBTZXJ2ZXIgVGVzdDELMAkGA1UECwwCSVQxEjAQBgNVBAMMCWxvY2Fs
+aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhHgrcr+CEuLcPF
+PnLMKuMi0UDeXnI0fihc4O4z+o9NuY+bkGjJX+K4PRZ93KC5a3pxyhTchdgGJv2w
+E4L/VUDsBnTyXDAuNc6aWYdasDHxX7TBRKcV0qAPuNwNg3CvfAnxEt3uj4RznAQc
+mcCECDL/AYVp/PRQrMsJ/iVpydsxgksSFWLR3hSw9FhgKH5FXfp7aZpd53uSbVla
+BLmGsH95F5+f52mDs+LvqZfoXHtjaLVNVRVfpsQ9iJVwIm3FyYu0txRS3NBCwiaX
+emsQiptsd0FkQAXcgd/UyWHuvJ3irHdUSW2Ts0RQkk0ZCtuLJAflnzwC6IqxnjNj
+GMVuptECAwEAAaNTMFEwHQYDVR0OBBYEFHGCNQNJa2BzlaAxnevlFWUxDe/BMB8G
+A1UdIwQYMBaAFHGCNQNJa2BzlaAxnevlFWUxDe/BMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQELBQADggEBAF4nGGKdun4ei0knEytoF+cJCvcmLZ2nlB7ZJu0q
+f+CexNElX7SZxVpYEBkYStFjDqZ/vZD6K2PXk6VsqfyD8grlrWOuaFRWRt7G2+TF
+9XWfespyXNqAJUIl9fozDYItfQB4PL4qbkR9ip2PPt0+XreGvmMAtL/1tpGgt49z
+ReWQpwGEZgagyMnIsLCg9Gin2s2eu6DKXRoufC7cZ7Fai3ipBUCRuZIZd7ZwLVpY
+ZTzdfgATOEsJ3xqYHZRSQFLafARh0VKctBHs8ZxGLCZ3v4pSkpF137kZBtJjwvyd
+uO+M0BDkDtHWpwQfUijnspHY6SxvaYgHuksu1rIurQPcyJQ=
+-----END CERTIFICATE-----
diff --git a/src/test/resources/builtin-server.jks b/src/test/resources/builtin-server.jks
new file mode 100644
index 00000000..e3b88698
Binary files /dev/null and b/src/test/resources/builtin-server.jks differ
diff --git a/src/test/resources/builtin-server.key b/src/test/resources/builtin-server.key
new file mode 100644
index 00000000..652993ee
--- /dev/null
+++ b/src/test/resources/builtin-server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIR4K3K/ghLi3D
+xT5yzCrjItFA3l5yNH4oXODuM/qPTbmPm5BoyV/iuD0WfdyguWt6ccoU3IXYBib9
+sBOC/1VA7AZ08lwwLjXOmlmHWrAx8V+0wUSnFdKgD7jcDYNwr3wJ8RLd7o+Ec5wE
+HJnAhAgy/wGFafz0UKzLCf4lacnbMYJLEhVi0d4UsPRYYCh+RV36e2maXed7km1Z
+WgS5hrB/eRefn+dpg7Pi76mX6Fx7Y2i1TVUVX6bEPYiVcCJtxcmLtLcUUtzQQsIm
+l3prEIqbbHdBZEAF3IHf1Mlh7ryd4qx3VEltk7NEUJJNGQrbiyQH5Z88AuiKsZ4z
+YxjFbqbRAgMBAAECggEAAuI5tPi4ecTUEpyrT6SWLP9ON0KEZl1QYDUnJXLg5ZGT
+TYiqQYH9xBJOSd290KAe1qxfAiIjOLcs9kvj2gfiYr0LQkGPl3+A1ZTWG2twPEXb
+EIuUWRoItnaHfT6iEPwr22pJ0OF7lkcCXPJK+fFEu3Q9wb2jD2L+e+1sx/3gEsx7
+Cmom5LVJy7N0ehUONRmg9iISqXoomyFw80L/HSE20gsUgyYfytPaR+hRJ+eEvBDc
+oFtMIjMyTJaSFwkPaSwXC7aFch8ZVNRrLluBcSwMKxr5WA4xNiJuZLVaS4OyCi5R
+gAtvjdum/3EXgJa4/s6Uao9lj8vcbyDDu1GbZVpuaQKBgQDs87+UFu7J9zLEIq3F
+vU79NgvrmJd1nyY0p1SgKOEB8PR7yVvHBXFIeXw+e/RVaeZNdMJgu/m0ur9oblDg
+IxRRAea/diHITevMDD8P2rujFbbg6BJ41juadZuvtWYN2XJxaGiofSP+M+4Bs3ei
+0CaZ36138mjo8cZUI5sXdvBCSQKBgQDYYRIOk7JhVdH4UCOj7IQCwrsJEMdn2K39
+BgBFoxuj+WamSZjR90+8j18fOO81VcLG41+4OW59z+DCsnVxgHF0QEQBRyl2dzd0
+UMHM9iM7plyVJ2znJN+qxtZQej7O4aQZuevFe/YSKQO6kVzhvu9qaWF1eDegLC2O
+m9AV743ASQKBgCq7bqJbXGcJ8JrUkjgmAtfBZnbvhukMcYxvXOChXqrF9KFaV/XV
+cm+akSCHpDG/LmxrkR525/qC7z/7HRS+3swZJq8Cs+b2vn6G+tx2Gv63TMrUr3gh
+UpBxg0k10pjOzUfMh4tTPil63gJqhjnThhKb9yG5ktfnule0MCUUKxIZAoGBAKd4
+2iT2I8Y6Sj1KqYsicDpCxciKeAvkJSYFEwjS6X3MdqqVo/Y+2DQ1M4ZS5rH9IrgV
+G+zWZrJxxqBqx4OFykoTw47JqUnLk9wu9RDSIQr2BApdbWipnYPsScwYEazF7+EJ
+iXZcL2R24i/welcvFNbjoU7uMP+Dse8CodeYhbohAoGARplGnV4tH/u7zIBoy8GG
+wj6N/L+MQTdxWmb1g7jzGP0LiOr3Ah3yd2LRqX4JMGxsFBeMmqjJbKl3sMiKDDFI
+rOAOsDkaUjLOf3Fbx1vlDmC1AzrTCXuwEYgUOhUbj0Y9k0ir2TuGxw+p2lzPnE9y
+YUfmvAt+4CIsux1Sr9vPpDk=
+-----END PRIVATE KEY-----
diff --git a/src/test/resources/default-builtin-server-config.yml b/src/test/resources/default-builtin-server-config.yml
index eaab68f4..5d9c2360 100644
--- a/src/test/resources/default-builtin-server-config.yml
+++ b/src/test/resources/default-builtin-server-config.yml
@@ -7,6 +7,8 @@ net:
httpaddr: "0.0.0.0:9088"
rpcaddr: "0.0.0.0:6534"
security: false
+ ssl_cert: /tmp/reindex/builtin-server.crt
+ ssl_key: /tmp/reindex/builtin-server.key
logger:
serverlog: "stdout"
corelog: "stdout"