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
This commit is contained in:
@@ -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())) {
|
||||
|
||||
BIN
spring-boot-actuator/src/test/resources/test.jks
Normal file
BIN
spring-boot-actuator/src/test/resources/test.jks
Normal file
Binary file not shown.
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user