Skip to content

Latest commit

 

History

History
226 lines (179 loc) · 8.21 KB

File metadata and controls

226 lines (179 loc) · 8.21 KB
sidebar_custom_props
icon
shield

Foster Security

Since there are several approaches on solving authentication and authorization in distributed web applications Spring Boot Admin doesn't ship a default one. By default spring-boot-admin-server-ui provides a login page and a logout button.

A Spring Security configuration for your server could look like this:

import org.springframework.http.HttpMethod;

@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig {

	private final AdminServerProperties adminServer;

	private final SecurityProperties security;

	public SecuritySecureConfig(AdminServerProperties adminServer, SecurityProperties security) {
		this.adminServer = adminServer;
		this.security = security;
	}

	@Bean
	protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
		successHandler.setTargetUrlParameter("redirectTo");
		successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

		http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests //
						.requestMatchers(PathPatternRequestMatcher.withDefaults()
								.matcher(this.adminServer.path("/assets/**")))
						.permitAll() // (1)
						.requestMatchers(PathPatternRequestMatcher.withDefaults()
								.matcher(this.adminServer.path("/actuator/info")))
						.permitAll()
						.requestMatchers(PathPatternRequestMatcher.withDefaults()
								.matcher(this.adminServer.path("/actuator/health")))
						.permitAll()
						.requestMatchers(PathPatternRequestMatcher.withDefaults()
								.matcher(this.adminServer.path("/login")))
						.permitAll()
						.dispatcherTypeMatchers(DispatcherType.ASYNC)
						.permitAll() // https://github.com/spring-projects/spring-security/issues/11027
						.anyRequest()
						.authenticated()) // (2)
				.formLogin(
						(formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler)) // (3)
				.logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
				.httpBasic(Customizer.withDefaults()); // (4)

		http.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class) // (5)
				.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
						.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
						.ignoringRequestMatchers(
								PathPatternRequestMatcher.withDefaults()
										.matcher(HttpMethod.POST, this.adminServer.path("/instances")), // (6)
								PathPatternRequestMatcher.withDefaults()
										.matcher(HttpMethod.DELETE, this.adminServer.path("/instances/*")), // (6)
								PathPatternRequestMatcher.withDefaults()
										.matcher(this.adminServer.path("/actuator/**")) // (7)
						));

		http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));

		return http.build();

	}

	// Required to provide UserDetailsService for "remember functionality"
	@Bean
	public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
		UserDetails user = User.withUsername("user").password(passwordEncoder.encode("password")).roles("USER").build();
		return new InMemoryUserDetailsManager(user);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}
  1. Grants public access to all static assets and the login page.
  2. Every other request must be authenticated.
  3. Configures login and logout.
  4. Enables HTTP-Basic support. This is needed for the Spring Boot Admin Client to register.
  5. Enables CSRF-Protection using Cookies
  6. Disables CSRF-Protection for the endpoint the Spring Boot Admin Client uses to (de-)register.
  7. Disables CSRF-Protection for the actuator endpoints.

In case you use the Spring Boot Admin Client, it needs the credentials for accessing the server:

spring.boot.admin.client:
  username: sba-client
  password: s3cret

For a complete sample look at spring-boot-admin-sample-servlet.

:::note If you protect the /instances endpoint don't forget to configure the username and password on your SBA-Client using spring.boot.admin.client.username and spring.boot.admin.client.password. :::

Securing Client Actuator Endpoints

When the actuator endpoints are secured using HTTP Basic authentication the SBA Server needs credentials to access them. You can submit the credentials in the metadata when registering the application. The BasicAuthHttpHeaderProvider then uses this metadata to add the Authorization header to access your application's actuator endpoints. You can provide your own HttpHeadersProvider to alter the behaviour (e.g. add some decryption) or add extra headers.

:::note The SBA Server masks certain metadata in the HTTP interface to prevent leaking of sensitive information. :::

:::warning You should configure HTTPS for your SBA Server or (service registry) when transferring credentials via the metadata. :::

:::warning When using Spring Cloud Discovery, you must be aware that anybody who can query your service registry can obtain the credentials. :::

:::tip When using this approach the SBA Server decides whether the user can access the registered applications. There are more complex solutions possible (using OAuth2) to let the clients decide if the user can access the endpoints. For that please have a look at the samples in joshiste/spring-boot-admin-samples. :::

SBA Client

spring.boot.admin.client:
  url: http://localhost:8080
  instance:
    metadata:
      user.name: ${spring.security.user.name}
      user.password: ${spring.security.user.password}

SBA Server

You can specify credentials via configuration properties in your admin server.

:::tip You can use this in conjunction with spring-cloud-kubernetes to pull credentials from secrets. :::

To enable pulling credentials from properties the spring.boot.admin.instance-auth.enabled property must be true ( default).

:::note If your clients provide credentials via metadata (i.e., via service annotations), that metadata will be used instead of the properties. :::

You can provide a default username and password by setting spring.boot.admin.instance-auth.default-user-name and spring.boot.admin.instance-auth.default-user-password. Optionally you can provide credentials for specific services ( by name) using the spring.boot.admin.instance-auth.service-map.*.user-name pattern, replacing * with the service name.

spring.boot.admin:
  instance-auth:
    enabled: true
    default-user-name: "${some.user.name.from.secret}"
    default-password: "${some.user.password.from.secret}"
    service-map:
      my-first-service-to-monitor:
        user-name: "${some.user.name.from.secret}"
        user-password: "${some.user.password.from.secret}"
      my-second-service-to-monitor:
        user-name: "${some.user.name.from.secret}"
        user-password: "${some.user.password.from.secret}"

Eureka

eureka:
  instance:
    metadata-map:
      user.name: ${spring.security.user.name}
      user.password: ${spring.security.user.password}

Consul

spring.cloud.consul:
  discovery:
    metadata:
      user-name: ${spring.security.user.name}
      user-password: ${spring.security.user.password}

:::warning Consul does not allow dots (".") in metadata keys, use dashes instead. :::

CSRF on Actuator Endpoints

Some of the actuator endpoints (e.g. /loggers) support POST requests. When using Spring Security you need to ignore the actuator endpoints for CSRF-Protection as the Spring Boot Admin Server currently lacks support.

@Bean
private SecurityFilterChain filterChain(HttpSecurity http) {
	return http.csrf(c -> c.ignoringRequestMatchers("/actuator/**")).build();
}