Add SSL bundle support to MongoDB auto-configuration

Update MongoDB auto-configuration so that an SSL can be configured
via an SSL bundle.

Closes gh-35042
pull/35107/head
Scott Frederick 2 years ago committed by Phillip Webb
parent 1618aa2dac
commit fd5fd1491a

@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -69,9 +70,9 @@ public class MongoAutoConfiguration {
@Bean
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
MongoConnectionDetails connectionDetails) {
MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) {
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
properties.getUuidRepresentation());
properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 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.
@ -110,6 +110,8 @@ public class MongoProperties {
*/
private UuidRepresentation uuidRepresentation = UuidRepresentation.JAVA_LEGACY;
private final Ssl ssl = new Ssl();
/**
* Whether to enable auto-index creation.
*/
@ -226,6 +228,10 @@ public class MongoProperties {
this.additionalHosts = additionalHosts;
}
public Ssl getSsl() {
return this.ssl;
}
public static class Gridfs {
/**
@ -256,4 +262,35 @@ public class MongoProperties {
}
public static class Ssl {
/**
* Whether to enable SSL support. Enabled automatically if "bundle" is provided
* unless specified otherwise.
*/
private Boolean enabled;
/**
* SSL bundle name.
*/
private String bundle;
public boolean isEnabled() {
return (this.enabled != null) ? this.enabled : this.bundle != null;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getBundle() {
return this.bundle;
}
public void setBundle(String bundle) {
this.bundle = bundle;
}
}
}

@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@ -76,9 +77,9 @@ public class MongoReactiveAutoConfiguration {
@Bean
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
MongoConnectionDetails connectionDetails) {
MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) {
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
properties.getUuidRepresentation());
properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable());
}
}

@ -18,8 +18,11 @@ package org.springframework.boot.autoconfigure.mongo;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.connection.SslSettings;
import org.bson.UuidRepresentation;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.core.Ordered;
/**
@ -37,18 +40,35 @@ public class StandardMongoClientSettingsBuilderCustomizer implements MongoClient
private final UuidRepresentation uuidRepresentation;
private final MongoProperties.Ssl ssl;
private final SslBundles sslBundles;
private int order = 0;
public StandardMongoClientSettingsBuilderCustomizer(ConnectionString connectionString,
UuidRepresentation uuidRepresentation) {
UuidRepresentation uuidRepresentation, MongoProperties.Ssl ssl, SslBundles sslBundles) {
this.connectionString = connectionString;
this.uuidRepresentation = uuidRepresentation;
this.ssl = ssl;
this.sslBundles = sslBundles;
}
@Override
public void customize(MongoClientSettings.Builder settingsBuilder) {
settingsBuilder.uuidRepresentation(this.uuidRepresentation);
settingsBuilder.applyConnectionString(this.connectionString);
if (this.ssl.isEnabled()) {
settingsBuilder.applyToSslSettings(this::configureSsl);
}
}
private void configureSsl(SslSettings.Builder settings) {
settings.enabled(true);
if (this.ssl.getBundle() != null) {
SslBundle sslBundle = this.sslBundles.getBundle(this.ssl.getBundle());
settings.context(sslBundle.createSslContext());
}
}
@Override

@ -23,9 +23,11 @@ import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.internal.MongoClientImpl;
import com.mongodb.connection.SslSettings;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@ -43,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class MongoAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class));
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, SslAutoConfiguration.class));
@Test
void clientExists() {
@ -72,6 +74,39 @@ class MongoAutoConfigurationTests {
.run((context) -> assertThat(getSettings(context).getSslSettings().isEnabled()).isTrue());
}
@Test
void configuresSslWhenEnabled() {
this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNull();
});
}
@Test
void configuresSslWithBundle() {
this.contextRunner
.withPropertyValues("spring.data.mongodb.ssl.bundle=test-bundle",
"spring.ssl.bundle.jks.test-bundle.keystore.location=classpath:test.jks",
"spring.ssl.bundle.jks.test-bundle.keystore.password=secret",
"spring.ssl.bundle.jks.test-bundle.key.password=password")
.run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNotNull();
});
}
@Test
void configuresWithoutSslWhenDisabledWithBundle() {
this.contextRunner
.withPropertyValues("spring.data.mongodb.ssl.enabled=false", "spring.data.mongodb.ssl.bundle=test-bundle")
.run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isFalse();
});
}
@Test
void configuresSingleClient() {
this.contextRunner.withUserConfiguration(FallbackMongoClientConfig.class)

@ -23,6 +23,7 @@ import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadPreference;
import com.mongodb.connection.AsynchronousSocketChannelStreamFactoryFactory;
import com.mongodb.connection.SslSettings;
import com.mongodb.connection.StreamFactory;
import com.mongodb.connection.StreamFactoryFactory;
import com.mongodb.connection.netty.NettyStreamFactoryFactory;
@ -32,6 +33,7 @@ import io.netty.channel.EventLoopGroup;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@ -52,7 +54,7 @@ import static org.mockito.Mockito.mock;
class MongoReactiveAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MongoReactiveAutoConfiguration.class));
.withConfiguration(AutoConfigurations.of(MongoReactiveAutoConfiguration.class, SslAutoConfiguration.class));
@Test
void clientExists() {
@ -86,6 +88,39 @@ class MongoReactiveAutoConfigurationTests {
});
}
@Test
void configuresSslWhenEnabled() {
this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNull();
});
}
@Test
void configuresSslWithBundle() {
this.contextRunner
.withPropertyValues("spring.data.mongodb.ssl.bundle=test-bundle",
"spring.ssl.bundle.jks.test-bundle.keystore.location=classpath:test.jks",
"spring.ssl.bundle.jks.test-bundle.keystore.password=secret",
"spring.ssl.bundle.jks.test-bundle.key.password=password")
.run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNotNull();
});
}
@Test
void configuresWithoutSslWhenDisabledWithBundle() {
this.contextRunner
.withPropertyValues("spring.data.mongodb.ssl.enabled=false", "spring.data.mongodb.ssl.bundle=test-bundle")
.run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isFalse();
});
}
@Test
void nettyStreamFactoryFactoryIsConfiguredAutomatically() {
AtomicReference<EventLoopGroup> eventLoopGroupReference = new AtomicReference<>();

@ -114,6 +114,31 @@ For example, you might declare the following settings in your `application.prope
password: "secret"
----
The auto-configured `MongoClient` can be configured to use SSL for communication with the server by setting the properties as shown in this example:
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
spring:
data:
mongodb:
uri: "mongodb://user:secret@mongoserver1.example.com:27017,mongoserver2.example.com:23456/test"
ssl:
enabled: true
----
Custom SSL trust material can be configured in an <<features#features.ssl,SSL bundle>> and applied to the `MongoClient` as shown in this example:
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
spring:
data:
mongodb:
uri: "mongodb://user:secret@mongoserver1.example.com:27017,mongoserver2.example.com:23456/test"
ssl:
bundle: "example"
----
[TIP]
====
If `spring.data.mongodb.port` is not specified, the default of `27017` is used.

Loading…
Cancel
Save