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
This commit is contained in:
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.
|
||||
|
||||
Reference in New Issue
Block a user