14

This is my Spring Boot 1.5.1 Actuator application.properties:

#Spring Boot Actuator
management.contextPath: /actuator
management.security.roles=R_0

This is my WebSecurityConfig:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${logout.success.url}")
    private String logoutSuccessUrl;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

        http
            .csrf().ignoringAntMatchers("/v1.0/**", "/logout")
        .and()
            .authorizeRequests()

            .antMatchers("/oauth/authorize").authenticated()
            //Anyone can access the urls
            .antMatchers("/signin/**").permitAll()
            .antMatchers("/v1.0/**").permitAll()
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/actuator/**").hasAuthority("R_0")
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .failureUrl("/login?error=true")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
            .and()
                .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessUrl(logoutSuccessUrl)
                    .permitAll();
        // @formatter:on
    }

    /**
     * Configures the authentication manager bean which processes authentication requests.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

Right now I'm successfully able to login in my application with a right user that has R_0 authorities but when I trying to access for example

http://localhost:8080/api/actuator/beans

I receive a following error:

There was an unexpected error (type=Forbidden, status=403).
Access is denied. User must have one of the these roles: R_0

How to correctly configure Spring Boot Actuator in order to be aware about the correct Authentication ?

Right now in order to get it workin I have to do the following trick:

management.security.enabled=false

.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority("R_0")

Is any chance to configure Actuator in a right way ?

UPDATED

I'm using UserDetailsService.UserDetails.Authorities

    public Collection<? extends GrantedAuthority> getAuthorities() {
        String[] authorities = permissions.stream().map(p -> {
            return p.getName();
        }).toArray(String[]::new);
        return AuthorityUtils.createAuthorityList(authorities);
    }
alexanoid
  • 19,599
  • 35
  • 160
  • 321
  • 1
    Thanks, but I'm able to manage this with `.antMatchers("/actuator/**").hasAuthority("R_0")`. The issue is that I'm unable to get working the default Spring Boot Actuator security integration with no needs to provide a custom configs like a this one. – alexanoid Feb 09 '17 at 17:45
  • I'm having the same issue. Noticed that by setting a `UserDetailsService` on `AuthenticationManagerBuilder` somehow overrides the default security config meaning a need for an explicit URI access configuration like you did via `HttpSecurity` class. Have you managed to make it work as you originally expected? – RZet Mar 23 '17 at 18:07
  • Yes, you have to use prefix `ROLE_` for your `management.security.roles` for example `management.security.roles=ROLE_SOMENAME` – alexanoid Mar 23 '17 at 18:09
  • Since I'm on Spring Boot 1.3.5, my problem was related to plural vs singular format of this property. For my version you can only define one role as, e.g. `management.security.role=ACTUATOR`, which was changed in versions 1.4.x to the plural equivalent. Besides, I combined two sources for `UserDetails` retrieval for `AuthenticationManagerBuilder` class, one is an implementation of `UserDetailsService` for my service users and the second is an instance of `InMemoryUserDetailsManagerConfigurer` for actuator and admin users (you can get away with only one too). – RZet Mar 24 '17 at 16:22

6 Answers6

6

You have to use prefix ROLE_ for your management.security.roles for example management.security.roles=ROLE_SOMENAME in order to solve this issue

alexanoid
  • 19,599
  • 35
  • 160
  • 321
  • I thought in Spring Boot 1.5.1 the values of `management.security.roles` property shouldn't be prefixed with `ROLE_` anymore. I found a lot of confusion concerning this subject. I believe your problem could also be fixed by modifying the following `HttpSecurity` settings `.antMatchers("/actuator/**").hasAuthority("ROLE_R_0")` instead. – RZet Mar 24 '17 at 16:02
  • antMatcher doesn't solve the problem. For examle actuator/health endpoint returns different data for authorized and not authorized user. It is impossible to implement based on the provided antMatcher configuration – alexanoid Mar 24 '17 at 16:26
  • The behaviour you're talking about is documented in section 48.7 of http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html. Your original question was about 403 status code for `/actuator/beans` call. I believe it can be fixed by removing both `antMatchers("/actuator...")` settings and ensuring the `UserDetailsService` returns a `UserDetails` object containing `GrantedAuthority` set to `ROLE_R_0` in your case. Alternatively, you could combine it with `InMemoryUserDetailsManagerConfigurer` as I mentioned in the comment to the main question. – RZet Mar 24 '17 at 18:11
  • It doesn't work because Spring Boot Actuator doesn't understand authorities/roles without _ROLE prefix. Also antMatchers doesn't help here because in order to use it I have to disable management.security. With disabling this property I'll lose Actuator functionality like I have described above so your solution completely doesn't have any sense to me. – alexanoid Mar 24 '17 at 18:18
  • for more details refer the following github issue https://github.com/spring-projects/spring-boot/issues/8255 – alexanoid Mar 24 '17 at 18:20
0

To have authorization to spring boot actuator endpoints you need to have ACTUATOR role. Refer this example Accessing Restricted Actuator Endpoints with Spring Security

Dhiraj Ray
  • 617
  • 6
  • 11
0

I'm coming at this from a Reactive Spring Boot 2.x app and had this problem and solved it by updating the WebSecurityConfig.securityWebFilterChain as well as SecurityContextRepository.load to include /actuator/** as follows:

public class WebSecurityConfig {
  private AuthenticationManager authenticationManager;

  private SecurityContextRepository securityContextRepository;

  @Autowired
  public WebSecurityConfig(AuthenticationManager authenticationManager, SecurityContextRepository securityContextRepository) {
    this.authenticationManager = authenticationManager;
    this.securityContextRepository = securityContextRepository;
  }

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http
      .exceptionHandling()
      .authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> {
        swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      })).accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> {
        swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
      })).and()
      .csrf().disable()
      .formLogin().disable()
      .httpBasic().disable()
      .authenticationManager(authenticationManager)
      .securityContextRepository(securityContextRepository)
      .authorizeExchange()
      .pathMatchers("/actuator/**").permitAll()
      .anyExchange().authenticated()
      .and().build();
  }

as well as updating

@Slf4j
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {

  private AuthenticationManager authenticationManager;

  public SecurityContextRepository(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
  }

  @Override
  public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
    return Mono.error(new UnsupportedOperationException("Not supported"));
  }

  @Override
  public Mono<SecurityContext> load(ServerWebExchange swe) {
    ServerHttpRequest request = swe.getRequest();

    if (request.getPath().value().startsWith("/actuator") ) {
      return Mono.empty();
    }
    // other authentication logic here
  }
Mitch1077487
  • 701
  • 6
  • 8
0
1
1.You can add the following properties
endpoints.health.enabled=true
endpoints.loggers.enabled=true
endpoints.metrics.enabled=true

endpoints.env.enabled=false
endpoints.configprops.enabled=false
endpoints.autoconfig.enabled=false
endpoints.info.enabled=false

management.context-path=/management

2)
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {

    private static final String ACTUATOR = "ACTUATOR";
    private static final String ROLE1 = "USER";

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
                .hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()

                .csrf().disable().headers().frameOptions().disable();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("username-of-app").password("encryptedpassword")
                .roles(ROLE1).and().withUser("username-of-management-for-path")
                .password("encrypted password").roles(ACTUATOR);

    }
}
0
  1. You can add the following properties
    endpoints.health.enabled=true
    endpoints.loggers.enabled=true
    endpoints.metrics.enabled=true
    
    endpoints.env.enabled=false
    endpoints.configprops.enabled=false
    endpoints.autoconfig.enabled=false
    endpoints.info.enabled=false
    
    management.context-path=/management
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
    
        private static final String ACTUATOR = "ACTUATOR";
        private static final String ROLE1 = "USER";
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
                    .hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()
    
                    .csrf().disable().headers().frameOptions().disable();
    
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("username-of-app").password("encryptedpassword")
                    .roles(ROLE1).and().withUser("username-of-management-for-path")
                    .password("encrypted password").roles(ACTUATOR);
    
        }
    }
Suraj Rao
  • 28,186
  • 10
  • 88
  • 94
-1

According to this link:

http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html

By default all sensitive HTTP endpoints are secured such that only users that have an ACTUATOR role may access them. Security is enforced using the standard HttpServletRequest.isUserInRole method.

Use the management.security.roles property if you want something different to ACTUATOR.

So I think all you have to do is set the following property in application.properties.

management.security.roles

Ex:

management.security.roles=R_0
mad_fox
  • 2,578
  • 4
  • 24
  • 36
  • Thanks, but please take a look onto my question. I have already provided `management.security.roles=R_0` The issue is that Spring Boot Actuator don't understand how to work with a current Spring Security for a some reason. – alexanoid Feb 10 '17 at 04:45