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]
This commit is contained in:
parent
c582fa2067
commit
621116c9b8
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue