0
votes

I have a project with WebFlux, Health actuator, and Spring security. I am trying to build custom authentication, but that authentication kicks in on health actuator endpoints as well. How can I disable it?

According to docs I implemented a custom ServerSecurityContextRepository, here is basic version of what it looks somewhat like:

@Component
class MySecurityContextRepository: ServerSecurityContextRepository {
    override fun save(exchange: ServerWebExchange?, context: SecurityContext?) = Mono.empty()

    override fun load(exchange: ServerWebExchange) = Mono.error(ResponseStatusException(HttpStatus.UNAUTHORIZED, "Access denied"))
}

According to documentation I should not be required to do any additional configuration in order to disable auth on health endpoints. Here is configuration from application.yml:

management:
  metrics:
        web:
          server:
            auto-time-requests: true
            requests-metric-name: xxx-xxx-xxx
        export:
          statsd:
            enabled: xxxx
            host: xxxxxxxxxxxx
            flavor: xxx
  endpoint:
      health:
        enabled: true
  endpoints:
      web:
          base-path: /application

That did not work as I saw 401 from /application/health endpoint. So I also added it to my security configuration:

@EnableWebFluxSecurity
class SecurityConfig @Autowired constructor(
        private val myRepository: MySecurityContextRepository
) {
    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http.securityContextRepository(myRepository)
        http.authorizeExchange()
                .pathMatchers("/application/health").permitAll()
                .anyExchange().permitAll()

        http.cors().disable()
        http.csrf().disable()
        http.formLogin().disable()

        return http.build()
    }
}

Despite adding this doing curl http://localhost:8080/application/health/ results in {"name":"xxxxxx","message":"Unknown Error","response":"401 UNAUTHORIZED \"Access denied\""} and status code is 401 as well. How can disable authorization for my health endpoint?

2

2 Answers

3
votes

So after no help I started looking at the source code and turns out ServerSecurityContextRepository is always invoked for HealthEndpoint and InfoEndpoint so I added the logic to skip the authentication check in repository myself. You can easily do:

val HEALTH_ENDPOINTS = EndpointRequest.to(HealthEndpoint::class.java, InfoEndpoint::class.java)

Now you can do something like:

            return HEALTH_ENDPOINTS.matches(exchange).flatMap { matches ->
                if (matches.isMatch) {
                    Mono.just(SecurityContextImpl(GuestAuthToken))
                } else {
                    Mono.empty()
                }
            }

That way I am not hardcoding any paths in my repository. It's not ideal but gets the job done.

0
votes

Regarding spring docu 29.2 WebFlux Security you need just to add:

import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
public class WebFluxSecurityConfigurer {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange()
                .pathMatchers("/status/**")
                .permitAll().and()
                .build();
    }
}

Don't forget to add all possible paths like ../health and ../health/ or even better with wildcards