0

we have a spring boot project (2.3.0.RELEASE) with actuator endpoints and we are introducing keycloak to the project with KeycloakWebSecurityConfigurerAdapter how can I prevent actuator endpoints being secured by the keycloak filter chain.

We would like to have the "/actuator/**" endpoints secured by basic auth.

Currently we have a custom WebSecurityConfigurerAdapter with @Order(1) where we apply the basic auth to "/actuator/**" and then we have with @Order(2) antotated the KeycloakWebSecurityConfigurerAdapter

so 2 filter chains gets registered and when I call the actuator endpoints the second filter chain fails as unauthorised 401

is it possible to prevent handling the "/actuator/**" resorce path on the second filter chain?

First actuator security configuration.

@Configuration
@Order(1)
public class ActuatorWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final String username;
    private final String password;
    private final PasswordEncoder encoder;

    public ActuatorWebSecurityConfig(
            @Value("${spring.security.user.name}") String username,
            @Value("${spring.security.user.password}") String password,
            Optional<PasswordEncoder> encoder) {
        this.username = username;
        this.password = password;
        this.encoder = encoder.orElseGet(PasswordEncoderFactories::createDelegatingPasswordEncoder);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser(username)
                .password(encoder.encode(password))
                .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .antMatcher("/actuator/**")
                .authorizeRequests(authorize -> authorize.anyRequest().authenticated())
                .httpBasic(Customizer.withDefaults());
    }
}

second keycloak securoty configuration

@Order(2)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    private final String swaggerUrl;
    private final CorsFilter corsFilter;
    private final CustomSecurityConfig customSecurityConfig;

    @Autowired
    public SecurityConfig(
            @Value("${springdoc.swagger-ui.url:#{null}}") String swaggerUrl,
            CorsFilter corsFilter,
            CustomSecurityConfig customSecurityConfig) {
        this.swaggerUrl = swaggerUrl;
        this.corsFilter = corsFilter;
        this.customSecurityConfig = customSecurityConfig;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakProvider = keycloakAuthenticationProvider();
        keycloakProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable()
            .requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/actuator/**")));
            .headers().frameOptions().disable()
        .and()
           .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
           .antMatchers("/public/**", "/resources/**", "/resources/public/**").permitAll()
           .antMatchers(OPTIONS, "/**").permitAll();
        .authorizeRequests()
           .antMatchers("/**")
           .authenticated();
    }
}

I have tried with on keycloak config

.antMatchers("/actuator/**").permitAll();

and with

http.requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/actuator/**")));

but nothing works I receive unauthorised 401 for actuator

the registered filter chains :

2022-01-18 17:38:44,688 INFO org.springframework.security.web.DefaultSecurityFilterChain [main] Creating filter chain: Ant [pattern='/actuator/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@25c6a9de, org.springframework.security.web.context.SecurityContextPersistenceFilter@56f3f9da, org.springframework.security.web.header.HeaderWriterFilter@33dcbdc2, org.springframework.security.web.csrf.CsrfFilter@522fdf0c, org.springframework.security.web.authentication.logout.LogoutFilter@365ad794, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@23df16cf, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@227cba85, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@b38dc7d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@142422a4, org.springframework.security.web.session.SessionManagementFilter@2f0b7b6d, org.springframework.security.web.access.ExceptionTranslationFilter@74bca236, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@30587737]
2022-01-18 17:38:44,691 INFO org.springframework.security.web.DefaultSecurityFilterChain [main] Creating filter chain: NegatedRequestMatcher [requestMatcher=Ant [pattern='/actuator/**']], [com.betex.auth.filters.CorsFilter@20a9f5fb, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@10e28d97, org.springframework.security.web.context.SecurityContextPersistenceFilter@c6b08a5, org.springframework.security.web.header.HeaderWriterFilter@5f05cd7e, org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter@2a54c92e, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter@55b62db8, org.springframework.security.web.authentication.logout.LogoutFilter@274f51ad, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@54980154, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@25874884, org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter@8cb7185, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter@4dac40b, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@37d43b9b, org.springframework.security.web.session.SessionManagementFilter@11e8e183, org.springframework.security.web.access.ExceptionTranslationFilter@56f1db5f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@78543f0d]
Jonas
  • 121,568
  • 97
  • 310
  • 388
simonC
  • 4,101
  • 10
  • 50
  • 78
  • Did you try debugging the `FilterChainProxy#doFilterInternal` to see which filter chain is being called? Also, I recommend adding `logging.level.org.springframework.security=TRACE` to your `application.properties` file and know exactly who is denying you access – Marcus Hert da Coregio Jan 18 '22 at 14:44
  • @MarcusHertdaCoregio yes the KeycloakAuthenticationProcessingFilter is not passing with Authorization header not found, see WWW-Authenticate header – simonC Jan 18 '22 at 14:58
  • here you can read the answer i have provided how to exclude a path completly from spring security https://stackoverflow.com/questions/70717704/how-to-disable-security-just-for-actuator-health/70719136#70719136 you could try that out – Toerktumlare Jan 18 '22 at 15:39
  • @Toerktumlare ths but is not working KeycloakAuthenticationProcessingFilter is called regardless of which filter chain is mapped to the request which is strange – simonC Jan 18 '22 at 16:02
  • @MarcusHertdaCoregio even If I have two different filter chains the fist one does not have KeycloakAuthenticationProcessingFilter in chain but is caller regardless of that ... looks that it is registered in internal filter list that is applied via VirtualFilterChain ... any way to avoid this? – simonC Jan 18 '22 at 16:18

1 Answers1

1

When you extend KeycloakWebSecurityConfigurerAdapter, the adapter register a Bean of type KeycloakAuthenticationProcessingFilter. This filter is registered in the Spring Security's SecurityFilterChain, and because it's a Bean, it is also automatically registered by Spring Boot in the original chain, therefore even if Spring Security doesn't apply it, it will be applied later on in original the filter chain.

Try disabling this filter from being registered by Spring Boot, like so:

@Bean
public FilterRegistrationBean registration(KeycloakAuthenticationProcessingFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}

In addition, if you are using OAuth 2, you may consider using spring-security-oauth2-resource-server and simplifying your Resource Server's configuration. Take a look at the documentation. This way you don't need to extend the custom adapter, just rely on the out-of-the-box configuration from Spring Security.

  • Marcus thnx for the info looks working, I understand that avoiding KeycloakWebSecurityConfigurerAdapter can be interesting aso to avoid the additional dependency to keycloak, but what do you loose with this, probably KeycloakWebSecurityConfigurerAdapter adds some additional stuff that is maybe good to have or not? We use OAuth2. – simonC Jan 19 '22 at 08:00
  • If you are using nothing special from the adapter, you should lose nothing. It will behave the same, receive the Bearer token, extract it, and validate it using the JWKs returned from the `issuer-uri` that you configured. – Marcus Hert da Coregio Jan 19 '22 at 11:10