Polish “Allow management server SSL to be configured independently”

This commit polishes b0fbc7e, throwing an exception when an attempt is
made to configure management-specific SSL without also configuring a
custom management port. The testing of management-specific SSL
configuration has also been improved.

See gh-6057
Closes gh-4810
pull/6018/merge
Andy Wilkinson 9 years ago
parent 3546ae399e
commit 618535f576

@ -160,10 +160,19 @@ public class EndpointWebMvcAutoConfiguration
+ "through JMX)");
}
}
if (managementPort == ManagementServerPort.SAME && this.applicationContext
.getEnvironment() instanceof ConfigurableEnvironment) {
addLocalManagementPortPropertyAlias(
(ConfigurableEnvironment) this.applicationContext.getEnvironment());
if (managementPort == ManagementServerPort.SAME) {
if (new RelaxedPropertyResolver(this.applicationContext.getEnvironment(),
"management.ssl.").getProperty("enabled") != null) {
throw new IllegalStateException(
"Management-specific SSL cannot be configured as the management "
+ "server is not listening on a separate port");
}
if (this.applicationContext
.getEnvironment() instanceof ConfigurableEnvironment) {
addLocalManagementPortPropertyAlias(
(ConfigurableEnvironment) this.applicationContext
.getEnvironment());
}
}
}

@ -29,6 +29,11 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
@ -75,6 +80,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.test.util.ReflectionTestUtils;
@ -113,6 +119,12 @@ public class EndpointWebMvcAutoConfigurationTests {
private static ManagementServerProperties management = new ManagementServerProperties();
@Before
public void defaultContextPath() {
management.setContextPath("");
server.setContextPath("");
}
@Before
public void grabPorts() {
Ports values = new Ports();
@ -175,32 +187,6 @@ public class EndpointWebMvcAutoConfigurationTests {
assertThat(interceptors).hasSize(1);
}
@Test
public void onDifferentPortManagementSslDisabled() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext,
"management.ssl.enabled:false");
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
DifferentPortConfig.class, BaseConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", ports.get().server, "controlleroutput");
assertContent("/endpoint", ports.get().server, null);
assertContent("/controller", ports.get().management, null);
assertContent("/endpoint", ports.get().management, "endpointoutput");
assertContent("/error", ports.get().management, startsWith("{"));
ApplicationContext managementContext = this.applicationContext
.getBean(ManagementContextResolver.class).getApplicationContext();
List<?> interceptors = (List<?>) ReflectionTestUtils.getField(
managementContext.getBean(EndpointHandlerMapping.class), "interceptors");
assertThat(interceptors).hasSize(1);
ManagementServerProperties managementServerProperties = this.applicationContext
.getBean(ManagementServerProperties.class);
assertThat(managementServerProperties.getSsl()).isNotNull();
assertThat(managementServerProperties.getSsl().isEnabled()).isFalse();
this.applicationContext.close();
assertAllClosed();
}
@Test
public void onDifferentPortWithSpecificContainer() throws Exception {
this.applicationContext.register(SpecificContainerConfig.class, RootConfig.class,
@ -226,8 +212,6 @@ public class EndpointWebMvcAutoConfigurationTests {
assertThat(managementContainerFactory)
.isInstanceOf(SpecificEmbeddedServletContainerFactory.class);
assertThat(managementContainerFactory).isNotSameAs(parentContainerFactory);
this.applicationContext.close();
assertAllClosed();
}
@Test
@ -511,6 +495,73 @@ public class EndpointWebMvcAutoConfigurationTests {
.hasSize(1);
}
@Test
public void managementSpecificSslUsingDifferentPort() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext,
"management.ssl.enabled=true",
"management.ssl.key-store=classpath:test.jks",
"management.ssl.key-password=password");
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
DifferentPortConfig.class, BaseConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", ports.get().server, "controlleroutput");
assertContent("/endpoint", ports.get().server, null);
assertHttpsContent("/controller", ports.get().management, null);
assertHttpsContent("/endpoint", ports.get().management, "endpointoutput");
assertHttpsContent("/error", ports.get().management, startsWith("{"));
ApplicationContext managementContext = this.applicationContext
.getBean(ManagementContextResolver.class).getApplicationContext();
List<?> interceptors = (List<?>) ReflectionTestUtils.getField(
managementContext.getBean(EndpointHandlerMapping.class), "interceptors");
assertThat(interceptors).hasSize(1);
ManagementServerProperties managementServerProperties = this.applicationContext
.getBean(ManagementServerProperties.class);
assertThat(managementServerProperties.getSsl()).isNotNull();
assertThat(managementServerProperties.getSsl().isEnabled()).isTrue();
}
@Test
public void managementSpecificSslUsingSamePortFails() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext,
"management.ssl.enabled=true",
"management.ssl.key-store=classpath:test.jks",
"management.ssl.key-password=password");
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, ServerPortConfig.class);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Management-specific SSL cannot be configured as the "
+ "management server is not listening on a separate port");
this.applicationContext.refresh();
}
@Test
public void managementServerCanDisableSslWhenUsingADifferentPort() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext,
"server.ssl.enabled=true", "server.ssl.key-store=classpath:test.jks",
"server.ssl.key-password=password", "management.ssl.enabled=false");
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
DifferentPortConfig.class, BaseConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertHttpsContent("/controller", ports.get().server, "controlleroutput");
assertHttpsContent("/endpoint", ports.get().server, null);
assertContent("/controller", ports.get().management, null);
assertContent("/endpoint", ports.get().management, "endpointoutput");
assertContent("/error", ports.get().management, startsWith("{"));
ApplicationContext managementContext = this.applicationContext
.getBean(ManagementContextResolver.class).getApplicationContext();
List<?> interceptors = (List<?>) ReflectionTestUtils.getField(
managementContext.getBean(EndpointHandlerMapping.class), "interceptors");
assertThat(interceptors).hasSize(1);
ManagementServerProperties managementServerProperties = this.applicationContext
.getBean(ManagementServerProperties.class);
assertThat(managementServerProperties.getSsl()).isNotNull();
assertThat(managementServerProperties.getSsl().isEnabled()).isFalse();
}
private void endpointDisabled(String name, Class<? extends MvcEndpoint> type) {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class,
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class);
@ -538,10 +589,27 @@ public class EndpointWebMvcAutoConfigurationTests {
assertContent("/endpoint", ports.get().management, null);
}
public void assertContent(String url, int port, Object expected) throws Exception {
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
ClientHttpRequest request = clientHttpRequestFactory
.createRequest(new URI("http://localhost:" + port + url), HttpMethod.GET);
private void assertHttpsContent(String url, int port, Object expected)
throws Exception {
assertContent("https", url, port, expected);
}
private void assertContent(String url, int port, Object expected) throws Exception {
assertContent("http", url, port, expected);
}
private void assertContent(String scheme, String url, int port, Object expected)
throws Exception {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build());
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
ClientHttpRequest request = requestFactory.createRequest(
new URI(scheme + "://localhost:" + port + url), HttpMethod.GET);
try {
ClientHttpResponse response = request.execute();
if (HttpStatus.NOT_FOUND.equals(response.getStatusCode())) {

@ -1006,10 +1006,25 @@ content into your application; rather pick only the properties that you need.
management.add-application-context-header=true # Add the "X-Application-Context" HTTP header in each response.
management.address= # Network address that the management endpoints should bind to.
management.context-path= # Management endpoint context-path. For instance `/actuator`
management.port= # Management endpoint HTTP port. Use the same port as the application by default.
management.port= # Management endpoint HTTP port. Uses the same port as the application by default. Configure a different port to use management-specific SSL.
management.security.enabled=true # Enable security.
management.security.roles=ADMIN # Comma-separated list of roles that can access the management endpoint.
management.security.sessions=stateless # Session creating policy to use (always, never, if_required, stateless).
management.ssl.ciphers= # Supported SSL ciphers. Requires a custom management.port.
management.ssl.client-auth= # Whether client authentication is wanted ("want") or needed ("need"). Requires a trust store. Requires a custom management.port.
management.ssl.enabled= # Enable SSL support. Requires a custom management.port.
management.ssl.enabled-protocols= # Enabled SSL protocols. Requires a custom management.port.
management.ssl.key-alias= # Alias that identifies the key in the key store. Requires a custom management.port.
management.ssl.key-password= # Password used to access the key in the key store. Requires a custom management.port.
management.ssl.key-store= # Path to the key store that holds the SSL certificate (typically a jks file). Requires a custom management.port.
management.ssl.key-store-password= # Password used to access the key store. Requires a custom management.port.
management.ssl.key-store-provider= # Provider for the key store. Requires a custom management.port.
management.ssl.key-store-type= # Type of the key store. Requires a custom management.port.
management.ssl.protocol=TLS # SSL protocol to use. Requires a custom management.port.
management.ssl.trust-store= # Trust store that holds SSL certificates. Requires a custom management.port.
management.ssl.trust-store-password= # Password used to access the trust store. Requires a custom management.port.
management.ssl.trust-store-provider= # Provider for the trust store. Requires a custom management.port.
management.ssl.trust-store-type= # Type of the trust store. Requires a custom management.port.
# HEALTH INDICATORS (previously health.*)
management.health.db.enabled=true # Enable database health check.

@ -595,6 +595,39 @@ disable the management security in this way, and it might even break the applica
[[production-ready-management-specific-ssl]]
=== Configuring management-specific SSL
When configured to use a custom port, the management server can also be configured with
its own SSL using the various `management.ssl.*` properties. For example, this allows a
management server to be available via HTTP while the main application uses HTTPS:
[source,properties,indent=0]
----
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:store.jks
server.ssl.key-password=secret
management.port=8080
management.ssl.enable=false
----
Alternatively, both the main server and the management server can use SSL but with
different key stores:
[source,properties,indent=0]
----
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:main.jks
server.ssl.key-password=secret
management.port=8080
management.ssl.enable=true
management.ssl.key-store=classpath:management.jks
management.ssl.key-password=secret
----
[[production-ready-customizing-management-server-address]]
=== Customizing the management server address
You can customize the address that the management endpoints are available on by

Loading…
Cancel
Save