Skip to content

Commit

Permalink
Merge pull request #1673 from jbellmann/feature/sslbundles_support
Browse files Browse the repository at this point in the history
Make it possible to use spring-boots ssl-bundle support
  • Loading branch information
fatroom authored Jan 8, 2025
2 parents e59f2e9 + 54f22ab commit b3d0a72
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.boot.ssl.SslBundles;
import org.zalando.logbook.Logbook;

import java.util.Arrays;
Expand All @@ -22,6 +23,7 @@

import static org.zalando.riptide.autoconfigure.ValueConstants.LOGBOOK_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.METER_REGISTRY_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.SSL_BUNDLE_REGISTRY_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.TRACER_REF;

@AllArgsConstructor
Expand Down Expand Up @@ -56,6 +58,10 @@ private Map<String, BeanMetadataElement> replacements() {
replacements.put(METER_REGISTRY_REF, getBeanRef(MeterRegistry.class, "meterRegistry"));
}

if (any(client -> client.getSslBundleUsage().getEnabled())) {
replacements.put(SSL_BUNDLE_REGISTRY_REF, getBeanRef(SslBundles.class, "sslBundleRegistry"));
}

return replacements;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import static org.zalando.riptide.autoconfigure.RiptideProperties.Chaos.Latency;
import static org.zalando.riptide.autoconfigure.ValueConstants.LOGBOOK_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.METER_REGISTRY_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.SSL_BUNDLE_REGISTRY_REF;
import static org.zalando.riptide.autoconfigure.ValueConstants.TRACER_REF;

@Slf4j
Expand Down Expand Up @@ -568,10 +569,7 @@ private String registerHttpClient(final String id, final Client client) {
return registry.registerIfAbsent(id, HttpClient.class, () -> {
log.debug("Client [{}]: Registering HttpClient", id);

final String connectionManager = registry.registerIfAbsent(id, HttpClientConnectionManager.class, () ->
genericBeanDefinition(HttpClientFactory.class)
.setFactoryMethod("createHttpClientConnectionManager")
.addConstructorArgValue(client));
final String connectionManager = registerConnectionManager(id, client);

if (client.getMetrics().getEnabled()) {
registry.registerIfAbsent(id, HttpConnectionPoolMetrics.class, () ->
Expand All @@ -592,6 +590,26 @@ private String registerHttpClient(final String id, final Client client) {
});
}

private String registerConnectionManager(final String id, final Client client) {
if(client.getSslBundleUsage().getEnabled() && client.getCertificatePinning().getEnabled()) {
throw new SslBundleUsageOrCertificatePinningException(id);
}

if(client.getSslBundleUsage().getEnabled()) {
return registry.registerIfAbsent(id, HttpClientConnectionManager.class, () ->
genericBeanDefinition(HttpClientFactory.class)
.setFactoryMethod("createHttpClientConnectionManagerWithSslBundle")
.addConstructorArgValue(client)
.addConstructorArgValue(id)
.addConstructorArgValue(SSL_BUNDLE_REGISTRY_REF));
} else {
return registry.registerIfAbsent(id, HttpClientConnectionManager.class, () ->
genericBeanDefinition(HttpClientFactory.class)
.setFactoryMethod("createHttpClientConnectionManager")
.addConstructorArgValue(client));
}
}

private List<BeanMetadataElement> configureFirstRequestInterceptors(final String id, final Client client) {
final List<BeanMetadataElement> interceptors = list();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.zalando.riptide.autoconfigure.RiptideProperties.RequestCompression;
import org.zalando.riptide.autoconfigure.RiptideProperties.Retry.Backoff;
import org.zalando.riptide.autoconfigure.RiptideProperties.Soap;
import org.zalando.riptide.autoconfigure.RiptideProperties.SslBundleUsage;
import org.zalando.riptide.autoconfigure.RiptideProperties.StackTracePreservation;
import org.zalando.riptide.autoconfigure.RiptideProperties.Telemetry;
import org.zalando.riptide.autoconfigure.RiptideProperties.Timeouts;
Expand Down Expand Up @@ -79,7 +80,8 @@ private static Defaults merge(final Defaults defaults) {
defaults.getTracing(),
defaults.getTelemetry(),
defaults.getChaos(),
defaults.getSoap()
defaults.getSoap(),
defaults.getSslBundleUsage()
);
}

Expand Down Expand Up @@ -121,7 +123,8 @@ private static Client merge(final Client base, final Defaults defaults) {
merge(base.getTracing(), defaults.getTracing(), Defaulting::merge),
merge(base.getTelemetry(), defaults.getTelemetry(), Defaulting::merge),
merge(base.getChaos(), defaults.getChaos(), Defaulting::merge),
merge(base.getSoap(), defaults.getSoap(), Defaulting::merge)
merge(base.getSoap(), defaults.getSoap(), Defaulting::merge),
merge(base.getSslBundleUsage(), defaults.getSslBundleUsage(), Defaulting::merge)
);
}

Expand Down Expand Up @@ -342,6 +345,13 @@ private static Soap merge(final Soap base, final Soap defaults) {
);
}

private static SslBundleUsage merge(final SslBundleUsage base, final SslBundleUsage defaults) {
return new SslBundleUsage(
either(base.getEnabled(), defaults.getEnabled()),
either(base.getSslBundleId(), defaults.getSslBundleId())
);
}

@SafeVarargs
private static <T> T either(final T... options) {
return Arrays.stream(options).filter(Objects::nonNull).findFirst().orElse(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.boot.ssl.SslBundles;
import org.zalando.riptide.autoconfigure.RiptideProperties.Caching;
import org.zalando.riptide.autoconfigure.RiptideProperties.Caching.Heuristic;
import org.zalando.riptide.autoconfigure.RiptideProperties.CertificatePinning;
import org.zalando.riptide.autoconfigure.RiptideProperties.CertificatePinning.Keystore;
import org.zalando.riptide.autoconfigure.RiptideProperties.Client;
import org.zalando.riptide.autoconfigure.RiptideProperties.Connections;
import org.zalando.riptide.autoconfigure.RiptideProperties.SslBundleUsage;

import javax.annotation.Nullable;
import javax.net.ssl.SSLContext;
Expand Down Expand Up @@ -63,6 +65,22 @@ public static HttpClientConnectionManager createHttpClientConnectionManager(fina
return manager;
}

public static HttpClientConnectionManager createHttpClientConnectionManagerWithSslBundle(final Client client, final String clientId, final SslBundles sslBundles) {

final Connections connections = client.getConnections();

final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(createSslContextFromSslBundle(client, clientId, sslBundles)))
.build());

manager.setMaxTotal(connections.getMaxTotal());
manager.setDefaultMaxPerRoute(connections.getMaxPerRoute());

return manager;
}

public static CloseableHttpClient createHttpClient(final Client client,
final List<HttpRequestInterceptor> firstRequestInterceptors,
final PoolingHttpClientConnectionManager connectionManager,
Expand Down Expand Up @@ -151,6 +169,23 @@ private static SSLContext createSSLContext(final Client client) throws GeneralSe
return SSLContexts.createDefault();
}

protected static SSLContext createSslContextFromSslBundle(final Client client, final String clientId, final SslBundles sslBundles) {
final SslBundleUsage sslBundleUsage = client.getSslBundleUsage();
if(sslBundleUsage.getEnabled()) {
final String bundleId = Optional.ofNullable(sslBundleUsage.getSslBundleId()).orElse(clientId);
try {
return sslBundles
.getBundle(bundleId)
.createSslContext();
} catch (final Exception e) {
log.error("Error loading ssl bundle [{}]:", bundleId, e);
throw e;
}
}

return SSLContexts.createDefault();
}

private static Consumer<HttpClientCustomizer> customize(final HttpClientBuilder builder) {
return customizer -> customizer.customize(builder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ public static final class Defaults {
@NestedConfigurationProperty
private Soap soap = new Soap(false, "1.1");

@NestedConfigurationProperty
private SslBundleUsage sslBundleUsage = new SslBundleUsage(false, null);

}

@Getter
Expand Down Expand Up @@ -214,6 +217,9 @@ public static final class Client {
@NestedConfigurationProperty
private Soap soap;

@NestedConfigurationProperty
private SslBundleUsage sslBundleUsage;

}

@Getter
Expand Down Expand Up @@ -460,4 +466,13 @@ public static final class Soap {
private String protocol;
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static final class SslBundleUsage {
private Boolean enabled;
private String sslBundleId;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.zalando.riptide.autoconfigure;

import org.springframework.core.NestedRuntimeException;

public class SslBundleUsageOrCertificatePinningException extends NestedRuntimeException {

private final String clientId;

public SslBundleUsageOrCertificatePinningException(final String clientId) {
super(createMessage(clientId));
this.clientId = clientId;
}

public SslBundleUsageOrCertificatePinningException(String clientId, String msg) {
super(msg);
this.clientId = clientId;
}

public SslBundleUsageOrCertificatePinningException(String clientId, String msg, Throwable cause) {
super(msg, cause);
this.clientId = clientId;
}

protected static String createMessage(String clientId) {
return String.format("CertificatePinning and SslBundleUsage configured at same time in http-client : '%s'", clientId);
}

public String getClientId() {
return clientId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.zalando.riptide.autoconfigure;

import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;

public class SslBundleUsageOrCertificatePinningFailureAnalyzer extends
AbstractFailureAnalyzer<SslBundleUsageOrCertificatePinningException> {

@Override
protected FailureAnalysis analyze(Throwable rootFailure,
SslBundleUsageOrCertificatePinningException cause) {
return new FailureAnalysis(getDescription(cause), getAction(cause), cause);
}

private String getDescription(SslBundleUsageOrCertificatePinningException cause) {
return String.format("The http-client '%s' is configured to use CertificatePinning and SslBundleUsage at the same time.", cause.getClientId());
}

private String getAction(SslBundleUsageOrCertificatePinningException cause) {
return String.format("Configure only CertificatePinning or SslBundleUsage for http-client '%s'", cause.getClientId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ private ValueConstants() {}
static final String METER_REGISTRY_REF = "\n\t\t\n\t\t\n\uE000\uE001\uE001\n\t\t\t\t\n";
static final String TRACER_REF = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
static final String LOGBOOK_REF = "\n\t\t\n\t\t\n\uE000\uE001\uE003\n\t\t\t\t\n";
static final String SSL_BUNDLE_REGISTRY_REF = "\n\t\t\n\uE000\uE001\uE004\n\t\t\t\t\n";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.zalando.riptide.autoconfigure.SslBundleUsageOrCertificatePinningFailureAnalyzer
Loading

0 comments on commit b3d0a72

Please sign in to comment.