diff --git a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java index a8b49e031d..6a2a1316d6 100644 --- a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java +++ b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java @@ -414,8 +414,16 @@ public class SegmentTarFixture extends OakFixture { .withBlobChunkSize(1 * MB) .withSecureConnection(secure) .build(); - clientSyncs[i] = new StandbyClientSync("127.0.0.1", port, stores[n + i], secure, DEFAULT_TIMEOUT, false, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())); - + clientSyncs[i] = StandbyClientSync.builder() + .withHost("127.0.0.1") + .withPort(port) + .withFileStore(stores[n + 1]) + .withSecureConnection(secure) + .withReadTimeoutMs(DEFAULT_TIMEOUT) + .withAutoClean(false) + .withSpoolFolder(new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())) + .build(); + if (!oneShotRun) { serverSyncs[i].start(); clientSyncs[i].start(); diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java index 9073cd6a03..2105df0d02 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java @@ -23,8 +23,6 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import javax.net.ssl.SSLException; - import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; @@ -54,12 +52,87 @@ import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequestEncoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponse; import org.apache.jackrabbit.oak.segment.standby.codec.ResponseDecoder; +import org.apache.jackrabbit.oak.segment.standby.netty.SSLSubjectMatcher; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class StandbyClient implements AutoCloseable { + public static Builder builder() { + return new Builder(); + } + + static class Builder { + + private String host; + private int port; + private NioEventLoopGroup group; + private String clientId; + private boolean secure; + private int readTimeoutMs; + private File spoolFolder; + private String sslKeyFile; + private String sslChainFile; + public String sslServerSubjectPattern; + + private Builder() {} + + public Builder withHost(String host) { + this.host = host; + return this; + } + + public Builder withPort(int port) { + this.port = port; + return this; + } + + public Builder withGroup(NioEventLoopGroup group) { + this.group = group; + return this; + } + + public Builder withClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Builder withSecure(boolean secure) { + this.secure = secure; + return this; + } + + public Builder withReadTimeoutMs(int readTimeoutMs) { + this.readTimeoutMs = readTimeoutMs; + return this; + } + + public Builder withSpoolFolder(File spoolFolder) { + this.spoolFolder = spoolFolder; + return this; + } + + public Builder withSSLKeyFile(String sslKeyFile) { + this.sslKeyFile = sslKeyFile; + return this; + } + + public Builder withSSLChainFile(String sslChainFile) { + this.sslChainFile = sslChainFile; + return this; + } + + public Builder withSSLServerSubjectPattern(String sslServerSubjectPattern) { + this.sslServerSubjectPattern = sslServerSubjectPattern; + return this; + } + + public StandbyClient build() throws InterruptedException { + return new StandbyClient(this); + } + } + private static final Logger log = LoggerFactory.getLogger(StandbyClient.class); private final BlockingQueue headQueue = new LinkedBlockingDeque<>(); @@ -76,12 +149,12 @@ class StandbyClient implements AutoCloseable { private Channel channel; - StandbyClient(String host, int port, NioEventLoopGroup group, String clientId, boolean secure, int readTimeoutMs, File spoolFolder) throws InterruptedException { - this.clientId = clientId; - this.readTimeoutMs = readTimeoutMs; + StandbyClient(Builder builder) throws InterruptedException { + this.clientId = builder.clientId; + this.readTimeoutMs = builder.readTimeoutMs; Bootstrap b = new Bootstrap() - .group(group) + .group(builder.group) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, readTimeoutMs) .option(ChannelOption.TCP_NODELAY, true) @@ -93,8 +166,18 @@ class StandbyClient implements AutoCloseable { public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); - if (secure) { - p.addLast(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build().newHandler(ch.alloc())); + if (builder.secure) { + SslContext sslContext; + if (builder.sslKeyFile != null && !"".equals(builder.sslKeyFile)) { + sslContext = SslContextBuilder.forClient().keyManager(new File(builder.sslChainFile), new File(builder.sslKeyFile)).build(); + } else { + sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + } + p.addLast("ssl", sslContext.newHandler(ch.alloc())); + + if (builder.sslServerSubjectPattern != null) { + p.addLast(new SSLSubjectMatcher(builder.sslServerSubjectPattern)); + } } p.addLast(new ReadTimeoutHandler(readTimeoutMs, TimeUnit.MILLISECONDS)); @@ -106,7 +189,7 @@ class StandbyClient implements AutoCloseable { // The frame length limits the chunk size to max. 2.2GB p.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4)); - p.addLast(new ResponseDecoder(spoolFolder)); + p.addLast(new ResponseDecoder(builder.spoolFolder)); // Encoders @@ -130,7 +213,7 @@ class StandbyClient implements AutoCloseable { }); - channel = b.connect(host, port).sync().channel(); + channel = b.connect(builder.host, builder.port).sync().channel(); } @Override diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java index a5b0901013..6af99db075 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java @@ -45,6 +45,80 @@ import org.slf4j.LoggerFactory; public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnable, Closeable { + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String host; + private int port; + private FileStore fileStore; + private boolean secure; + private int readTimeoutMs; + private boolean autoClean; + private File spoolFolder; + private String sslKeyFile; + private String sslChainFile; + private String sslServerSubjectPattern; + + private Builder() {} + + public Builder withHost(String host) { + this.host = host; + return this; + } + + public Builder withPort(int port) { + this.port = port; + return this; + } + + public Builder withFileStore(FileStore fileStore) { + this.fileStore = fileStore; + return this; + } + + public Builder withSecureConnection(boolean secure) { + this.secure = secure; + return this; + } + + public Builder withReadTimeoutMs(int readTimeoutMs) { + this.readTimeoutMs = readTimeoutMs; + return this; + } + + public Builder withAutoClean(boolean autoClean) { + this.autoClean = autoClean; + return this; + } + + public Builder withSpoolFolder(File spoolFolder) { + this.spoolFolder = spoolFolder; + return this; + } + + public Builder withSSLKeyFile(String sslKeyFile) { + this.sslKeyFile = sslKeyFile; + return this; + } + + public Builder withSSLChainFile(String sslChainFile) { + this.sslChainFile = sslChainFile; + return this; + } + + public Builder withSSLServerSubjectPattern(String sslServerSubjectPattern) { + this.sslServerSubjectPattern = sslServerSubjectPattern; + return this; + } + + public StandbyClientSync build() { + return new StandbyClientSync(this); + } + } + public static final String CLIENT_ID_PROPERTY_NAME = "standbyID"; private static final Logger log = LoggerFactory.getLogger(StandbyClientSync.class); @@ -73,6 +147,12 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab private final AtomicBoolean active = new AtomicBoolean(false); + private final String sslKeyFile; + + private final String sslChainFile; + + private final String sslServerSubjectPattern; + private int failedRequests; private long lastSuccessfulRequest; @@ -95,22 +175,25 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab return s; } - public StandbyClientSync(String host, int port, FileStore store, boolean secure, int readTimeoutMs, boolean autoClean, File spoolFolder) { + private StandbyClientSync(Builder builder) { this.state = STATUS_INITIALIZING; this.lastSuccessfulRequest = -1; this.syncStartTimestamp = -1; this.syncEndTimestamp = -1; this.failedRequests = 0; - this.host = host; - this.port = port; - this.secure = secure; - this.readTimeoutMs = readTimeoutMs; - this.autoClean = autoClean; - this.fileStore = store; + this.host = builder.host; + this.port = builder.port; + this.secure = builder.secure; + this.readTimeoutMs = builder.readTimeoutMs; + this.autoClean = builder.autoClean; + this.fileStore = builder.fileStore; this.observer = new CommunicationObserver(clientId()); this.group = new NioEventLoopGroup(0, new NamedThreadFactory("standby")); this.execution = new StandbyClientSyncExecution(fileStore, () -> running); - this.spoolFolder = spoolFolder; + this.spoolFolder = builder.spoolFolder; + this.sslKeyFile = builder.sslKeyFile; + this.sslChainFile = builder.sslChainFile; + this.sslServerSubjectPattern = builder.sslServerSubjectPattern; try { ManagementFactory.getPlatformMBeanServer().registerMBean(new StandardMBean(this, ClientStandbyStatusMBean.class), new ObjectName(this.getMBeanName())); } catch (Exception e) { @@ -161,7 +244,17 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab GCGeneration genBefore = headGeneration(fileStore); - try (StandbyClient client = new StandbyClient(host, port, group, observer.getID(), secure, readTimeoutMs, spoolFolder)) { + try (StandbyClient client = StandbyClient.builder() + .withHost(host) + .withPort(port) + .withGroup(group) + .withClientId(observer.getID()) + .withSecure(secure) + .withReadTimeoutMs(readTimeoutMs) + .withSpoolFolder(spoolFolder) + .withSSLKeyFile(sslKeyFile) + .withSSLChainFile(sslChainFile) + .withSSLServerSubjectPattern(sslServerSubjectPattern).build()) { execution.execute(client); } diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java new file mode 100644 index 0000000000..cf67973b3f --- /dev/null +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jackrabbit.oak.segment.standby.netty; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.ssl.SslHandler; +import io.netty.handler.ssl.SslHandshakeCompletionEvent; + +import javax.net.ssl.SSLSession; +import javax.security.cert.X509Certificate; +import java.security.Principal; +import java.util.regex.Pattern; + +public class SSLSubjectMatcher extends ChannelInboundHandlerAdapter { + + private final Pattern pattern; + + public SSLSubjectMatcher(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + @Override + public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object ev) throws Exception { + if (ev instanceof SslHandshakeCompletionEvent) { + final Channel channel = channelHandlerContext.channel(); + final ChannelPipeline pipeline = channel.pipeline(); + final SslHandler ssl = (SslHandler) pipeline.get("ssl"); + final SSLSession session = ssl.engine().getSession(); + final X509Certificate x509Certificate = session.getPeerCertificateChain()[0]; + final Principal subjectDN = x509Certificate.getSubjectDN(); + final String subject = subjectDN.getName(); + if (!pattern.matcher(subject).matches()) { + throw new Exception( + "Pattern match /" + pattern.toString() + "/ failed on: " + subject + "\n" + + "Note: the Java implementation of regex always implicitly matches from beginning to\n" + + "end, as in ^YOUR_PATTERN$, so if you want to match /acme/ anywhere in the subject\n" + + "you'd use .*acme.*"); + } + } + } +} diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java index 6935ca71f8..bac60395c5 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java @@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.segment.standby.server; import static com.google.common.base.Preconditions.checkState; +import java.io.File; import java.security.cert.CertificateException; import java.util.concurrent.TimeUnit; @@ -40,6 +41,7 @@ import io.netty.handler.codec.compression.SnappyFrameEncoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.util.CharsetUtil; @@ -50,6 +52,7 @@ import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponseEncoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetReferencesResponseEncoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponseEncoder; import org.apache.jackrabbit.oak.segment.standby.codec.RequestDecoder; +import org.apache.jackrabbit.oak.segment.standby.netty.SSLSubjectMatcher; import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,6 +107,14 @@ class StandbyServer implements AutoCloseable { private StandbyBlobReader standbyBlobReader; + private String sslKeyFile; + + private String sslChainFile; + + private boolean sslClientValidation; + + public String sslClientSubjectPattern; + private Builder(final int port, final StoreProvider storeProvider, final int blobChunkSize) { this.port = port; this.storeProvider = storeProvider; @@ -150,6 +161,26 @@ class StandbyServer implements AutoCloseable { return this; } + Builder withSSLKeyFile(String sslKeyFile) { + this.sslKeyFile = sslKeyFile; + return this; + } + + Builder withSSLChainFile(String sslChainFile) { + this.sslChainFile = sslChainFile; + return this; + } + + Builder withSSLClientValidation(boolean sslValidateClient) { + this.sslClientValidation = sslValidateClient; + return this; + } + + Builder withSSLClientSubjectPattern(String sslClientSubjectPattern) { + this.sslClientSubjectPattern = sslClientSubjectPattern; + return this; + } + StandbyServer build() throws CertificateException, SSLException { checkState(storeProvider != null); @@ -180,8 +211,12 @@ class StandbyServer implements AutoCloseable { this.port = builder.port; if (builder.secure) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + if (builder.sslKeyFile != null && !"".equals(builder.sslKeyFile)) { + sslContext = SslContextBuilder.forServer(new File(builder.sslChainFile), new File(builder.sslKeyFile)).build(); + } else { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + } } bossGroup = new NioEventLoopGroup(1, new NamedThreadFactory("primary-run")); @@ -205,7 +240,13 @@ class StandbyServer implements AutoCloseable { p.addLast(new ClientFilterHandler(new ClientIpFilter(builder.allowedClientIPRanges))); if (sslContext != null) { - p.addLast("ssl", sslContext.newHandler(ch.alloc())); + SslHandler handler = sslContext.newHandler(ch.alloc()); + handler.engine().setNeedClientAuth(builder.sslClientValidation); + p.addLast("ssl", handler); + } + + if (builder.sslClientSubjectPattern != null) { + p.addLast(new SSLSubjectMatcher(builder.sslClientSubjectPattern)); } // Decoders diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java index 7d5dd46e7a..bc7988c160 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java @@ -63,6 +63,14 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto private StandbySegmentReader standbySegmentReader; + private String sslKeyFile; + + private String sslChainFile; + + private boolean sslValidateClient; + + public String sslClientSubjectPattern; + private Builder() { // Prevent external instantiation } @@ -119,6 +127,26 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto return this; } + public Builder withSSLKeyFile(String sslKeyFile) { + this.sslKeyFile = sslKeyFile; + return this; + } + + public Builder withSSLChainFile(String sslChainFile) { + this.sslChainFile = sslChainFile; + return this; + } + + public Builder withSSLClientValidation(boolean sslValidateClient) { + this.sslValidateClient = sslValidateClient; + return this; + } + + public Builder withSSLClientSubjectPattern(String sslClientSubjectPattern) { + this.sslClientSubjectPattern = sslClientSubjectPattern; + return this; + } + public StandbyServerSync build() { checkArgument(port > 0); checkArgument(fileStore != null); @@ -156,6 +184,14 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto private StandbyServer server; + private final String sslCertificate; + + private final String sslChain; + + private final boolean sslValidateClient; + + private final String sslClientSubjectPattern; + private StandbyServerSync(Builder builder) { this.port = builder.port; this.fileStore = builder.fileStore; @@ -166,6 +202,10 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto this.standbyHeadReader = builder.standbyHeadReader; this.standbyReferencesReader = builder.standbyReferencesReader; this.standbySegmentReader = builder.standbySegmentReader; + this.sslCertificate = builder.sslKeyFile; + this.sslChain = builder.sslChainFile; + this.sslValidateClient = builder.sslValidateClient; + this.sslClientSubjectPattern = builder.sslClientSubjectPattern; this.observer = new CommunicationObserver("primary"); final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); @@ -196,7 +236,7 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto state = STATUS_STARTING; try { - server = StandbyServer.builder(port, this, blobChunkSize) + StandbyServer.Builder builder = StandbyServer.builder(port, this, blobChunkSize) .secure(secure) .allowIPRanges(allowedClientIPRanges) .withStateConsumer(this) @@ -204,8 +244,16 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto .withStandbyBlobReader(standbyBlobReader) .withStandbyHeadReader(standbyHeadReader) .withStandbyReferencesReader(standbyReferencesReader) - .withStandbySegmentReader(standbySegmentReader) - .build(); + .withStandbySegmentReader(standbySegmentReader); + if (secure) { + if (sslCertificate != null && !"".equals(sslCertificate)) { + builder.withSSLKeyFile(sslCertificate) + .withSSLChainFile(sslChain) + .withSSLClientValidation(sslValidateClient) + .withSSLClientSubjectPattern(sslClientSubjectPattern); + } + } + server = builder.build(); server.start(); state = STATUS_RUNNING; diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java index 89fcda6036..95e8284d96 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java @@ -117,6 +117,35 @@ public class StandbyStoreService { ) boolean standby_autoclean() default true; + @AttributeDefinition( + name = "SSL Key File", + description = "The file name which contains the SSL key. If this is empty, a key will be generated on-the-fly." + ) + String sslKeyFile(); + + @AttributeDefinition( + name = "SSL Certificate Chain File", + description = "The file name which contains the SSL certificate chain." + ) + String sslChainFile(); + + @AttributeDefinition( + name = "SSL Validate Client", + description = "Validate the client's SSL certificate." + ) + boolean sslValidateClient() default false; + + @AttributeDefinition( + name = "SSL Server Certificate Subject Pattern", + description = "The server certificate subject must match this pattern in order to be accepted by the client." + ) + String sslServerSubjectPattern(); + + @AttributeDefinition( + name = "SSL Client Certificate Subject Pattern", + description = "The client certificate subject must match this pattern in order to be accepted by the server." + ) + String sslClientSubjectPattern(); } @Reference(policy = STATIC, policyOption = GREEDY) @@ -137,12 +166,12 @@ public class StandbyStoreService { String mode = config.mode(); if (mode.equals("primary")) { - bootstrapMaster(config, fileStore); + bootstrapPrimary(config, fileStore); return; } if (mode.equals("standby")) { - bootstrapSlave(context, config, fileStore); + bootstrapSecondary(context, config, fileStore); return; } @@ -154,18 +183,31 @@ public class StandbyStoreService { closer.close(); } - private void bootstrapMaster(Configuration config, FileStore fileStore) { + private void bootstrapPrimary(Configuration config, FileStore fileStore) { int port = config.port(); String[] ranges = config.primary_allowed$_$client$_$ip$_$ranges(); boolean secure = config.secure(); + String sslKeyFile = config.sslKeyFile(); + String sslChainFile = config.sslChainFile(); + boolean sslValidateClient = config.sslValidateClient(); + String sslClientSubjectPattern = config.sslClientSubjectPattern(); - StandbyServerSync standbyServerSync = StandbyServerSync.builder() + StandbyServerSync.Builder builder = StandbyServerSync.builder() .withPort(port) .withFileStore(fileStore) .withBlobChunkSize(BLOB_CHUNK_SIZE) - .withAllowedClientIPRanges(ranges) - .withSecureConnection(secure) - .build(); + .withAllowedClientIPRanges(ranges); + + if (secure) { + builder.withSecureConnection(true); + if (sslKeyFile != null && !"".equals(sslKeyFile)) { + builder.withSSLKeyFile(sslKeyFile) + .withSSLChainFile(sslChainFile) + .withSSLClientValidation(sslValidateClient) + .withSSLClientSubjectPattern(sslClientSubjectPattern); + } + } + StandbyServerSync standbyServerSync = builder.build(); closer.register(standbyServerSync); standbyServerSync.start(); @@ -173,24 +215,36 @@ public class StandbyStoreService { log.info("Started primary on port {} with allowed IP ranges {}", port, ranges); } - private void bootstrapSlave(ComponentContext context, Configuration config, FileStore fileStore) { - int port = config.port(); - long interval = config.interval(); - String host = config.primary_host(); - boolean secure = config.secure(); - int readTimeout = config.standby_readtimeout(); - boolean clean = config.standby_autoclean(); + private void bootstrapSecondary(ComponentContext context, Configuration config, FileStore fileStore) { - StandbyClientSync standbyClientSync = new StandbyClientSync(host, port, fileStore, secure, readTimeout, clean, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())); + StandbyClientSync.Builder builder = StandbyClientSync.builder() + .withHost(config.primary_host()) + .withPort(config.port()) + .withFileStore(fileStore) + .withSecureConnection(config.secure()) + .withReadTimeoutMs(config.standby_readtimeout()) + .withAutoClean(config.standby_autoclean()) + .withSpoolFolder(new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())) + ; + + if (config.secure()) { + builder + .withSecureConnection(true) + .withSSLKeyFile(config.sslKeyFile()) + .withSSLChainFile(config.sslChainFile()) + .withSSLServerSubjectPattern(config.sslClientSubjectPattern()) + ; + } + StandbyClientSync standbyClientSync = builder.build(); closer.register(standbyClientSync); Dictionary dictionary = new Hashtable(); - dictionary.put("scheduler.period", interval); + dictionary.put("scheduler.period", config.interval()); dictionary.put("scheduler.concurrent", false); ServiceRegistration registration = context.getBundleContext().registerService(Runnable.class.getName(), standbyClientSync, dictionary); closer.register(registration::unregister); - log.info("Started standby on port {} with {}s sync frequency", port, interval); + log.info("Started standby on port {} with {}s sync frequency", config.port(), config.interval()); } } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java index cb7dce2d09..1d90cb5e62 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java @@ -73,7 +73,15 @@ public class BrokenNetworkIT extends TestBase { .withBlobChunkSize(MB) .withSecureConnection(false) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientStore, false, getClientTimeout(), false, folder.newFolder()); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(clientStore) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); clientSync.run(); @@ -98,7 +106,15 @@ public class BrokenNetworkIT extends TestBase { .withBlobChunkSize(MB) .withSecureConnection(true) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder()); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); clientSync.run(); @@ -179,7 +195,15 @@ public class BrokenNetworkIT extends TestBase { try ( NetworkErrorProxy ignored = new NetworkErrorProxy(proxyPort.getPort(), getServerHost(), serverPort.getPort(), flipPosition, skipPosition, skipBytes); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), proxyPort.getPort(), clientStore, ssl, getClientTimeout(), false, spoolFolder) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(proxyPort.getPort()) + .withFileStore(clientStore) + .withSecureConnection(ssl) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() ) { clientSync.run(); } @@ -191,10 +215,19 @@ public class BrokenNetworkIT extends TestBase { serverStore.flush(); } - try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientStore, ssl, getClientTimeout(), false, spoolFolder)) { - clientSync.run(); + try (StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(clientStore) + .withSecureConnection(ssl) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() + ) { + clientSync.run(); } - } + } assertEquals("stores are not equal", serverStore.getHead(), clientStore.getHead()); } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java index 28a845c330..7b4b6e99a5 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java @@ -155,7 +155,15 @@ public abstract class DataStoreTestBase extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 4_000, false, spoolFolder) + StandbyClientSync cl = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(4_000) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() ) { serverSync.start(); // no persisted head on primary @@ -172,7 +180,15 @@ public abstract class DataStoreTestBase extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 4_000, false, spoolFolder) + StandbyClientSync cl = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(4_000) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() ) { serverSync.start(); // this time persisted head will be available on primary @@ -213,7 +229,15 @@ public abstract class DataStoreTestBase extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync cl = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); primary.flush(); @@ -259,7 +283,15 @@ public abstract class DataStoreTestBase extends TestBase { .withFileStore(primary) .withBlobChunkSize(8 * MB) .build(); - StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 2 * 60 * 1000, false, folder.newFolder()) + StandbyClientSync cl = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(2 * 60 * 1000) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); primary.flush(); @@ -302,7 +334,15 @@ public abstract class DataStoreTestBase extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); @@ -376,7 +416,15 @@ public abstract class DataStoreTestBase extends TestBase { try ( NetworkErrorProxy ignored = new NetworkErrorProxy(proxyPort.getPort(), getServerHost(), serverPort.getPort(), flipPosition, skipPosition, skipBytes); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), proxyPort.getPort(), secondary, false, getClientTimeout(), false, spoolFolder) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(proxyPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() ) { clientSync.run(); } @@ -391,7 +439,16 @@ public abstract class DataStoreTestBase extends TestBase { primary.flush(); } - try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, spoolFolder)) { + try (StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(spoolFolder) + .build() + ) { clientSync.run(); } } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java index 3deb5394cc..c50983d1c1 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java @@ -163,7 +163,15 @@ public class FailoverIPRangeIT extends TestBase { .withBlobChunkSize(MB) .withAllowedClientIPRanges(ipRanges) .build(); - StandbyClientSync clientSync = new StandbyClientSync(host, serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(host) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); addTestContent(store, "server"); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java index 0e52b530e4..fc1d373ac0 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java @@ -68,8 +68,24 @@ public class FailoverMultipleClientsTestIT extends TestBase { .withFileStore(storeS) .withBlobChunkSize(MB) .build(); - StandbyClientSync cl1 = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder()); - StandbyClientSync cl2 = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC2, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync cl1 = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build(); + StandbyClientSync cl2 = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC2) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); SegmentTestUtils.addTestContent(store, "server"); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java index f45f4a0927..a13d6792cf 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java @@ -64,7 +64,15 @@ public class FailoverSslTestIT extends TestBase { .withBlobChunkSize(MB) .withSecureConnection(true) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder()); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { assertTrue(synchronizeAndCompareHead(serverSync, clientSync)); } @@ -81,7 +89,15 @@ public class FailoverSslTestIT extends TestBase { .withBlobChunkSize(MB) .withSecureConnection(true) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder()); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { assertFalse(synchronizeAndCompareHead(serverSync, clientSync)); } @@ -97,7 +113,15 @@ public class FailoverSslTestIT extends TestBase { .withFileStore(storeS) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder()); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { assertFalse(synchronizeAndCompareHead(serverSync, clientSync)); } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java index 7a138f149e..0357ce18dc 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java @@ -99,7 +99,16 @@ public class MBeanIT extends TestBase { public void testClientEmptyConfigNoServer() throws Exception { MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); ObjectName status = new ObjectName(StandbyStatusMBean.JMX_NAME + ",id=*"); - try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder())) { + try (StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(clientFileStore.fileStore()) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build(); + ) { clientSync.start(); clientSync.run(); @@ -136,7 +145,16 @@ public class MBeanIT extends TestBase { System.setProperty(StandbyClientSync.CLIENT_ID_PROPERTY_NAME, "Foo"); MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); ObjectName status; - try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder())) { + try (StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(clientFileStore.fileStore()) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build(); + ) { clientSync.start(); clientSync.run(); @@ -164,7 +182,15 @@ public class MBeanIT extends TestBase { .withFileStore(serverFileStore.fileStore()) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(clientFileStore.fileStore()) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); serverFileStore.fileStore().flush(); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java index f760bb7224..d2ced58f87 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java @@ -67,7 +67,15 @@ public class RecoverTestIT extends TestBase { .withFileStore(storeS) .withBlobChunkSize(MB) .build(); - StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync cl = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(storeC) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); store = SegmentNodeStoreBuilders.builder(storeS).build(); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java index 567a6977e3..2fad0ea74b 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java @@ -99,7 +99,15 @@ public class StandbySegmentBlobTestIT extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); addTestContent(store, "server"); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java index 892670b4f1..8908891bfd 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java @@ -21,17 +21,23 @@ package org.apache.jackrabbit.oak.segment.standby; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileOutputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; import java.util.Random; import com.google.common.io.ByteStreams; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.IOUtils; import org.apache.jackrabbit.oak.commons.junit.TemporaryPort; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.file.FileStore; @@ -42,6 +48,7 @@ import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -63,6 +70,9 @@ public class StandbyTestIT extends TestBase { .around(serverFileStore) .around(clientFileStore); + /** + * This test syncs a few segments over an unencrypted connection. + */ @Test public void testSync() throws Exception { int blobSize = 5 * MB; @@ -76,7 +86,68 @@ public class StandbyTestIT extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() + ) { + serverSync.start(); + byte[] data = addTestContent(store, "server", blobSize, 150); + primary.flush(); + + clientSync.run(); + + assertEquals(primary.getHead(), secondary.getHead()); + + assertTrue(primary.getStats().getApproximateSize() > blobSize); + assertTrue(secondary.getStats().getApproximateSize() > blobSize); + + PropertyState ps = secondary.getHead().getChildNode("root") + .getChildNode("server").getProperty("testBlob"); + assertNotNull(ps); + assertEquals(Type.BINARY.tag(), ps.getType().tag()); + Blob b = ps.getValue(Type.BINARY); + assertEquals(blobSize, b.length()); + + byte[] testData = new byte[blobSize]; + ByteStreams.readFully(b.getNewStream(), testData); + assertArrayEquals(data, testData); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * Both server and client certificates are generated on-the-fly. + */ + @Test + @Ignore("This test takes ~4s and is therefore disabled by default") + public void testSyncSSL() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); byte[] data = addTestContent(store, "server", blobSize, 150); @@ -102,6 +173,642 @@ public class StandbyTestIT extends TestBase { } } + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore. + * The server does not validate the client certificate. + * The client creates its certificate on-the-fly. + */ + @Test + @Ignore("This test takes ~2s and is therefore disabled by default") + public void testSyncSSLNoClientValidation() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(false) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() + ) { + serverSync.start(); + byte[] data = addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertEquals(primary.getHead(), secondary.getHead()); + + assertTrue(primary.getStats().getApproximateSize() > blobSize); + assertTrue(secondary.getStats().getApproximateSize() > blobSize); + + PropertyState ps = secondary.getHead().getChildNode("root") + .getChildNode("server").getProperty("testBlob"); + assertNotNull(ps); + assertEquals(Type.BINARY.tag(), ps.getType().tag()); + Blob b = ps.getValue(Type.BINARY); + assertEquals(blobSize, b.length()); + + byte[] testData = new byte[blobSize]; + ByteStreams.readFully(b.getNewStream(), testData); + assertArrayEquals(data, testData); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore. + */ + @Test + @Ignore("This test takes ~2s and is therefore disabled by default") + public void testSyncSSLValidClient() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .build() + ) { + serverSync.start(); + byte[] data = addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertEquals(primary.getHead(), secondary.getHead()); + + assertTrue(primary.getStats().getApproximateSize() > blobSize); + assertTrue(secondary.getStats().getApproximateSize() > blobSize); + + PropertyState ps = secondary.getHead().getChildNode("root") + .getChildNode("server").getProperty("testBlob"); + assertNotNull(ps); + assertEquals(Type.BINARY.tag(), ps.getType().tag()); + Blob b = ps.getValue(Type.BINARY); + assertEquals(blobSize, b.length()); + + byte[] testData = new byte[blobSize]; + ByteStreams.readFully(b.getNewStream(), testData); + assertArrayEquals(data, testData); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore. + * The server validates the client certificate. + * The client has a configured certificate which cannot be validated with the truststore. + * The SSL connection is expected to fail. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLInvalidClient() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, arbitraryKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, arbitraryCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertNotEquals(primary.getHead(), secondary.getHead()); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which cannot be validated with the truststore. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore. + * The SSL connection is expected to fail. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLInvalidServer() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, arbitraryKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, arbitraryCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertNotEquals(primary.getHead(), secondary.getHead()); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore, + * but cannot be validated against the required subject pattern. + * The SSL connection is expected to fail. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLInvalidClientSubject() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .withSSLClientSubjectPattern("foobar") + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertNotEquals(primary.getHead(), secondary.getHead()); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore, + * and can also be validated against the required subject pattern. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLValidClientSubject() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .withSSLClientSubjectPattern(".*.esting.*") + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertEquals(primary.getHead(), secondary.getHead()); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore, + * but cannot be validated against the required subject pattern. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore. + * The SSL connection is expected to fail. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLInvalidServerSubject() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .withSSLServerSubjectPattern("foobar") + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertNotEquals(primary.getHead(), secondary.getHead()); + } + } + + /** + * This test syncs a few segments over an encrypted connection. + * The server has a configured certificate which can be validated with the truststore, + * and can also be validated against the required subject pattern. + * The server validates the client certificate. + * The client has a configured certificate which can be validated with the truststore. + */ + @Test + @Ignore("This test takes ~7s and is therefore disabled by default") + public void testSyncSSLValidServerSubject() throws Exception { + int blobSize = 5 * MB; + FileStore primary = serverFileStore.fileStore(); + FileStore secondary = clientFileStore.fileStore(); + + FileOutputStream fos; + + File serverKeyFile = folder.newFile(); + fos = new FileOutputStream(serverKeyFile); + IOUtils.writeString(fos, serverKey); + fos.close(); + + File clientKeyFile = folder.newFile(); + fos = new FileOutputStream(clientKeyFile); + IOUtils.writeString(fos, clientKey); + fos.close(); + + File serverCertFile = folder.newFile(); + fos = new FileOutputStream(serverCertFile); + IOUtils.writeString(fos, serverCert); + fos.close(); + + File clientCertFile = folder.newFile(); + fos = new FileOutputStream(clientCertFile); + IOUtils.writeString(fos, clientCert); + fos.close(); + + File keyStoreFile = folder.newFile(); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, "changeit".toCharArray()); + Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes())); + keyStore.setCertificateEntry("the-ca-cert", c); + keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray()); + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + + NodeStore store = SegmentNodeStoreBuilders.builder(primary).build(); + try ( + StandbyServerSync serverSync = StandbyServerSync.builder() + .withPort(serverPort.getPort()) + .withFileStore(primary) + .withBlobChunkSize(MB) + .withSecureConnection(true) + .withSSLKeyFile(serverKeyFile.getAbsolutePath()) + .withSSLChainFile(serverCertFile.getAbsolutePath()) + .withSSLClientValidation(true) + .build(); + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(true) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .withSSLKeyFile(clientKeyFile.getAbsolutePath()) + .withSSLChainFile(clientCertFile.getAbsolutePath()) + .withSSLServerSubjectPattern(".*.esting.*") + .build() + ) { + serverSync.start(); + addTestContent(store, "server", blobSize, 1); + primary.flush(); + + clientSync.run(); + + assertEquals(primary.getHead(), secondary.getHead()); + } + } + /** * OAK-2430 */ @@ -120,7 +827,15 @@ public class StandbyTestIT extends TestBase { .withFileStore(primary) .withBlobChunkSize(MB) .build(); - StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder()) + StandbyClientSync clientSync = StandbyClientSync.builder() + .withHost(getServerHost()) + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(getClientTimeout()) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { serverSync.start(); byte[] data = addTestContent(store, "server", blobSize, dataNodes); diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java index 880423f864..7a8370945b 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java @@ -48,4 +48,197 @@ public class TestBase { return timeout; } + // A self-signed certificate representing the "Testing CA" + static final String caCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIIDxjCCAq4CCQDWKVsDO4p3MDANBgkqhkiG9w0BAQUFADCBpDELMAkGA1UEBhMC\n" + + "VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\n" + + "FjAUBgNVBAoMDUNBVGVzdGluZyBDby4xEDAOBgNVBAsMB1Rlc3RpbmcxFjAUBgNV\n" + + "BAMMDWNhdGVzdGluZy5jb20xJjAkBgkqhkiG9w0BCQEWF2NhdGVzdGluZ0BjYXRl\n" + + "c3RpbmcuY29tMB4XDTIxMDUzMTEzNTczOFoXDTQ4MTAxNTEzNTczOFowgaQxCzAJ\n" + + "BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJh\n" + + "bmNpc2NvMRYwFAYDVQQKDA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5n\n" + + "MRYwFAYDVQQDDA1jYXRlc3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3Rp\n" + + "bmdAY2F0ZXN0aW5nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" + + "AKmolPe+fw1pQ91MNrlHBdRCX5J55vuV7zqbKU8UdZVuI3iajvhFtMm6xxIyISIM\n" + + "cO+h0JYKH0NHgCV0wqUKh+T16FEPo8BskxMCXIn0R0IQBBeUIIS3+FtNzRhLK3ff\n" + + "R7jtpv4YMp50tx8FVj9zIYFnLq5RObH7hENrxLTk04G3Gr8ENqapH21pfVlkb4GP\n" + + "RXBonxyR/q997ZGqutxiEbsCeWDMl/uvPDL3zKbd2e/Hx0Xy5kBOqAF35JTmKxax\n" + + "up33ld3u3r2MlFzcD7l/2uG7ur42MNDFBG2yQXsmPFUE6/Ogk/IfuGiqVYYjjRZV\n" + + "ixO+HK3WnICJggsNDQO6CqsCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAOjhaAvUw\n" + + "AeQ6PO485xoNpnadrFu+Q1RafqZCHd/UJ/c2VLeEogsBjZpLMMclF01T6k7X/6N5\n" + + "44oy9MS90/IVeRsfL0fT0pPGao2by4eja5di2j8UiiyuWSUI4J5PqBn48pNNYt4P\n" + + "W0MKS74ea/lDWmsGWw9B/mGZ/0znIo2WUCpifIGBqUfqfmBAFjMU/4aK1bMfzhZc\n" + + "OJNcpa2iOgrqd9Irjrs0kM5j0omN42be65FXUEoTVH5zOJr9sHaThTVM8PZ+A5Vm\n" + + "uSxSBG00WgrVuuCixz1DDrXI+LR/ipVU/fTGWRywBQRunrQsY7V9ODtmSipczi+o\n" + + "udwFdKx9QrsPEA==\n" + + "-----END CERTIFICATE-----\n"; + + // A client certificate signed by the "Testing CA" + static final String clientCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIIDxDCCAqwCAQIwDQYJKoZIhvcNAQEFBQAwgaQxCzAJBgNVBAYTAlVTMRMwEQYD\n" + + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRYwFAYDVQQK\n" + + "DA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5nMRYwFAYDVQQDDA1jYXRl\n" + + "c3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3RpbmdAY2F0ZXN0aW5nLmNv\n" + + "bTAeFw0yMTA1MzExMzU3MzhaFw00ODEwMTUxMzU3MzhaMIGqMQswCQYDVQQGEwJV\n" + + "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEU\n" + + "MBIGA1UECgwLRXhhbXBsZSBDby4xFzAVBgNVBAsMDlRlc3RpbmcgQ2xpZW50MRQw\n" + + "EgYDVQQDDAtleGFtcGxlLmNvbTEpMCcGCSqGSIb3DQEJARYadGVzdGluZy1jbGll\n" + + "bnRAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp\n" + + "izibTCEp21PffO6UN8wX7PW+dd7HTepbwqd2mT3iYqC2/IuO8mzS5Sk/mNqL2T9T\n" + + "GPcx4cLRGGovBw/Ig3A3nv4uBxzFVHJqtHZJl+VPgZiUDmyBaNsv0g5fqluiktcU\n" + + "MI0HQWABOEcwCyBYHXadxp8DUAUdjniopax7cMTujrWRFJAH7s4++yu5kK06mMwD\n" + + "Rsh65CGKvP5tz+cszoabo5I8dT+8tTPHrgdaCqjI9536VtbgmU3LKNG1DNkmR6nq\n" + + "ICo1Lg0+GFnkFnEQpbV8WsckwFempDND3MtZKw6U5VxGs+EdTj0s0UEbCNVwLkW1\n" + + "uBFyfcnODpWe2VY0wPafAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHf1P4+fzuln\n" + + "KHIBpkZTE++0tW0KUwoXfRIWATOp3XFqYJTHQm4KtTQ+mVo5FejavC57KrgeTp73\n" + + "mljxKJhzT6Oh89shLxYPF/mvgfhWgDpwPLXmFkepigZ88MLeMV3UG9MJVcB5UrTv\n" + + "RTxire5Ga1iaWRtHHeY4OXKp1foBxPyc9l+XHfxHeAjM4Oj4UoOKdV9sr+UYKXP7\n" + + "N0QBXdTHa70mhzOQP6PE5VUu98HFAA6oYlbIWwetXCBDdJ1sTeh+Uw5NAzfsUw5S\n" + + "NGib2Ru0SeKvjrpIzOto5DO6ZQdlb3Yfbj2S4ea3HBUHe6dNYQNa+dnq4xvrA3Iv\n" + + "CT7KNBGK8AQ=\n" + + "-----END CERTIFICATE-----\n"; + + // A server certificate signed by the "Testing CA" + static final String serverCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIIDxDCCAqwCAQEwDQYJKoZIhvcNAQEFBQAwgaQxCzAJBgNVBAYTAlVTMRMwEQYD\n" + + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRYwFAYDVQQK\n" + + "DA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5nMRYwFAYDVQQDDA1jYXRl\n" + + "c3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3RpbmdAY2F0ZXN0aW5nLmNv\n" + + "bTAeFw0yMTA1MzExMzU3MzhaFw00ODEwMTUxMzU3MzhaMIGqMQswCQYDVQQGEwJV\n" + + "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEU\n" + + "MBIGA1UECgwLRXhhbXBsZSBDby4xFzAVBgNVBAsMDlRlc3RpbmcgU2VydmVyMRQw\n" + + "EgYDVQQDDAtleGFtcGxlLmNvbTEpMCcGCSqGSIb3DQEJARYadGVzdGluZy1zZXJ2\n" + + "ZXJAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA\n" + + "7nYmXdUcCgkDnwDPV0MEjbNQvVNxL9Sd/zJZBy02jcn2mklBJtAb18Nvra7tw14L\n" + + "KmEILFC4GwKVEtF1vqE6gBCxaohE0ZZ/dJGgPVvuRn+r1nzIwT4LKmDoayfne29K\n" + + "w2oDvxX+0vU9BI9c2N8ZN3Ge9n/r8Rp25SP0cyRKa8Q862kPXgsFsYX/D/mZpK/p\n" + + "KfwhLGl/6tCKTsOteetEvHPnirHIYgb81zI5NGirrckO6PJ3b+PJzdjgW3/12jWg\n" + + "9JrqT9Wg3Uf3VESGiUoAr9xnMun026jpWhRJL0zzZnpP0hr8RYAJ2Mfh8JpZjlx6\n" + + "0ePzHPTckRydLPc8tvCvAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHq0euTu0433\n" + + "nPwt9CuyC10o4wyTBCW0tXupM/06Hqk0U9rOQsPw0zNuQaW/Ww1rqpyG8S3Mw27f\n" + + "oua6Usfnwog5eREUi1XGe2HcGeqSca+34+WPUyi6agS2NEqpXyZWiRgfLUEngo3d\n" + + "Ph1BFHsxOXqLk+LCouA1OxeS8WZHdMRt6lzP+3FEJhiiEBAF8YIoQD6kD1lVBo4J\n" + + "e5Vof1Zs6frxbi12nh/Iu7YUEgm0IZ6X5GSs5c+nj0hHdPPN86Pul3fc9tWJ8MJO\n" + + "KYmsEX4YiIebo+dzrFEZywSQXOG5xkMhOprdc28stiIGkJCRSf/1lPy70i5YHMjr\n" + + "VqwFqu16zZg=\n" + + "-----END CERTIFICATE-----\n"; + + // An arbitrary self-signed certificate + static final String arbitraryCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIID3jCCAsYCCQD5JkW9FPJFYzANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n" + + "VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\n" + + "FDASBgNVBAoMC0V4YW1wbGUgQ28uMRowGAYDVQQLDBFUZXN0aW5nIEFyYml0cmFy\n" + + "eTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xLDAqBgkqhkiG9w0BCQEWHXRlc3Rpbmct\n" + + "YXJiaXRyYXJ5QGV4YW1wbGUuY29tMB4XDTIxMDYwMjA4MDkwNloXDTQ4MTAxNzA4\n" + + "MDkwNlowgbAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\n" + + "VQQHDA1TYW4gRnJhbmNpc2NvMRQwEgYDVQQKDAtFeGFtcGxlIENvLjEaMBgGA1UE\n" + + "CwwRVGVzdGluZyBBcmJpdHJhcnkxFDASBgNVBAMMC2V4YW1wbGUuY29tMSwwKgYJ\n" + + "KoZIhvcNAQkBFh10ZXN0aW5nLWFyYml0cmFyeUBleGFtcGxlLmNvbTCCASIwDQYJ\n" + + "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrvljMgkh0nLoNH9fNolHJWZ74xRDYT\n" + + "fjpMjNvJZqKvveQMhIWgLFkPM1fMsXQEeXxfrLMEt5bh0Crbr7GfODaoltH+HM8d\n" + + "xoXDqaXcDFtRdVyf4nM7RdwEQGhZHfa3oiewQG6UOrWpOU5s4GQL0Pk0oKNZFRGJ\n" + + "nR8hyPxnyB5p+M5VBOO/ATuOsKS+uXYjjI5ndof8bTx9xDykebiMTLmfy745req6\n" + + "KnaLC7NKSHsBUEvJlaQrRzyK5zouEz0Sfk3QfvnCOoLOng11WZ3I88jTl69J2X6E\n" + + "TLwAiNrsI24zvuCa24jKeehBvDIGjfa68+YQq7Zkdlsere9EoEe7JSMCAwEAATAN\n" + + "BgkqhkiG9w0BAQUFAAOCAQEAEwDID0JaJaI2WQMT0oZ9+Zz8Mf7wZbngrf9uG9MW\n" + + "oljQNwtFtcUYcMd6q+wsGd24bR//YBGkAXke6x+DNyRtbbTbFLFQgko/7Do6jB7U\n" + + "p75oNJgRBKsNO6eGIaLBxIYWxZcZ77IhRoX82WPRRLaT2iAc8p2I3QMra5Y+E+Aj\n" + + "f9gKqRKzwDgWOApD27KosJJZd2zddHZm/Fj+T8kWPTHFCt0FnzgGAVxKp1bFmzxX\n" + + "qGGFKW4IThE87fTFcgbRkUZdnrKTo6tCDDjIi2Rb2jJv7ip6BvI2cHip+UvQmvhe\n" + + "qRQdDY0scDekIRZ0WQYg4h/kmfNp9Zw55Ce18aMTXEwGrQ==\n" + + "-----END CERTIFICATE-----\n"; + + static final String clientKey = + "-----BEGIN PRIVATE KEY-----\n" + + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpizibTCEp21Pf\n" + + "fO6UN8wX7PW+dd7HTepbwqd2mT3iYqC2/IuO8mzS5Sk/mNqL2T9TGPcx4cLRGGov\n" + + "Bw/Ig3A3nv4uBxzFVHJqtHZJl+VPgZiUDmyBaNsv0g5fqluiktcUMI0HQWABOEcw\n" + + "CyBYHXadxp8DUAUdjniopax7cMTujrWRFJAH7s4++yu5kK06mMwDRsh65CGKvP5t\n" + + "z+cszoabo5I8dT+8tTPHrgdaCqjI9536VtbgmU3LKNG1DNkmR6nqICo1Lg0+GFnk\n" + + "FnEQpbV8WsckwFempDND3MtZKw6U5VxGs+EdTj0s0UEbCNVwLkW1uBFyfcnODpWe\n" + + "2VY0wPafAgMBAAECggEAZvBnsyq66/4F46in9ogWO+ScpEJOu/XbuFDsen66ayx0\n" + + "5gVZ+rXISxfmPn3hG44Q+7QpyjiHn4rSVbFU7OqZBLxdGbcpycnnGlBtjWtTSD2o\n" + + "VSSYzs3KXzOLlJwLvR6oxdJgniocT0FLP6lRvw5MiakhvNIl+Pca3VKR8fTbLPet\n" + + "wXsTSQ+80GRAUyLMKMn1Xs7A6AIrXodFmwuVFB8MUdAf8g6jGEacagz26SBzl34v\n" + + "YfcF6RWKnBJOwz0ej0sCX8rUyB5rBqGO/z9Ey9XzuaCYJgc4SBwcohsvVsBCGD+E\n" + + "8aXYPC5v9nWOSBRnzZruw/hg/j8YHP8Coy8bavtTEQKBgQDWVM6AI7CDH2wbqKdL\n" + + "L3S4cILpQ4nP0ajF7Ql5TCAxLTWSCvryMSBeas4tnG5oM9+z5JQiIzveCOqtMd8s\n" + + "geysLwk39wBN50uZPiOSlq+PxraPibDo9cI/hmi5IKIXqxnJS3OQ1LqJ4vDun7Yh\n" + + "rsXhk0UN62irRqXTzVY0qPlZBwKBgQDKgVvi63Lm5KVTLDB8Q162Hr83QV6VEMjK\n" + + "yd3AEaps+Q1McyKuub6VSucj3JyTZXDpUYUSoZaRrD8qqNDZ+h/uxHs8d8PD+jW7\n" + + "c3z3YVkeUezAUOdwZMvQ0SXa1zdf159rRAKl7Xj+VLhR0+3HXxw6RYsxCPhfHYy5\n" + + "Lukh1BoHqQKBgBFrBvUm8VtWnGSLCj1z99pdWmY2lOaMtViQcOqoox0b/XSG6+nu\n" + + "0CCcMXFHezmArbdi5h74Gg9rThcRLH/jdyZvFCK2MhIir+QeRqnNEStwDLoRiI0G\n" + + "G+kptS0GV+Xwg8H2HcgxYY9/H/FkjVqjZ3VzkHMXJIR201cpIs5YxRrVAoGAM+CB\n" + + "vpccn2PRqoX2gc7sc3FbAPfBGCTtm22tXifoZfRDYONZ7jLtTOecYQaCIgxpqYvV\n" + + "sFku7nCW2gHXRxAZoBw7idkQkKMHotbKG8GXh/nq0bWoJJXd1MfPj8l0iRv+3gbV\n" + + "OtakGVtwwJ2vG1UVMSRhrRUkM5GpXENVO/JPHMkCgYBn8rw3sPZ6gNk2ttF1yKGA\n" + + "Kr3Wb2rwcG5Pf5ESCNktuvY6ipxnOGuvmbMnWMQe9KnEbFrjiEDgYTxcnHztPAt1\n" + + "cK8KDB8cBrj2OHIMc83Yfon5mM8VybTTWx3Cd/AAfhyNB0vohbJNeHO80sQH+2Cn\n" + + "6aAcVdLuUdYcYq6RrIPM8w==\n" + + "-----END PRIVATE KEY-----\n"; + + static final String serverKey = + "-----BEGIN PRIVATE KEY-----\n" + + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDA7nYmXdUcCgkD\n" + + "nwDPV0MEjbNQvVNxL9Sd/zJZBy02jcn2mklBJtAb18Nvra7tw14LKmEILFC4GwKV\n" + + "EtF1vqE6gBCxaohE0ZZ/dJGgPVvuRn+r1nzIwT4LKmDoayfne29Kw2oDvxX+0vU9\n" + + "BI9c2N8ZN3Ge9n/r8Rp25SP0cyRKa8Q862kPXgsFsYX/D/mZpK/pKfwhLGl/6tCK\n" + + "TsOteetEvHPnirHIYgb81zI5NGirrckO6PJ3b+PJzdjgW3/12jWg9JrqT9Wg3Uf3\n" + + "VESGiUoAr9xnMun026jpWhRJL0zzZnpP0hr8RYAJ2Mfh8JpZjlx60ePzHPTckRyd\n" + + "LPc8tvCvAgMBAAECggEAScjpFrM8FYUg/WmJ/cH5t3wZ3/8IMnmAbwxyTOoZuItx\n" + + "egZ3jZsya/OQot1h0Tyucsa6ZU3NcRujWS/hO460SpM/zxpXEzq0u/nw17+fsPj1\n" + + "Stq0znJZMBv9A+Y3VKg4X/dsTBKAbvxvHe7ohTHL4PD7WzgapDmJTX9EyPBgKLVz\n" + + "+6z6wOtP2KwjtgWBipm3gMFNUSJVEtgD4XQP1nOTF+lUI51dhW9iVFbx5l85zrtX\n" + + "gzqTMNVxQTPpdTvILUmjJBFEgZcGrq8mGYcXSHZhdIgPYLk3G1bexvtUTVzq8zbV\n" + + "/vrCADb25B/1jT+u+txAkh9XpJM/sQFylUoo4EDGgQKBgQDss0JVZ4504zqlncFe\n" + + "IizOOEy1Ba+M5DNZCqx9RlHJdMTDCdJi0phHvvaOhwXvyKLJkJuzxByUb5QthQ5p\n" + + "Yc1yU14ps8K7d9Ck8HX3K035SPprYQrBd0XMHzaDS2+2zr+/c+xyf/xMTC8iaUvl\n" + + "lizbu8O442PlyMHUbrVONeS/xwKBgQDQqZl8biSO0ljYBgYNc8INtS/TTFq5lUqP\n" + + "LgFfi1He2wj44nGd8iSHuNbaJJCv0UJVjKlZYpazN4bwzJSckrlU72lp6WFNapqF\n" + + "CDRV295PDbwaE2NRi2pJ+inTFpN1nvRC2Rx54UVKBDDcU+bZQw/WuQJ3LQcMlSVz\n" + + "QYoQXb6X2QKBgBSkES3HaQnSYuPcXOdrjYKyMCY9B7D+mWezYZVPE4TA1QO5EIqj\n" + + "mLnw8ik9pwvg8CkpnhpQCLn8/Ov3RWl1KOhGUtjKHzof2ab4fSD/ur35WjUQ8lIq\n" + + "p4CEXEmYw3Yqk1gLsNvPQ14X6qhSjFbKAMFsn0W5NpXsKtLukIrwcjEzAoGAWGql\n" + + "KP6a6xHip5bV1blpTtmprEU8ZEsITudVmaC1TlNN1/hL4HuMUx5VnBXGYVmwXAPA\n" + + "dqm55bLvsPVfO4FImt7fsgs8OcukMh6p3n/OEX1maT4x5YnHvhUMx+9XCI4UPoc0\n" + + "88gqzhQ8h//dX8501a2Lh+hChmhkeBQbZpfyfPECgYAj5dPvdITryOyjsEzkPcex\n" + + "zhwrgrdrSk/B+AlhmT3y6AwQ4ZI0uABJeVMpzQE6sS7lMkg7intgGIsienW60vdH\n" + + "oGxJxL7KGnzcY32N66a9SRReU3k6KWC0dMbWOZi6ebLLDXcjc4DIlUscUcHFEwfS\n" + + "AkZ3d5ywA+rEzXzaAquoBg==\n" + + "-----END PRIVATE KEY-----\n"; + + static final String arbitraryKey = + "-----BEGIN PRIVATE KEY-----\n" + + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDK75YzIJIdJy6D\n" + + "R/XzaJRyVme+MUQ2E346TIzbyWair73kDISFoCxZDzNXzLF0BHl8X6yzBLeW4dAq\n" + + "26+xnzg2qJbR/hzPHcaFw6ml3AxbUXVcn+JzO0XcBEBoWR32t6InsEBulDq1qTlO\n" + + "bOBkC9D5NKCjWRURiZ0fIcj8Z8geafjOVQTjvwE7jrCkvrl2I4yOZ3aH/G08fcQ8\n" + + "pHm4jEy5n8u+Oa3quip2iwuzSkh7AVBLyZWkK0c8iuc6LhM9En5N0H75wjqCzp4N\n" + + "dVmdyPPI05evSdl+hEy8AIja7CNuM77gmtuIynnoQbwyBo32uvPmEKu2ZHZbHq3v\n" + + "RKBHuyUjAgMBAAECggEBAMi9jtosUdy8sWnzePu6a31es2iT22GSjr6kkoGnC/vJ\n" + + "1BENwNldxACk5KjpNnAJLRM2oOLEu8ZowT5j6bvOQBDxW5+FuoG2dnZDQkFrFl4O\n" + + "igWBssNB0qz9F6kg3l7671BLLLE1t42TQ7isQps0hRa+VFjA+fJLKj1tch8bmf1a\n" + + "Gfcscs0ornwerXlMtRXcunVh64/aRQpT7/f3RhDvucASXrly28gLh1qx7AxtpgJ0\n" + + "tHtaenq4yYJujZHTJfIkB6WzAwT0RhVJYL8hm+DjJePG65u9b/W2oHCt3Mi3Rj+b\n" + + "XlDEPFmD2SXvuu2tenkEHkOMQRQpoOh69Vc6d/bQlMECgYEA9yNHnHrctc6N56k+\n" + + "zCPMI4nvgWEeKPczdfVmzYQonwqNhPim5R70hAKmRMznrcvPjCNH71u63BvPfMFQ\n" + + "PasKCBG10TGIRlpT2V0uOplTenV7xeOM7jVkQpjaz+NN7PoApL7bv9vCLL0l5TGS\n" + + "t7+s8DyIjRHJqxHmgLhWTbk4aMkCgYEA0jaIv4xEq9rjhneQ3cjNNn6hbunXqRuR\n" + + "8hxwDYFGhl6LcxhWF7chQGNl4TTJvXtTms4Umt+vcTJL7iSkLlpITQBWuyU310cL\n" + + "1+DXEh5J4f3iqmgkbdsVHcYmOOLQnuYssn7+cblB6dadSB/mZui6zkIrvopnoLVb\n" + + "YL9DjxioQIsCgYA5NZaHN73N7GHXJcuesA66j1y9I4E61Ha6MLO6kYRhxKycAn+H\n" + + "/JF32bEprhFXnx2NgEFPvHlWKK3wYEO18tkgoxDmu0OjnZdZcwOXlxTG/VlIpvNh\n" + + "1UQ/UmkcxK6uU/VALdpq4HFjr+mM09v1404iUrD9jweTLVKhq4p29ZCEWQKBgQCO\n" + + "mD+a7+OFUC4XAPRb/eJ2nN+VBTstk24k9fVss8zLSUb/A/siiy8bJlHtuok+53GH\n" + + "CVQg2qt/9cZb/K8CYmu5EAnFWTHP7nmyLuq1d6ZWjoo7XfmYK4zfbZJv9CvgHfMk\n" + + "AdFIA4savGJkkn8QP764O1rBHdG9ykf6EMQbRXackQKBgH4GygOQ3ndk+h+ArM78\n" + + "P8v0Cn8/30qHyQH8X8y9/BEk3Wz3aJ+AwgORaW4FZxVelWiOTJgHdZNy6Rg90q7I\n" + + "pKHE9hkL/dxsEBXkVdaRHzrfxCBxsP3b3EmOJ4xdl1phGhedUk/+RUWGY8VSnyaq\n" + + "uVMy1feUqV+AebfUSPzPVubV\n" + + "-----END PRIVATE KEY-----\n"; } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java index 44e7fdfb2a..0edecca98d 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java @@ -97,7 +97,15 @@ public class SlowServerIT { // binary is requested, the delay on the server guarantees that the // timeout on the client will expire. - StandbyClientSync client = new StandbyClientSync("localhost", serverPort.getPort(), secondary, false, 1000, false, folder.newFolder()) + StandbyClientSync client = StandbyClientSync.builder() + .withHost("localhost") + .withPort(serverPort.getPort()) + .withFileStore(secondary) + .withSecureConnection(false) + .withReadTimeoutMs(1000) + .withAutoClean(false) + .withSpoolFolder(folder.newFolder()) + .build() ) { server.start(); client.run();