Merge pull request #6057 from Alex Antonov

* gh-6057:
  Polish “Allow management server SSL to be configured independently”
  Allow management server SSL to be configured independently
This commit is contained in:
Andy Wilkinson 2016-06-27 13:25:02 +01:00
commit 1175879fcc
7 changed files with 178 additions and 11 deletions

View File

@ -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());
}
}
}

View File

@ -188,6 +188,9 @@ public class EndpointWebMvcChildContextConfiguration {
container.setContextPath("");
// and add the management-specific bits
container.setPort(this.managementServerProperties.getPort());
if (this.managementServerProperties.getSsl() != null) {
container.setSsl(this.managementServerProperties.getSsl());
}
container.setServerHeader(this.server.getServerHeader());
container.setAddress(this.managementServerProperties.getAddress());
container.addErrorPages(new ErrorPage(this.server.getError().getPath()));

View File

@ -25,7 +25,9 @@ import javax.validation.constraints.NotNull;
import org.springframework.boot.autoconfigure.security.SecurityPrerequisite;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -68,6 +70,9 @@ public class ManagementServerProperties implements SecurityPrerequisite {
*/
private Integer port;
@NestedConfigurationProperty
private Ssl ssl;
/**
* Network address that the management endpoints should bind to.
*/
@ -112,6 +117,14 @@ public class ManagementServerProperties implements SecurityPrerequisite {
this.port = port;
}
public Ssl getSsl() {
return this.ssl;
}
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
public InetAddress getAddress() {
return this.address;
}

View File

@ -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();
@ -200,8 +212,6 @@ public class EndpointWebMvcAutoConfigurationTests {
assertThat(managementContainerFactory)
.isInstanceOf(SpecificEmbeddedServletContainerFactory.class);
assertThat(managementContainerFactory).isNotSameAs(parentContainerFactory);
this.applicationContext.close();
assertAllClosed();
}
@Test
@ -485,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);
@ -512,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())) {

Binary file not shown.

View File

@ -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.

View File

@ -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