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:
commit
1175879fcc
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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…
Reference in New Issue