diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java index 5b5f417dca..ec5ad7b19d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.amqp; import java.time.Duration; +import java.util.Objects; import com.rabbitmq.client.Channel; @@ -43,6 +44,7 @@ import org.springframework.context.annotation.Import; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetryTemplate; +import org.springframework.util.ReflectionUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link RabbitTemplate}. @@ -92,6 +94,11 @@ public class RabbitAutoConfiguration { @ConditionalOnMissingBean(ConnectionFactory.class) protected static class RabbitConnectionFactoryCreator { + // Only available in rabbitmq-java-client 5.4.0 + + private static final boolean CAN_ENABLE_HOSTNAME_VERIFICATION = ReflectionUtils + .findMethod(com.rabbitmq.client.ConnectionFactory.class, + "enableHostnameVerification") != null; + @Bean public CachingConnectionFactory rabbitConnectionFactory( RabbitProperties properties, @@ -141,6 +148,13 @@ public class RabbitAutoConfiguration { map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); map.from(ssl::getTrustStore).to(factory::setTrustStore); map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); + map.from(ssl::isValidateServerCertificate).to((validate) -> factory + .setSkipServerCertificateValidation(!validate)); + map.from(ssl::getVerifyHostname).when(Objects::nonNull) + .to(factory::setEnableHostnameVerification); + if (ssl.getVerifyHostname() == null && CAN_ENABLE_HOSTNAME_VERIFICATION) { + factory.setEnableHostnameVerification(true); + } } map.from(properties::getConnectionTimeout).whenNonNull() .asInt(Duration::toMillis).to(factory::setConnectionTimeout); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java index abf24b7650..252d9d39d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java @@ -349,6 +349,17 @@ public class RabbitProperties { */ private String algorithm; + /** + * Whether to enable server side certificate validation. + */ + private boolean validateServerCertificate = true; + + /** + * Whether to enable hostname verification. Requires AMQP client 4.8 or above and + * defaults to true when a suitable client version is used. + */ + private Boolean verifyHostname; + public boolean isEnabled() { return this.enabled; } @@ -413,6 +424,22 @@ public class RabbitProperties { this.algorithm = sslAlgorithm; } + public boolean isValidateServerCertificate() { + return this.validateServerCertificate; + } + + public void setValidateServerCertificate(boolean validateServerCertificate) { + this.validateServerCertificate = validateServerCertificate; + } + + public Boolean getVerifyHostname() { + return this.verifyHostname; + } + + public void setVerifyHostname(Boolean verifyHostname) { + this.verifyHostname = verifyHostname; + } + } public static class Cache { 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 938f861fb9..d1fd0231cc 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 @@ -19,10 +19,14 @@ package org.springframework.boot.autoconfigure.amqp; import java.security.NoSuchAlgorithmException; import java.util.concurrent.atomic.AtomicInteger; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; import com.rabbitmq.client.Address; import com.rabbitmq.client.Connection; +import com.rabbitmq.client.SslContextFactory; +import com.rabbitmq.client.TrustEverythingTrustManager; import org.aopalliance.aop.Advice; import org.junit.Rule; import org.junit.Test; @@ -58,6 +62,7 @@ import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.interceptor.MethodInvocationRecoverer; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetryTemplate; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; @@ -679,6 +684,45 @@ public class RabbitAutoConfigurationTests { .run((context) -> assertThat(context).hasNotFailed()); } + @Test + public void enableSslWithValidateServerCertificateFalse() throws Exception { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.rabbitmq.ssl.enabled:true", + "spring.rabbitmq.ssl.validateServerCertificate=false") + .run((context) -> { + com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory( + context); + TrustManager trustManager = getTrustManager(rabbitConnectionFactory); + assertThat(trustManager) + .isInstanceOf(TrustEverythingTrustManager.class); + }); + } + + @Test + public void enableSslWithValidateServerCertificateDefault() throws Exception { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.rabbitmq.ssl.enabled:true").run((context) -> { + com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory( + context); + TrustManager trustManager = getTrustManager(rabbitConnectionFactory); + assertThat(trustManager) + .isNotInstanceOf(TrustEverythingTrustManager.class); + }); + } + + private TrustManager getTrustManager( + com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) { + SslContextFactory sslContextFactory = (SslContextFactory) ReflectionTestUtils + .getField(rabbitConnectionFactory, "sslContextFactory"); + SSLContext sslContext = sslContextFactory.create("connection"); + Object spi = ReflectionTestUtils.getField(sslContext, "contextSpi"); + Object trustManager = ReflectionTestUtils.getField(spi, "trustManager"); + while (trustManager.getClass().getName().endsWith("Wrapper")) { + trustManager = ReflectionTestUtils.getField(trustManager, "tm"); + } + return (TrustManager) trustManager; + } + private com.rabbitmq.client.ConnectionFactory getTargetConnectionFactory( AssertableApplicationContext context) { CachingConnectionFactory connectionFactory = context diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index c41712242a..63da61e285 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -152,7 +152,7 @@ 1.19 6.6.5 5.0.8.RELEASE - 2.0.5.RELEASE + 2.0.6.BUILD-SNAPSHOT 4.0.1.RELEASE 2.0.2.RELEASE Kay-SR9 diff --git a/spring-boot-samples/spring-boot-sample-amqp/pom.xml b/spring-boot-samples/spring-boot-sample-amqp/pom.xml index 9cd849c0d4..c6a66a6100 100644 --- a/spring-boot-samples/spring-boot-sample-amqp/pom.xml +++ b/spring-boot-samples/spring-boot-sample-amqp/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -13,6 +14,7 @@ Spring Boot AMQP Sample ${basedir}/../.. + 5.4.0