Search code examples
javaspringspring-securityspring-boot-actuator

How to handle actuator and server with spring security on 2 different ports using base path?


I have this setup

# Server
server.servlet.contextPath=/myapp/api
server.port=8080

# Actuator port
management.health.probes.enabled=true
management.server.port=8090
management.endpoints.web.base-path=/myapp/api/actuator
management.metrics.export.prometheus.enabled=true

With simple authorization like this

@Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeHttpRequests()
            .requestMatchers(HttpMethod.GET, "/actuator/health").permitAll() # Worked before when everything was on port 8080. Still works but with token
            .requestMatchers(HttpMethod.GET, "/myapp/api/actuator/health").permitAll() # Worked when actuator was on different port without token
            .requestMatchers(HttpMethod.GET, "/vehicles/**").permitAll() 
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(jwtAuthenticationConverter())
        return http.build()
    }

Before i used to run everything on port 8080. Now i need to run logs on a secondary port. Both must have base path beginning with /myapp/api/. Whats the best practice way to do this?


Solution

  • you can make use of two separate SecurityConfigurerAdapter instances for each port:

    @Configuration
    @EnableWebSecurity
    class SecurityConfig {
    
        @Bean
        fun actuatorSecurityConfigurerAdapter(): SecurityConfigurerAdapter {
            return object : SecurityConfigurerAdapter() {
                override fun configure(http: HttpSecurity) {
                    http.antMatcher("/myapp/api/actuator/**")
                        .authorizeRequests {
                            it.antMatchers(HttpMethod.GET, "/myapp/api/actuator/health").permitAll()
                            // Other actuator endpoints can be configured here
                        }
                        .anyRequest().authenticated()
                        .and()
                        .oauth2ResourceServer()
                        .jwt()
                        .jwtAuthenticationConverter(jwtAuthenticationConverter())
                }
            }
        }
    
        @Bean
        fun appSecurityConfigurerAdapter(): SecurityConfigurerAdapter {
            return object : SecurityConfigurerAdapter() {
                override fun configure(http: HttpSecurity) {
                    http.antMatcher("/myapp/api/**")
                        .authorizeRequests {
                            it.antMatchers(HttpMethod.GET, "/myapp/api/vehicles/**").permitAll()
                            // Other application endpoints can be configured here
                        }
                        .anyRequest().authenticated()
                        .and()
                        .oauth2ResourceServer()
                        .jwt()
                        .jwtAuthenticationConverter(jwtAuthenticationConverter())
                }
            }
        }
    
        @Bean
        fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
            http.csrf().disable() // Disable CSRF for simplicity
            http.apply(actuatorSecurityConfigurerAdapter())
            http.apply(appSecurityConfigurerAdapter())
            return http.build()
        }
    

    actuatorSecurityConfigurerAdapter configures security for the actuator endpoints, and appSecurityConfigurerAdapter configures security for your application endpoints. The securityFilterChain bean applies both configurations to the overall security setup.

    This way, you can have different security configurations for the actuator and application endpoints, and they will be applied based on the specified base paths.