Add SSL bundle support to Couchbase auto-configuration
Update Couchbase auto-configuration so that an SSL can be configured via an SSL bundle. Closes gh-34811
This commit is contained in:
committed by
Phillip Webb
parent
682457377a
commit
1618aa2dac
@@ -44,6 +44,8 @@ import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfigurati
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -69,8 +71,7 @@ public class CouchbaseAutoConfiguration {
|
||||
|
||||
private final CouchbaseProperties properties;
|
||||
|
||||
CouchbaseAutoConfiguration(CouchbaseProperties properties,
|
||||
ObjectProvider<CouchbaseConnectionDetails> connectionDetails) {
|
||||
CouchbaseAutoConfiguration(CouchbaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@@ -83,8 +84,8 @@ public class CouchbaseAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClusterEnvironment couchbaseClusterEnvironment(CouchbaseConnectionDetails connectionDetails,
|
||||
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers) {
|
||||
Builder builder = initializeEnvironmentBuilder(connectionDetails);
|
||||
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers, ObjectProvider<SslBundles> sslBundles) {
|
||||
Builder builder = initializeEnvironmentBuilder(connectionDetails, sslBundles.getIfAvailable());
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
@@ -99,7 +100,8 @@ public class CouchbaseAutoConfiguration {
|
||||
return Cluster.connect(connectionDetails.getConnectionString(), options);
|
||||
}
|
||||
|
||||
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails) {
|
||||
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails,
|
||||
SslBundles sslBundles) {
|
||||
ClusterEnvironment.Builder builder = ClusterEnvironment.builder();
|
||||
Timeouts timeouts = this.properties.getEnv().getTimeouts();
|
||||
builder.timeoutConfig((config) -> config.kvTimeout(timeouts.getKeyValue())
|
||||
@@ -117,13 +119,28 @@ public class CouchbaseAutoConfiguration {
|
||||
.idleHttpConnectionTimeout(io.getIdleHttpConnectionTimeout()));
|
||||
if ((connectionDetails instanceof PropertiesCouchbaseConnectionDetails)
|
||||
&& this.properties.getEnv().getSsl().getEnabled()) {
|
||||
builder.securityConfig((config) -> config.enableTls(true)
|
||||
.trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl())));
|
||||
configureSsl(builder, sslBundles);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl) {
|
||||
private void configureSsl(Builder builder, SslBundles sslBundles) {
|
||||
builder.securityConfig((config) -> config.enableTls(true)
|
||||
.trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl(), sslBundles)));
|
||||
}
|
||||
|
||||
private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl, SslBundles sslBundles) {
|
||||
if (ssl.getKeyStore() != null) {
|
||||
return loadTrustManagerFactory(ssl);
|
||||
}
|
||||
if (ssl.getBundle() != null) {
|
||||
SslBundle bundle = sslBundles.getBundle(ssl.getBundle());
|
||||
return bundle.getManagers().getTrustManagerFactory();
|
||||
}
|
||||
throw new IllegalStateException("A key store or bundle must be configured when SSL is enabled");
|
||||
}
|
||||
|
||||
private TrustManagerFactory loadTrustManagerFactory(CouchbaseProperties.Ssl ssl) {
|
||||
String resource = ssl.getKeyStore();
|
||||
try {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -148,8 +148,8 @@ public class CouchbaseProperties {
|
||||
public static class Ssl {
|
||||
|
||||
/**
|
||||
* Whether to enable SSL support. Enabled automatically if a "keyStore" is
|
||||
* provided unless specified otherwise.
|
||||
* Whether to enable SSL support. Enabled automatically if a "keyStore" or
|
||||
* "bundle" is provided unless specified otherwise.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
@@ -163,8 +163,14 @@ public class CouchbaseProperties {
|
||||
*/
|
||||
private String keyStorePassword;
|
||||
|
||||
/**
|
||||
* SSL bundle name.
|
||||
*/
|
||||
private String bundle;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return (this.enabled != null) ? this.enabled : StringUtils.hasText(this.keyStore);
|
||||
return (this.enabled != null) ? this.enabled
|
||||
: StringUtils.hasText(this.keyStore) || StringUtils.hasText(this.bundle);
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
@@ -187,6 +193,14 @@ public class CouchbaseProperties {
|
||||
this.keyStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
public String getBundle() {
|
||||
return this.bundle;
|
||||
}
|
||||
|
||||
public void setBundle(String bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Timeouts {
|
||||
|
||||
@@ -36,6 +36,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.PropertiesCouchbaseConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
||||
import org.springframework.boot.ssl.NoSuchSslBundleException;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -56,7 +58,7 @@ import static org.mockito.Mockito.mock;
|
||||
class CouchbaseAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseAutoConfiguration.class, SslAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void connectionStringIsRequired() {
|
||||
@@ -179,7 +181,7 @@ class CouchbaseAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableSslNoEnabledFlag() {
|
||||
void enableSslWithKeyStore() {
|
||||
testClusterEnvironment((env) -> {
|
||||
SecurityConfig securityConfig = env.securityConfig();
|
||||
assertThat(securityConfig.tlsEnabled()).isTrue();
|
||||
@@ -187,6 +189,30 @@ class CouchbaseAutoConfigurationTests {
|
||||
}, "spring.couchbase.env.ssl.keyStore=classpath:test.jks", "spring.couchbase.env.ssl.keyStorePassword=secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableSslWithBundle() {
|
||||
testClusterEnvironment((env) -> {
|
||||
SecurityConfig securityConfig = env.securityConfig();
|
||||
assertThat(securityConfig.tlsEnabled()).isTrue();
|
||||
assertThat(securityConfig.trustManagerFactory()).isNotNull();
|
||||
}, "spring.ssl.bundle.jks.test-bundle.keystore.location=classpath:test.jks",
|
||||
"spring.ssl.bundle.jks.test-bundle.keystore.password=secret",
|
||||
"spring.couchbase.env.ssl.bundle=test-bundle");
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableSslWithInvalidBundle() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.couchbase.connection-string=localhost",
|
||||
"spring.couchbase.env.ssl.bundle=test-bundle")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context.getStartupFailure()).rootCause()
|
||||
.isInstanceOf(NoSuchSslBundleException.class)
|
||||
.hasMessageContaining("test-bundle");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableSslEvenWithKeyStore() {
|
||||
testClusterEnvironment((env) -> {
|
||||
@@ -197,6 +223,15 @@ class CouchbaseAutoConfigurationTests {
|
||||
"spring.couchbase.env.ssl.keyStorePassword=secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableSslEvenWithBundle() {
|
||||
testClusterEnvironment((env) -> {
|
||||
SecurityConfig securityConfig = env.securityConfig();
|
||||
assertThat(securityConfig.tlsEnabled()).isFalse();
|
||||
assertThat(securityConfig.trustManagerFactory()).isNull();
|
||||
}, "spring.couchbase.env.ssl.enabled=false", "spring.couchbase.env.ssl.bundle=test-bundle");
|
||||
}
|
||||
|
||||
private void testClusterEnvironment(Consumer<ClusterEnvironment> environmentConsumer, String... environment) {
|
||||
this.contextRunner.withUserConfiguration(CouchbaseTestConfiguration.class)
|
||||
.withPropertyValues("spring.couchbase.connection-string=localhost")
|
||||
|
||||
@@ -455,7 +455,7 @@ Generally, you provide the https://github.com/couchbaselabs/sdk-rfcs/blob/master
|
||||
----
|
||||
|
||||
It is also possible to customize some of the `ClusterEnvironment` settings.
|
||||
For instance, the following configuration changes the timeout to open a new `Bucket` and enables SSL support:
|
||||
For instance, the following configuration changes the timeout to open a new `Bucket` and enables SSL support with a reference to a configured <<features#features.ssl,SSL bundle>>:
|
||||
|
||||
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
|
||||
----
|
||||
@@ -465,8 +465,7 @@ For instance, the following configuration changes the timeout to open a new `Buc
|
||||
timeouts:
|
||||
connect: "3s"
|
||||
ssl:
|
||||
key-store: "/location/of/keystore.jks"
|
||||
key-store-password: "secret"
|
||||
bundle: "example"
|
||||
----
|
||||
|
||||
TIP: Check the `spring.couchbase.env.*` properties for more details.
|
||||
|
||||
Reference in New Issue
Block a user