Skip to content

Commit 606da8c

Browse files
committed
Hot reload for downstream client certificate
1 parent 7d60111 commit 606da8c

24 files changed

+485
-274
lines changed

cautils/src/main/java/com/dajudge/proxybase/ca/selfsign/CertificateAuthority.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.dajudge.proxybase.ca.Helpers;
2121
import com.dajudge.proxybase.certs.KeyStoreManager;
22+
import com.dajudge.proxybase.certs.KeyStoreWrapper;
2223

2324
import java.io.IOException;
2425
import java.security.*;
@@ -41,8 +42,9 @@ public CertificateAuthority(
4142

4243

4344
private PrivateKey caPrivateKey() {
44-
final KeyStore keyStore = keyStoreManager.getKeyStore().getKeyStore();
45-
final char[] password = keyStoreManager.getKeyStore().getKeyPassword();
45+
final KeyStoreWrapper wrapper = keyStoreManager.getKeyStore();
46+
final KeyStore keyStore = wrapper.getKeyStore();
47+
final char[] password = wrapper.getKeyPassword();
4648
return loadKey(keyStore, keyAlias, password);
4749
}
4850

proxybase/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ dependencies {
1010
testImplementation 'junit:junit:4.13'
1111
testImplementation project(":cautils")
1212
testImplementation project(":testca")
13+
testImplementation 'org.mockito:mockito-core:2.23.4'
1314
testRuntime 'ch.qos.logback:logback-classic:1.2.3'
1415
}

proxybase/src/main/java/com/dajudge/proxybase/DownstreamChannelFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.dajudge.proxybase;
1919

20+
import com.dajudge.proxybase.config.Endpoint;
2021
import io.netty.bootstrap.Bootstrap;
2122
import io.netty.channel.Channel;
2223
import io.netty.channel.ChannelInitializer;
@@ -43,6 +44,7 @@ public Channel create(
4344
final Consumer<SocketChannel> initializer
4445
) {
4546
try {
47+
LOG.debug("Creating downstream channel for {}:{}", endpoint.getHost(), endpoint.getPort());
4648
final Channel channel = new Bootstrap()
4749
.group(workerGroup)
4850
.channel(NioSocketChannel.class)

proxybase/src/main/java/com/dajudge/proxybase/DownstreamSslHandlerFactory.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,51 @@
1717

1818
package com.dajudge.proxybase;
1919

20+
import com.dajudge.proxybase.certs.Filesystem;
2021
import com.dajudge.proxybase.certs.KeyStoreManager;
22+
import com.dajudge.proxybase.config.DownstreamSslConfig;
2123
import io.netty.channel.ChannelHandler;
2224
import io.netty.handler.ssl.SslHandler;
2325

2426
import javax.net.ssl.SSLContext;
2527
import javax.net.ssl.SSLEngine;
26-
import javax.net.ssl.TrustManager;
2728
import javax.net.ssl.X509TrustManager;
2829
import java.security.KeyManagementException;
2930
import java.security.NoSuchAlgorithmException;
30-
import java.util.List;
31-
import java.util.stream.Stream;
31+
import java.util.Optional;
32+
import java.util.function.Supplier;
3233

34+
import static com.dajudge.proxybase.HostnameCheck.NULL_VERIFIER;
3335
import static com.dajudge.proxybase.SslUtils.createKeyManagers;
3436
import static com.dajudge.proxybase.SslUtils.createTrustManagers;
35-
import static java.util.stream.Collectors.toList;
37+
import static com.dajudge.proxybase.certs.ReloadingKeyStoreManager.createReloader;
3638

3739
public class DownstreamSslHandlerFactory {
40+
public static ChannelHandler createDownstreamSslHandler(
41+
final DownstreamSslConfig config,
42+
final String downstreamHostname,
43+
final Supplier<Long> clock,
44+
final Filesystem filesystem
45+
) {
46+
final HostnameCheck hostnameCheck = config.isHostnameVerificationEnabled()
47+
? new HttpClientHostnameCheck(downstreamHostname)
48+
: NULL_VERIFIER;
49+
return createDownstreamSslHandler(
50+
hostnameCheck,
51+
createReloader(config.getTrustStore(), clock, filesystem),
52+
config.getKeyStore().map(it -> createReloader(it, clock, filesystem))
53+
);
54+
}
55+
3856
public static ChannelHandler createDownstreamSslHandler(
3957
final HostnameCheck hostnameCheck,
4058
final KeyStoreManager trustStoreManager,
41-
final KeyStoreManager keyStoreManager
59+
final Optional<KeyStoreManager> keyStoreManager
4260
) {
4361
try {
4462
final SSLContext clientContext = SSLContext.getInstance("TLS");
4563
final X509TrustManager[] trustManagers = {
46-
new HostCheckingTrustManager(createDefaultTrustManagers(trustStoreManager), hostnameCheck)
64+
new HostCheckingTrustManager(createTrustManagers(trustStoreManager), hostnameCheck)
4765
};
4866
clientContext.init(createKeyManagers(keyStoreManager), trustManagers, null);
4967
final SSLEngine engine = clientContext.createSSLEngine();
@@ -53,9 +71,4 @@ public static ChannelHandler createDownstreamSslHandler(
5371
throw new RuntimeException("Failed to initialize downstream SSL handler", e);
5472
}
5573
}
56-
57-
private static List<X509TrustManager> createDefaultTrustManagers(final KeyStoreManager trustStoreManager) {
58-
return Stream.of((createTrustManagers(trustStoreManager)))
59-
.map(it -> (X509TrustManager) it).collect(toList());
60-
}
6174
}

proxybase/src/main/java/com/dajudge/proxybase/HostCheckingTrustManager.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@
2222
import java.security.cert.X509Certificate;
2323
import java.util.Collection;
2424

25+
import static java.util.Arrays.asList;
26+
2527
class HostCheckingTrustManager implements X509TrustManager {
2628
private final Collection<X509TrustManager> nextManagers;
2729
private final HostnameCheck hostnameCheck;
2830

2931
HostCheckingTrustManager(
30-
final Collection<X509TrustManager> nextManagers,
32+
final X509TrustManager[] nextManagers,
3133
final HostnameCheck hostnameCheck
3234
) {
33-
this.nextManagers = nextManagers;
35+
this.nextManagers = asList(nextManagers);
3436
this.hostnameCheck = hostnameCheck;
3537
}
3638

proxybase/src/main/java/com/dajudge/proxybase/HostnameCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.security.cert.CertificateException;
2121
import java.security.cert.X509Certificate;
2222

23-
interface HostnameCheck {
23+
public interface HostnameCheck {
2424
void verify(X509Certificate cert) throws CertificateException;
2525

2626
HostnameCheck NULL_VERIFIER = (cert) -> {

proxybase/src/main/java/com/dajudge/proxybase/ProxyChannelFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.dajudge.proxybase;
1919

20+
import com.dajudge.proxybase.config.Endpoint;
2021
import io.netty.channel.Channel;
2122
import io.netty.channel.socket.SocketChannel;
2223

proxybase/src/main/java/com/dajudge/proxybase/SslUtils.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
package com.dajudge.proxybase;
1919

20-
import com.dajudge.proxybase.certs.KeyStoreWrapper;
2120
import com.dajudge.proxybase.certs.KeyStoreManager;
21+
import com.dajudge.proxybase.certs.KeyStoreWrapper;
2222

2323
import javax.net.ssl.KeyManagerFactory;
2424
import javax.net.ssl.TrustManagerFactory;
@@ -28,17 +28,22 @@
2828
import java.security.NoSuchAlgorithmException;
2929
import java.security.UnrecoverableKeyException;
3030
import java.util.Arrays;
31+
import java.util.Optional;
3132

3233
import static javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm;
3334

3435
class SslUtils {
3536
static X509TrustManager[] createTrustManagers(final KeyStoreManager trustStoreManager) {
37+
return createTrustManagers(Optional.of(trustStoreManager));
38+
}
39+
40+
static X509TrustManager[] createTrustManagers(final Optional<KeyStoreManager> trustStoreManager) {
3641
try {
37-
if (trustStoreManager == null) {
42+
if (!trustStoreManager.isPresent()) {
3843
return new X509TrustManager[]{};
3944
}
4045
final TrustManagerFactory factory = TrustManagerFactory.getInstance(getDefaultAlgorithm());
41-
factory.init(trustStoreManager.getKeyStore().getKeyStore());
46+
factory.init(trustStoreManager.get().getKeyStore().getKeyStore());
4247
return Arrays.stream(factory.getTrustManagers())
4348
.filter(it -> it instanceof X509TrustManager)
4449
.map(it -> (X509TrustManager) it)
@@ -49,12 +54,17 @@ static X509TrustManager[] createTrustManagers(final KeyStoreManager trustStoreMa
4954
}
5055

5156
static X509KeyManager[] createKeyManagers(final KeyStoreManager keyStoreManager) {
57+
return createKeyManagers(Optional.of(keyStoreManager));
58+
}
59+
60+
static X509KeyManager[] createKeyManagers(final Optional<KeyStoreManager> keyStoreManager) {
5261
try {
53-
if (keyStoreManager == null) {
62+
if (!keyStoreManager.isPresent()) {
5463
return new X509KeyManager[]{};
5564
}
5665
final KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
57-
final KeyStoreWrapper keyStore = keyStoreManager.getKeyStore();
66+
final KeyStoreManager keyStoreWrapper = keyStoreManager.get();
67+
final KeyStoreWrapper keyStore = keyStoreWrapper.getKeyStore();
5868
factory.init(keyStore.getKeyStore(), keyStore.getKeyPassword());
5969
return Arrays.stream(factory.getKeyManagers())
6070
.filter(it -> it instanceof X509KeyManager)

proxybase/src/main/java/com/dajudge/proxybase/UpstreamChannelFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.dajudge.proxybase;
1919

20+
import com.dajudge.proxybase.config.Endpoint;
2021
import io.netty.bootstrap.ServerBootstrap;
2122
import io.netty.channel.Channel;
2223
import io.netty.channel.ChannelFuture;

proxybase/src/main/java/com/dajudge/proxybase/UpstreamSslHandlerFactory.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
package com.dajudge.proxybase;
1919

20+
import com.dajudge.proxybase.certs.Filesystem;
2021
import com.dajudge.proxybase.certs.KeyStoreManager;
22+
import com.dajudge.proxybase.config.UpstreamSslConfig;
2123
import io.netty.channel.ChannelHandler;
2224
import io.netty.handler.ssl.SslHandler;
2325

@@ -27,14 +29,29 @@
2729
import javax.net.ssl.TrustManager;
2830
import java.security.KeyManagementException;
2931
import java.security.NoSuchAlgorithmException;
32+
import java.util.Optional;
33+
import java.util.function.Supplier;
3034

3135
import static com.dajudge.proxybase.SslUtils.createKeyManagers;
3236
import static com.dajudge.proxybase.SslUtils.createTrustManagers;
37+
import static com.dajudge.proxybase.certs.ReloadingKeyStoreManager.createReloader;
3338

3439
public class UpstreamSslHandlerFactory {
40+
public static ChannelHandler createUpstreamSslHandler(
41+
final UpstreamSslConfig config,
42+
final Supplier<Long> clock,
43+
final Filesystem filesystem
44+
) {
45+
return createUpstreamSslHandler(
46+
config.isClientAuthRequired(),
47+
config.getTrustStore().map(trustStore -> createReloader(trustStore, clock, filesystem)),
48+
createReloader(config.getKeyStore(), clock, filesystem)
49+
);
50+
}
51+
3552
public static ChannelHandler createUpstreamSslHandler(
3653
final boolean enableClientAuth,
37-
final KeyStoreManager trustStoreManager,
54+
final Optional<KeyStoreManager> trustStoreManager,
3855
final KeyStoreManager keyStoreManager
3956
) {
4057
try {

0 commit comments

Comments
 (0)