diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java index fb7df723789..c6ba861cd89 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java @@ -132,25 +132,15 @@ public class RabbitConnectionFactoryBeanConfigurer { .to(factory::setRequestedHeartbeat); map.from(this.rabbitProperties::getRequestedChannelMax).to(factory::setRequestedChannelMax); SslBundle sslBundle = this.connectionDetails.getSslBundle(); - if (sslBundle != null) { - applySslBundle(factory, sslBundle); - } - else { - RabbitProperties.Ssl ssl = this.rabbitProperties.getSsl(); - if (ssl.determineEnabled()) { - factory.setUseSSL(true); - map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm); - map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType); - map.from(ssl::getKeyStore).to(factory::setKeyStore); - map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase); - map.from(ssl::getKeyStoreAlgorithm).whenNonNull().to(factory::setKeyStoreAlgorithm); - map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); - map.from(ssl::getTrustStore).to(factory::setTrustStore); - map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); - map.from(ssl::getTrustStoreAlgorithm).whenNonNull().to(factory::setTrustStoreAlgorithm); - map.from(ssl::isValidateServerCertificate) - .to((validate) -> factory.setSkipServerCertificateValidation(!validate)); - map.from(ssl::isVerifyHostname).to(factory::setEnableHostnameVerification); + RabbitProperties.Ssl ssl = this.rabbitProperties.getSsl(); + if (sslBundle != null || ssl.determineEnabled()) { + factory.setUseSSL(true); + map.from(ssl::isVerifyHostname).to(factory::setEnableHostnameVerification); + if (sslBundle != null) { + applySslBundle(factory, sslBundle); + } + else { + applySslProperties(factory, map, ssl); } } map.from(this.rabbitProperties::getConnectionTimeout) @@ -169,11 +159,24 @@ public class RabbitConnectionFactoryBeanConfigurer { .to(factory::setMaxInboundMessageBodySize); } - private static void applySslBundle(RabbitConnectionFactoryBean factory, SslBundle bundle) { - factory.setUseSSL(true); + private void applySslBundle(RabbitConnectionFactoryBean factory, SslBundle bundle) { if (factory instanceof SslBundleRabbitConnectionFactoryBean sslFactory) { sslFactory.setSslBundle(bundle); } } + private void applySslProperties(RabbitConnectionFactoryBean factory, PropertyMapper map, RabbitProperties.Ssl ssl) { + map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm); + map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType); + map.from(ssl::getKeyStore).to(factory::setKeyStore); + map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase); + map.from(ssl::getKeyStoreAlgorithm).whenNonNull().to(factory::setKeyStoreAlgorithm); + map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); + map.from(ssl::getTrustStore).to(factory::setTrustStore); + map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); + map.from(ssl::getTrustStoreAlgorithm).whenNonNull().to(factory::setTrustStoreAlgorithm); + map.from(ssl::isValidateServerCertificate) + .to((validate) -> factory.setSkipServerCertificateValidation(!validate)); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/SslBundleRabbitConnectionFactoryBean.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/SslBundleRabbitConnectionFactoryBean.java index af71c00c63f..5a6868353b8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/SslBundleRabbitConnectionFactoryBean.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/SslBundleRabbitConnectionFactoryBean.java @@ -29,7 +29,7 @@ class SslBundleRabbitConnectionFactoryBean extends RabbitConnectionFactoryBean { private SslBundle sslBundle; - private boolean enableHostnameVerification; + private boolean enableHostnameVerification = true; @Override protected void setUpSSL() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java index ba743efef55..278c9e18096 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java @@ -16,17 +16,21 @@ package org.springframework.boot.autoconfigure.amqp; +import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.List; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import com.rabbitmq.client.Address; import com.rabbitmq.client.Connection; import com.rabbitmq.client.JDKSaslConfig; +import com.rabbitmq.client.SslEngineConfigurator; import com.rabbitmq.client.impl.CredentialsProvider; import com.rabbitmq.client.impl.CredentialsRefreshService; import com.rabbitmq.client.impl.DefaultCredentialsProvider; @@ -38,6 +42,7 @@ import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.springframework.amqp.core.AcknowledgeMode; @@ -843,6 +848,20 @@ class RabbitAutoConfigurationTests { assertThat(rabbitConnectionFactory.isSSL()).isTrue(); assertThat(rabbitConnectionFactory.getSocketFactory()).as("SocketFactory must use SSL") .isInstanceOf(SSLSocketFactory.class); + assertThatHostnameVerificationIsEnabled(rabbitConnectionFactory); + }); + } + + @Test + void enableSslWithoutHostnameVerification() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.rabbitmq.ssl.enabled:true", "spring.rabbitmq.ssl.verify-hostname:false") + .run((context) -> { + com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory(context); + assertThat(rabbitConnectionFactory.isSSL()).isTrue(); + assertThat(rabbitConnectionFactory.getSocketFactory()).as("SocketFactory must use SSL") + .isInstanceOf(SSLSocketFactory.class); + assertThatHostnameVerificationIsDisabled(rabbitConnectionFactory); }); } @@ -915,6 +934,20 @@ class RabbitAutoConfigurationTests { .run((context) -> { com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory(context); assertThat(rabbitConnectionFactory.isSSL()).isTrue(); + assertThatHostnameVerificationIsEnabled(rabbitConnectionFactory); + }); + } + + @Test + void enableSslWithBundleAndWithoutHostnameVerification() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.rabbitmq.ssl.bundle=test-bundle", "spring.rabbitmq.ssl.verify-hostname=false", + "spring.ssl.bundle.jks.test-bundle.keystore.location=classpath:org/springframework/boot/autoconfigure/amqp/test.jks", + "spring.ssl.bundle.jks.test-bundle.keystore.password=secret") + .run((context) -> { + com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory(context); + assertThat(rabbitConnectionFactory.isSSL()).isTrue(); + assertThatHostnameVerificationIsDisabled(rabbitConnectionFactory); }); } @@ -1078,6 +1111,25 @@ class RabbitAutoConfigurationTests { .configure(any(DirectMessageListenerContainer.class))); } + private void assertThatHostnameVerificationIsEnabled(com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) + throws IOException { + SslEngineConfigurator sslEngineConfigurator = rabbitConnectionFactory.getNioParams().getSslEngineConfigurator(); + SSLEngine engine = mock(SSLEngine.class); + sslEngineConfigurator.configure(engine); + ArgumentCaptor sslParametersCaptor = ArgumentCaptor.forClass(SSLParameters.class); + then(engine).should().setSSLParameters(sslParametersCaptor.capture()); + SSLParameters sslParameters = sslParametersCaptor.getValue(); + assertThat(sslParameters.getEndpointIdentificationAlgorithm()).isEqualTo("HTTPS"); + } + + private void assertThatHostnameVerificationIsDisabled(com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) + throws IOException { + SslEngineConfigurator sslEngineConfigurator = rabbitConnectionFactory.getNioParams().getSslEngineConfigurator(); + SSLEngine engine = mock(SSLEngine.class); + sslEngineConfigurator.configure(engine); + then(engine).shouldHaveNoMoreInteractions(); + } + private com.rabbitmq.client.ConnectionFactory getTargetConnectionFactory(AssertableApplicationContext context) { CachingConnectionFactory connectionFactory = context.getBean(CachingConnectionFactory.class); return connectionFactory.getRabbitConnectionFactory();