Surface additional rabbit SSL factory properties

Add `validate-server-certificate` and `verify-hostname` properties to
`spring.rabbitmq.ssl` to allow additional SSL configuration.

Closes gh-14259
This commit is contained in:
Phillip Webb 2018-09-04 10:37:09 -07:00
parent ca7f961cf0
commit a50686b404
5 changed files with 93 additions and 5 deletions

View File

@ -39,6 +39,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}.
@ -87,6 +88,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 config)
throws Exception {
@ -117,6 +123,16 @@ public class RabbitAutoConfiguration {
factory.setKeyStorePassphrase(ssl.getKeyStorePassword());
factory.setTrustStore(ssl.getTrustStore());
factory.setTrustStorePassphrase(ssl.getTrustStorePassword());
factory.setSkipServerCertificateValidation(
!ssl.isValidateServerCertificate());
if (ssl.getVerifyHostname() != null) {
factory.setEnableHostnameVerification(ssl.getVerifyHostname());
}
else {
if (CAN_ENABLE_HOSTNAME_VERIFICATION) {
factory.setEnableHostnameVerification(true);
}
}
}
if (config.getConnectionTimeout() != null) {
factory.setConnectionTimeout(config.getConnectionTimeout());

View File

@ -337,6 +337,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;
}
@ -385,6 +396,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 {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -18,8 +18,10 @@ package org.springframework.boot.autoconfigure.amqp;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.NullTrustManager;
import org.aopalliance.aop.Advice;
import org.junit.After;
import org.junit.Rule;
@ -53,6 +55,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.Mockito.mock;
@ -411,6 +414,8 @@ public class RabbitAutoConfigurationTests {
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
assertThat(rabbitConnectionFactory.getSocketFactory())
.as("SocketFactory must use SSL").isInstanceOf(SSLSocketFactory.class);
TrustManager trustManager = getTrustManager(rabbitConnectionFactory);
assertThat(trustManager).isNotInstanceOf(NullTrustManager.class);
}
@Test
@ -422,7 +427,38 @@ public class RabbitAutoConfigurationTests {
"spring.rabbitmq.ssl.keyStore=foo",
"spring.rabbitmq.ssl.keyStorePassword=secret",
"spring.rabbitmq.ssl.trustStore=bar",
"spring.rabbitmq.ssl.trustStorePassword=secret");
"spring.rabbitmq.ssl.trustStorePassword=secret",
"spring.rabbitmq.ssl.validateServerCertificate=false");
getTargetConnectionFactory();
}
@Test
public void enableSslWithValidateServerCertificateFalse() throws Exception {
load(TestConfiguration.class, "spring.rabbitmq.ssl.enabled:true",
"spring.rabbitmq.ssl.validateServerCertificate=false");
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
TrustManager trustManager = getTrustManager(rabbitConnectionFactory);
assertThat(trustManager).isInstanceOf(NullTrustManager.class);
}
@Test
public void enableSslWithValidateServerCertificateDefault() throws Exception {
load(TestConfiguration.class, "spring.rabbitmq.ssl.enabled:true");
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
TrustManager trustManager = getTrustManager(rabbitConnectionFactory);
assertThat(trustManager).isNotInstanceOf(NullTrustManager.class);
}
private TrustManager getTrustManager(
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) {
Object sslContext = ReflectionTestUtils.getField(rabbitConnectionFactory,
"sslContext");
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() {

View File

@ -140,6 +140,7 @@
<neo4j-ogm.version>2.1.6</neo4j-ogm.version>
<postgresql.version>9.4.1212.jre7</postgresql.version>
<querydsl.version>4.1.4</querydsl.version>
<rabbit-amqp-client.version>4.0.3</rabbit-amqp-client.version>
<reactor.version>2.0.8.RELEASE</reactor.version>
<reactor-spring.version>2.0.7.RELEASE</reactor-spring.version>
<selenium.version>2.53.1</selenium.version>
@ -152,7 +153,7 @@
<solr.version>5.5.5</solr.version>
<spock.version>1.0-groovy-2.4</spock.version>
<spring.version>4.3.18.RELEASE</spring.version>
<spring-amqp.version>1.7.9.RELEASE</spring-amqp.version>
<spring-amqp.version>1.7.10.BUILD-SNAPSHOT</spring-amqp.version>
<spring-cloud-connectors.version>1.2.6.RELEASE</spring-cloud-connectors.version>
<spring-batch.version>3.0.9.RELEASE</spring-batch.version>
<spring-data-releasetrain.version>Ingalls-SR14</spring-data-releasetrain.version>
@ -730,6 +731,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>${rabbit-amqp-client.version}</version>
</dependency>
<dependency>
<groupId>com.samskivert</groupId>
<artifactId>jmustache</artifactId>
@ -3263,4 +3269,4 @@
<id>integration-test</id>
</profile>
</profiles>
</project>
</project>

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
@ -17,6 +19,7 @@
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<rabbit-amqp-client.version>4.8.0</rabbit-amqp-client.version>
</properties>
<dependencies>
<!-- Compile -->