Skip to content

Commit 1618aa2

Browse files
scottfrederickphilwebb
authored andcommitted
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
1 parent 6824573 commit 1618aa2

File tree

4 files changed

+82
-17
lines changed

4 files changed

+82
-17
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts;
4545
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
4646
import org.springframework.boot.context.properties.EnableConfigurationProperties;
47+
import org.springframework.boot.ssl.SslBundle;
48+
import org.springframework.boot.ssl.SslBundles;
4749
import org.springframework.context.annotation.Bean;
4850
import org.springframework.context.annotation.Conditional;
4951
import org.springframework.context.annotation.Configuration;
@@ -69,8 +71,7 @@ public class CouchbaseAutoConfiguration {
6971

7072
private final CouchbaseProperties properties;
7173

72-
CouchbaseAutoConfiguration(CouchbaseProperties properties,
73-
ObjectProvider<CouchbaseConnectionDetails> connectionDetails) {
74+
CouchbaseAutoConfiguration(CouchbaseProperties properties) {
7475
this.properties = properties;
7576
}
7677

@@ -83,8 +84,8 @@ PropertiesCouchbaseConnectionDetails couchbaseConnectionDetails() {
8384
@Bean
8485
@ConditionalOnMissingBean
8586
public ClusterEnvironment couchbaseClusterEnvironment(CouchbaseConnectionDetails connectionDetails,
86-
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers) {
87-
Builder builder = initializeEnvironmentBuilder(connectionDetails);
87+
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers, ObjectProvider<SslBundles> sslBundles) {
88+
Builder builder = initializeEnvironmentBuilder(connectionDetails, sslBundles.getIfAvailable());
8889
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
8990
return builder.build();
9091
}
@@ -99,7 +100,8 @@ public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment,
99100
return Cluster.connect(connectionDetails.getConnectionString(), options);
100101
}
101102

102-
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails) {
103+
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails,
104+
SslBundles sslBundles) {
103105
ClusterEnvironment.Builder builder = ClusterEnvironment.builder();
104106
Timeouts timeouts = this.properties.getEnv().getTimeouts();
105107
builder.timeoutConfig((config) -> config.kvTimeout(timeouts.getKeyValue())
@@ -117,13 +119,28 @@ private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnect
117119
.idleHttpConnectionTimeout(io.getIdleHttpConnectionTimeout()));
118120
if ((connectionDetails instanceof PropertiesCouchbaseConnectionDetails)
119121
&& this.properties.getEnv().getSsl().getEnabled()) {
120-
builder.securityConfig((config) -> config.enableTls(true)
121-
.trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl())));
122+
configureSsl(builder, sslBundles);
122123
}
123124
return builder;
124125
}
125126

126-
private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl) {
127+
private void configureSsl(Builder builder, SslBundles sslBundles) {
128+
builder.securityConfig((config) -> config.enableTls(true)
129+
.trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl(), sslBundles)));
130+
}
131+
132+
private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl, SslBundles sslBundles) {
133+
if (ssl.getKeyStore() != null) {
134+
return loadTrustManagerFactory(ssl);
135+
}
136+
if (ssl.getBundle() != null) {
137+
SslBundle bundle = sslBundles.getBundle(ssl.getBundle());
138+
return bundle.getManagers().getTrustManagerFactory();
139+
}
140+
throw new IllegalStateException("A key store or bundle must be configured when SSL is enabled");
141+
}
142+
143+
private TrustManagerFactory loadTrustManagerFactory(CouchbaseProperties.Ssl ssl) {
127144
String resource = ssl.getKeyStore();
128145
try {
129146
TrustManagerFactory trustManagerFactory = TrustManagerFactory

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseProperties.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -148,8 +148,8 @@ public void setIdleHttpConnectionTimeout(Duration idleHttpConnectionTimeout) {
148148
public static class Ssl {
149149

150150
/**
151-
* Whether to enable SSL support. Enabled automatically if a "keyStore" is
152-
* provided unless specified otherwise.
151+
* Whether to enable SSL support. Enabled automatically if a "keyStore" or
152+
* "bundle" is provided unless specified otherwise.
153153
*/
154154
private Boolean enabled;
155155

@@ -163,8 +163,14 @@ public static class Ssl {
163163
*/
164164
private String keyStorePassword;
165165

166+
/**
167+
* SSL bundle name.
168+
*/
169+
private String bundle;
170+
166171
public Boolean getEnabled() {
167-
return (this.enabled != null) ? this.enabled : StringUtils.hasText(this.keyStore);
172+
return (this.enabled != null) ? this.enabled
173+
: StringUtils.hasText(this.keyStore) || StringUtils.hasText(this.bundle);
168174
}
169175

170176
public void setEnabled(Boolean enabled) {
@@ -187,6 +193,14 @@ public void setKeyStorePassword(String keyStorePassword) {
187193
this.keyStorePassword = keyStorePassword;
188194
}
189195

196+
public String getBundle() {
197+
return this.bundle;
198+
}
199+
200+
public void setBundle(String bundle) {
201+
this.bundle = bundle;
202+
}
203+
190204
}
191205

192206
public static class Timeouts {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.boot.autoconfigure.AutoConfigurations;
3737
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.PropertiesCouchbaseConnectionDetails;
3838
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
39+
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
40+
import org.springframework.boot.ssl.NoSuchSslBundleException;
3941
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4042
import org.springframework.context.annotation.Bean;
4143
import org.springframework.context.annotation.Configuration;
@@ -56,7 +58,7 @@
5658
class CouchbaseAutoConfigurationTests {
5759

5860
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
59-
.withConfiguration(AutoConfigurations.of(CouchbaseAutoConfiguration.class));
61+
.withConfiguration(AutoConfigurations.of(CouchbaseAutoConfiguration.class, SslAutoConfiguration.class));
6062

6163
@Test
6264
void connectionStringIsRequired() {
@@ -179,14 +181,38 @@ void customizeEnvTimeouts() {
179181
}
180182

181183
@Test
182-
void enableSslNoEnabledFlag() {
184+
void enableSslWithKeyStore() {
183185
testClusterEnvironment((env) -> {
184186
SecurityConfig securityConfig = env.securityConfig();
185187
assertThat(securityConfig.tlsEnabled()).isTrue();
186188
assertThat(securityConfig.trustManagerFactory()).isNotNull();
187189
}, "spring.couchbase.env.ssl.keyStore=classpath:test.jks", "spring.couchbase.env.ssl.keyStorePassword=secret");
188190
}
189191

192+
@Test
193+
void enableSslWithBundle() {
194+
testClusterEnvironment((env) -> {
195+
SecurityConfig securityConfig = env.securityConfig();
196+
assertThat(securityConfig.tlsEnabled()).isTrue();
197+
assertThat(securityConfig.trustManagerFactory()).isNotNull();
198+
}, "spring.ssl.bundle.jks.test-bundle.keystore.location=classpath:test.jks",
199+
"spring.ssl.bundle.jks.test-bundle.keystore.password=secret",
200+
"spring.couchbase.env.ssl.bundle=test-bundle");
201+
}
202+
203+
@Test
204+
void enableSslWithInvalidBundle() {
205+
this.contextRunner
206+
.withPropertyValues("spring.couchbase.connection-string=localhost",
207+
"spring.couchbase.env.ssl.bundle=test-bundle")
208+
.run((context) -> {
209+
assertThat(context).hasFailed();
210+
assertThat(context.getStartupFailure()).rootCause()
211+
.isInstanceOf(NoSuchSslBundleException.class)
212+
.hasMessageContaining("test-bundle");
213+
});
214+
}
215+
190216
@Test
191217
void disableSslEvenWithKeyStore() {
192218
testClusterEnvironment((env) -> {
@@ -197,6 +223,15 @@ void disableSslEvenWithKeyStore() {
197223
"spring.couchbase.env.ssl.keyStorePassword=secret");
198224
}
199225

226+
@Test
227+
void disableSslEvenWithBundle() {
228+
testClusterEnvironment((env) -> {
229+
SecurityConfig securityConfig = env.securityConfig();
230+
assertThat(securityConfig.tlsEnabled()).isFalse();
231+
assertThat(securityConfig.trustManagerFactory()).isNull();
232+
}, "spring.couchbase.env.ssl.enabled=false", "spring.couchbase.env.ssl.bundle=test-bundle");
233+
}
234+
200235
private void testClusterEnvironment(Consumer<ClusterEnvironment> environmentConsumer, String... environment) {
201236
this.contextRunner.withUserConfiguration(CouchbaseTestConfiguration.class)
202237
.withPropertyValues("spring.couchbase.connection-string=localhost")

spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ Generally, you provide the https://github.com/couchbaselabs/sdk-rfcs/blob/master
455455
----
456456

457457
It is also possible to customize some of the `ClusterEnvironment` settings.
458-
For instance, the following configuration changes the timeout to open a new `Bucket` and enables SSL support:
458+
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>>:
459459

460460
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
461461
----
@@ -465,8 +465,7 @@ For instance, the following configuration changes the timeout to open a new `Buc
465465
timeouts:
466466
connect: "3s"
467467
ssl:
468-
key-store: "/location/of/keystore.jks"
469-
key-store-password: "secret"
468+
bundle: "example"
470469
----
471470

472471
TIP: Check the `spring.couchbase.env.*` properties for more details.

0 commit comments

Comments
 (0)