From bacbe0459b939a6dfcbe67983009fc8eca0a7527 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Sun, 27 Aug 2017 21:24:31 -0700 Subject: [PATCH] Disable web endpoints by default Since the handler interceptors have been removed, web endpoints are all disabled by default to prevent accidental exposure of sensitive information. Closes gh-7958 --- .../support/EndpointEnablementProvider.java | 4 ++ .../ConditionalOnEnabledEndpointTests.java | 1 + ...tInfrastructureAutoConfigurationTests.java | 64 +++++++++---------- .../EndpointEnablementProviderTests.java | 16 ++--- ...ntManagementContextConfigurationTests.java | 4 +- .../mvc/MvcEndpointCorsIntegrationTests.java | 2 + .../endpoint/web/WebEndpointsRunner.java | 5 ++ .../boot/endpoint/EndpointType.java | 13 +++- 8 files changed, 66 insertions(+), 43 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProvider.java index 11ea5a249a..15efbc8816 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProvider.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProvider.java @@ -103,6 +103,10 @@ public class EndpointEnablementProvider { if (globalTypeOutcome != null) { return globalTypeOutcome; } + else if (!endpointType.isEnabledByDefault()) { + return new EndpointEnablement(false, createDefaultEnablementMessage("all", false, + endpointType)); + } } else { // Check if there is a global tech required diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ConditionalOnEnabledEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ConditionalOnEnabledEndpointTests.java index bb956f81f9..8068973874 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ConditionalOnEnabledEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ConditionalOnEnabledEndpointTests.java @@ -184,6 +184,7 @@ public class ConditionalOnEnabledEndpointTests { @Test public void enabledOnlyWebByDefault() { this.contextRunner.withUserConfiguration(OnlyWebConfig.class) + .withPropertyValues("endpoints.all.web.enabled=true") .run((context) -> assertThat(context).hasBean("onlyweb")); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/infrastructure/WebMvcEndpointInfrastructureAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/infrastructure/WebMvcEndpointInfrastructureAutoConfigurationTests.java index f3215752a8..28d56909b5 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/infrastructure/WebMvcEndpointInfrastructureAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/infrastructure/WebMvcEndpointInfrastructureAutoConfigurationTests.java @@ -55,40 +55,12 @@ public class WebMvcEndpointInfrastructureAutoConfigurationTests { ServletEndpointAutoConfiguration.class)); @Test - public void webEndpointsAreExposed() { - this.contextRunner.run((context) -> { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/autoconfig")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/beans")).isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/configprops")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/env")).isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/health")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/info")).isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/mappings")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/metrics")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.POST, "/application/shutdown")) - .isFalse(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/threaddump")) - .isTrue(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/trace")).isTrue(); - }); - } - - @Test - public void webEndpointsCanBeDisabled() { - WebApplicationContextRunner contextRunner = this.contextRunner - .withPropertyValues("endpoints.all.web.enabled=false"); - contextRunner.run((context) -> { + public void webEndpointsAreDisabledByDefault() { + this.contextRunner.run(context -> { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/autoconfig")) .isFalse(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/beans")) - .isFalse(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/beans")).isFalse(); assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/configprops")) .isFalse(); assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/env")).isFalse(); @@ -103,8 +75,36 @@ public class WebMvcEndpointInfrastructureAutoConfigurationTests { .isFalse(); assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/threaddump")) .isFalse(); - assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/trace")) + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/trace")).isFalse(); + }); + } + + @Test + public void webEndpointsCanBeEnabled() { + WebApplicationContextRunner contextRunner = this.contextRunner + .withPropertyValues("endpoints.all.web.enabled=true"); + contextRunner.run(context -> { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/autoconfig")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/beans")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/configprops")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/env")).isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/health")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/info")).isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/mappings")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/metrics")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.POST, "/application/shutdown")) .isFalse(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/threaddump")) + .isTrue(); + assertThat(isExposed(mockMvc, HttpMethod.GET, "/application/trace")) + .isTrue(); }); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProviderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProviderTests.java index 1c11a2e308..445a4201ac 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProviderTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/support/EndpointEnablementProviderTests.java @@ -185,8 +185,8 @@ public class EndpointEnablementProviderTests { @Test public void specificEnabledByDefault() { - validate(determineEnablement("foo", true, EndpointType.WEB), true, - "endpoint 'foo' (web) is enabled by default"); + validate(determineEnablement("foo", true, EndpointType.JMX), true, + "endpoint 'foo' (jmx) is enabled by default"); } @Test @@ -208,15 +208,15 @@ public class EndpointEnablementProviderTests { @Test public void specificNotDisabledViaUnrelatedTechProperty() { validate( - determineEnablement("foo", true, EndpointType.WEB, - "endpoints.foo.jmx.enabled=false"), - true, "endpoint 'foo' (web) is enabled by default"); + determineEnablement("foo", true, EndpointType.JMX, + "endpoints.foo.web.enabled=false"), + true, "endpoint 'foo' (jmx) is enabled by default"); } @Test public void specificDisabledViaGeneralProperty() { validate( - determineEnablement("foo", true, EndpointType.WEB, + determineEnablement("foo", true, EndpointType.JMX, "endpoints.all.enabled=false"), false, "found property endpoints.all.enabled"); } @@ -256,8 +256,8 @@ public class EndpointEnablementProviderTests { @Test public void specificEnabledOverrideHasNoEffectWithUnrelatedTechProperty() { validate( - determineEnablement("foo", true, EndpointType.WEB, - "endpoints.all.enabled=false", "endpoints.all.jmx.enabled=true"), + determineEnablement("foo", true, EndpointType.JMX, + "endpoints.all.enabled=false", "endpoints.all.web.enabled=true"), false, "found property endpoints.all.enabled"); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java index 9767f0dbed..4bb19d295d 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java @@ -161,7 +161,9 @@ public class WebEndpointManagementContextConfigurationTests { } private void beanIsAutoConfigured(Class beanType, Class... config) { - contextRunner().withUserConfiguration(config) + contextRunner() + .withPropertyValues("endpoints.all.web.enabled:true") + .withUserConfiguration(config) .run((context) -> assertThat(context).hasSingleBean(beanType)); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointCorsIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointCorsIntegrationTests.java index 03361f4d4e..7b33be03fd 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointCorsIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointCorsIntegrationTests.java @@ -65,6 +65,8 @@ public class MvcEndpointCorsIntegrationTests { EndpointInfrastructureAutoConfiguration.class, EndpointAutoConfiguration.class, ManagementContextAutoConfiguration.class, ServletEndpointAutoConfiguration.class); + TestPropertyValues.of("endpoints.all.web.enabled:true") + .applyTo(this.context); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebEndpointsRunner.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebEndpointsRunner.java index 43799b6fa0..dc6c15713b 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebEndpointsRunner.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebEndpointsRunner.java @@ -79,6 +79,8 @@ import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; * {@link org.springframework.core.env.Environment} are reset at the end of every test. * This means that {@link TestPropertyValues} can be used in a test without affecting the * {@code Environment} of other tests in the same class. + * The runner always sets the flag `endpoints.all.web.enabled` to true so that web endpoints + * are enabled. * * @author Andy Wilkinson */ @@ -190,6 +192,7 @@ public class WebEndpointsRunner extends Suite { private MvcWebEndpointsRunner(Class klass) throws InitializationError { super(klass, "Spring MVC", (classes) -> { AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); + TestPropertyValues.of("endpoints.all.web.enabled:true").applyTo(context); classes.add(MvcTestConfiguration.class); context.register(classes.toArray(new Class[classes.size()])); context.refresh(); @@ -221,6 +224,7 @@ public class WebEndpointsRunner extends Suite { private JerseyWebEndpointsRunner(Class klass) throws InitializationError { super(klass, "Jersey", (classes) -> { AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); + TestPropertyValues.of("endpoints.all.web.enabled:true").applyTo(context); classes.add(JerseyAppConfiguration.class); classes.add(JerseyInfrastructureConfiguration.class); context.register(classes.toArray(new Class[classes.size()])); @@ -260,6 +264,7 @@ public class WebEndpointsRunner extends Suite { private ReactiveWebEndpointsRunner(Class klass) throws InitializationError { super(klass, "Reactive", (classes) -> { ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext(); + TestPropertyValues.of("endpoints.all.web.enabled:true").applyTo(context); classes.add(ReactiveInfrastructureConfiguration.class); context.register(classes.toArray(new Class[classes.size()])); context.refresh(); diff --git a/spring-boot/src/main/java/org/springframework/boot/endpoint/EndpointType.java b/spring-boot/src/main/java/org/springframework/boot/endpoint/EndpointType.java index 657a3357a4..8567f273af 100644 --- a/spring-boot/src/main/java/org/springframework/boot/endpoint/EndpointType.java +++ b/spring-boot/src/main/java/org/springframework/boot/endpoint/EndpointType.java @@ -27,11 +27,20 @@ public enum EndpointType { /** * Expose the endpoint as a JMX MBean. */ - JMX, + JMX(true), /** * Expose the endpoint as a Web endpoint. */ - WEB + WEB(false); + private final boolean enabledByDefault; + + EndpointType(boolean enabledByDefault) { + this.enabledByDefault = enabledByDefault; + } + + public boolean isEnabledByDefault() { + return this.enabledByDefault; + } }