Commit a899e1ca authored by Dave Syer's avatar Dave Syer

ManagementServerConfiguration security

Management endpoints are still secure by default if
Spring Security is present, but now the default
user details have an ADMIN role, and a random password
(which is logged at INFO level if not overridden).

To override you add management.user.password (name, role)
to external properties.

[Fixes #53029715] [bs-203]
parent 8f3c4808
......@@ -20,9 +20,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.actuate.properties.ManagementServerProperties.User;
import org.springframework.boot.actuate.properties.SecurityProperties;
import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -43,6 +47,7 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
......@@ -112,6 +117,9 @@ public class SecurityAutoConfiguration {
@Autowired
private SecurityProperties security;
@Autowired
private ManagementServerProperties management;
@Autowired(required = false)
private EndpointHandlerMapping endpointHandlerMapping;
......@@ -129,19 +137,23 @@ public class SecurityAutoConfiguration {
}
if (this.security.getBasic().isEnabled()) {
String[] paths = getSecurePaths();
http.exceptionHandling().authenticationEntryPoint(entryPoint()).and()
.requestMatchers().antMatchers(paths);
http.exceptionHandling().authenticationEntryPoint(entryPoint());
http.httpBasic().and().anonymous().disable();
http.authorizeUrls().anyRequest()
.hasRole(this.security.getBasic().getRole());
ExpressionUrlAuthorizationConfigurer<HttpSecurity> authorizeUrls = http
.authorizeUrls();
if (getEndpointPaths(true).length > 0) {
authorizeUrls.antMatchers(getEndpointPaths(true)).hasRole(
this.management.getUser().getRole());
}
authorizeUrls.antMatchers(getSecureApplicationPaths())
.hasRole(this.security.getBasic().getRole()).and().httpBasic();
}
// No cookies for service endpoints by default
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
}
private String[] getSecurePaths() {
private String[] getSecureApplicationPaths() {
List<String> list = new ArrayList<String>();
for (String path : this.security.getBasic().getPath()) {
path = (path == null ? "" : path.trim());
......@@ -203,11 +215,26 @@ public class SecurityAutoConfiguration {
@Configuration
public static class AuthenticationManagerConfiguration {
private static Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class);
@Autowired
private ManagementServerProperties management;
@Bean
public AuthenticationManager authenticationManager() throws Exception {
User user = this.management.getUser();
if (user.isDefaultPassword()) {
logger.info("Using default password for ");
}
List<String> roles = new ArrayList<String>();
roles.add("USER");
if (!"USER".equals(user.getRole())) {
roles.add(user.getRole());
}
return new AuthenticationManagerBuilder().inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and().and()
.build();
.withUser(user.getName()).password(user.getPassword())
.roles(roles.toArray(new String[roles.size()])).and().and().build();
}
}
......
......@@ -17,6 +17,7 @@
package org.springframework.boot.actuate.properties;
import java.net.InetAddress;
import java.util.UUID;
import javax.validation.constraints.NotNull;
......@@ -39,8 +40,14 @@ public class ManagementServerProperties {
@NotNull
private String contextPath = "";
private User user = new User();
private boolean allowShutdown = false;
public User getUser() {
return this.user;
}
public boolean isAllowShutdown() {
return this.allowShutdown;
}
......@@ -82,4 +89,45 @@ public class ManagementServerProperties {
this.contextPath = contextPath;
}
public static class User {
private String name = "user";
private String password = UUID.randomUUID().toString();
private String role = "ADMIN";
private boolean defaultPassword;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.defaultPassword = false;
this.password = password;
}
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
public boolean isDefaultPassword() {
return this.defaultPassword;
}
}
}
......@@ -17,8 +17,6 @@
package org.springframework.boot.actuate.autoconfigure;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -47,6 +45,7 @@ public class SecurityAutoConfigurationTests {
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityAutoConfiguration.class,
EndpointAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManager.class));
......@@ -58,6 +57,7 @@ public class SecurityAutoConfigurationTests {
this.context.setServletContext(new MockServletContext());
this.context.register(TestConfiguration.class, SecurityAutoConfiguration.class,
EndpointAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager,
......
......@@ -30,7 +30,7 @@ import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.ops.SampleActuatorApplication;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......@@ -84,7 +84,7 @@ public class ManagementAddressSampleActuatorApplicationTests {
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:" + port, Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -126,6 +126,10 @@ public class ManagementAddressSampleActuatorApplicationTests {
assertEquals(999, body.get("status"));
}
private String getPassword() {
return context.getBean(ManagementServerProperties.class).getUser().getPassword();
}
private RestTemplate getRestTemplate() {
return getRestTemplate(null, null);
}
......
......@@ -29,7 +29,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.ops.SampleActuatorApplication;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......@@ -82,7 +82,7 @@ public class NoManagementSampleActuatorApplicationTests {
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080", Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -94,11 +94,15 @@ public class NoManagementSampleActuatorApplicationTests {
public void testMetricsNotAvailable() throws Exception {
testHome(); // makes sure some requests have been made
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:" + managementPort + "/metrics", Map.class);
assertEquals(HttpStatus.NOT_FOUND, entity.getStatusCode());
}
private String getPassword() {
return context.getBean(ManagementServerProperties.class).getUser().getPassword();
}
private RestTemplate getRestTemplate(final String username, final String password) {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
......
......@@ -29,7 +29,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.ops.SampleActuatorApplication;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......@@ -92,7 +92,7 @@ public class SampleActuatorApplicationTests {
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080", Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -104,7 +104,7 @@ public class SampleActuatorApplicationTests {
public void testMetrics() throws Exception {
testHome(); // makes sure some requests have been made
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080/metrics", Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -115,7 +115,7 @@ public class SampleActuatorApplicationTests {
@Test
public void testEnv() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080/env", Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -134,7 +134,7 @@ public class SampleActuatorApplicationTests {
@Test
public void testErrorPage() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080/foo", Map.class);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -157,8 +157,8 @@ public class SampleActuatorApplicationTests {
@Test
public void testBeans() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<List> entity = getRestTemplate("user", "password").getForEntity(
"http://localhost:8080/beans", List.class);
ResponseEntity<List> entity = getRestTemplate("user", getPassword())
.getForEntity("http://localhost:8080/beans", List.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals(1, entity.getBody().size());
@SuppressWarnings("unchecked")
......@@ -167,6 +167,10 @@ public class SampleActuatorApplicationTests {
((String) body.get("context")).startsWith("application"));
}
private String getPassword() {
return context.getBean(ManagementServerProperties.class).getUser().getPassword();
}
private RestTemplate getRestTemplate() {
return getRestTemplate(null, null);
}
......
......@@ -29,7 +29,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.ops.SampleActuatorApplication;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......@@ -79,7 +79,7 @@ public class ShutdownSampleActuatorApplicationTests {
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
ResponseEntity<Map> entity = getRestTemplate("user", getPassword()).getForEntity(
"http://localhost:8080", Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
......@@ -90,8 +90,8 @@ public class ShutdownSampleActuatorApplicationTests {
@Test
public void testShutdown() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate("user", "password").postForEntity(
"http://localhost:8080/shutdown", null, Map.class);
ResponseEntity<Map> entity = getRestTemplate("user", getPassword())
.postForEntity("http://localhost:8080/shutdown", null, Map.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
......@@ -99,6 +99,10 @@ public class ShutdownSampleActuatorApplicationTests {
((String) body.get("message")).contains("Shutting down"));
}
private String getPassword() {
return context.getBean(ManagementServerProperties.class).getUser().getPassword();
}
private RestTemplate getRestTemplate(final String username, final String password) {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
......
......@@ -29,7 +29,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.sample.tomcat.SampleTomcatApplication;
import org.springframework.boot.sample.tomcat.service.HelloWorldService;
import org.springframework.boot.sample.tomcat.web.SampleController;
import org.springframework.context.ConfigurableApplicationContext;
......@@ -76,7 +75,7 @@ public class NonAutoConfigurationSampleTomcatApplicationTests {
.run(NonAutoConfigurationSampleTomcatApplication.class);
}
});
context = future.get(10, TimeUnit.SECONDS);
context = future.get(60, TimeUnit.SECONDS);
}
@AfterClass
......
......@@ -26,7 +26,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.tomcat.SampleTomcatApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -56,7 +55,7 @@ public class SampleTomcatApplicationTests {
.run(SampleTomcatApplication.class);
}
});
context = future.get(10, TimeUnit.SECONDS);
context = future.get(60, TimeUnit.SECONDS);
}
@AfterClass
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment