diff --git a/src/main/java/com/project/common/security/filter/JwtAuthenticationFilter.java b/src/main/java/com/project/common/security/filter/JwtAuthenticationFilter.java index 7838b97..72a5387 100644 --- a/src/main/java/com/project/common/security/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/project/common/security/filter/JwtAuthenticationFilter.java @@ -30,6 +30,7 @@ import static com.project.common.util.WebSecurityUrl.getHealthCheckEndpoints; import static com.project.common.util.WebSecurityUrl.getReadOnlyPublicEndpoints; +import static com.project.common.util.WebSecurityUrl.getActuatorEndpoints; import static com.project.common.util.WebSecurityUrl.getAnonymousEndpoints; @Slf4j @@ -44,6 +45,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { protected static final String[] PUBLIC_ENDPOINTS = Stream.of( getHealthCheckEndpoints(), + getActuatorEndpoints(), getReadOnlyPublicEndpoints() ).flatMap(Arrays::stream).toArray(String[]::new); diff --git a/src/main/java/com/project/common/util/WebSecurityUrl.java b/src/main/java/com/project/common/util/WebSecurityUrl.java index dc15b21..823f7ca 100644 --- a/src/main/java/com/project/common/util/WebSecurityUrl.java +++ b/src/main/java/com/project/common/util/WebSecurityUrl.java @@ -11,6 +11,7 @@ private WebSecurityUrl() { public static final String LOCAL_LOGIN_ENDPOINT = "/oauth/local/login"; public static final String LOCAL_SIGN_ENDPOINT = "/oauth/local/sign"; private static final String[] HEALTH_CHECK_ENDPOINTS = {"/health", "/actuator/health"}; + private static final String[] ACTUATOR_ENDPOINTS = {"/actuator/prometheus", "/actuator/metrics", "/actuator/info"}; private static final String[] READ_ONLY_PUBLIC_ENDPOINTS = {"/favicon.ico"}; private static final String[] ANONYMOUS_ENDPOINTS = { LOGIN_ENDPOINT, REISSUE_ENDPOINT, LOCAL_LOGIN_ENDPOINT, LOCAL_SIGN_ENDPOINT}; @@ -19,6 +20,10 @@ public static String[] getHealthCheckEndpoints() { return HEALTH_CHECK_ENDPOINTS.clone(); } + public static String[] getActuatorEndpoints() { + return ACTUATOR_ENDPOINTS.clone(); + } + public static String[] getReadOnlyPublicEndpoints() { return READ_ONLY_PUBLIC_ENDPOINTS.clone(); } diff --git a/src/main/java/com/project/config/security/ActuatorAuthorizationManager.java b/src/main/java/com/project/config/security/ActuatorAuthorizationManager.java new file mode 100644 index 0000000..ec62559 --- /dev/null +++ b/src/main/java/com/project/config/security/ActuatorAuthorizationManager.java @@ -0,0 +1,35 @@ +package com.project.config.security; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.stereotype.Component; + +import java.util.function.Supplier; + +@Component +public class ActuatorAuthorizationManager implements AuthorizationManager { + + private final String monitoringHost; + + public ActuatorAuthorizationManager(@Value("${LOKI_URI:localhost}") String monitoringHost) { + this.monitoringHost = monitoringHost; + } + + @Override + public AuthorizationDecision check(Supplier authenticationSupplier, RequestAuthorizationContext context) { + HttpServletRequest request = context.getRequest(); + String remoteAddr = request.getRemoteAddr(); + + boolean isAllowed = isLocalhost(remoteAddr) || monitoringHost.equals(remoteAddr); + + return new AuthorizationDecision(isAllowed); + } + + private boolean isLocalhost(String ip) { + return "127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || "localhost".equals(ip); + } +} diff --git a/src/main/java/com/project/config/security/SecurityConfig.java b/src/main/java/com/project/config/security/SecurityConfig.java index 9a32c75..b0da54a 100644 --- a/src/main/java/com/project/config/security/SecurityConfig.java +++ b/src/main/java/com/project/config/security/SecurityConfig.java @@ -18,6 +18,7 @@ import static com.project.common.util.WebSecurityUrl.getReadOnlyPublicEndpoints; import static com.project.common.util.WebSecurityUrl.getHealthCheckEndpoints; +import static com.project.common.util.WebSecurityUrl.getActuatorEndpoints; import static com.project.common.util.WebSecurityUrl.LOGIN_ENDPOINT; import static com.project.common.util.WebSecurityUrl.REISSUE_ENDPOINT; import static com.project.common.util.WebSecurityUrl.LOCAL_LOGIN_ENDPOINT; @@ -33,6 +34,7 @@ public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; private final AccessDeniedHandler accessDeniedHandler; private final AuthenticationEntryPoint authenticationEntryPoint; + private final ActuatorAuthorizationManager actuatorAuthorizationManager; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -55,6 +57,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers("/slack/**").permitAll() .requestMatchers(HttpMethod.GET, getReadOnlyPublicEndpoints()).permitAll() .requestMatchers(getHealthCheckEndpoints()).permitAll() + .requestMatchers(getActuatorEndpoints()).access(actuatorAuthorizationManager) .requestMatchers(LOCAL_LOGIN_ENDPOINT).permitAll() .requestMatchers(LOCAL_SIGN_ENDPOINT).permitAll() .requestMatchers(LOGIN_ENDPOINT).permitAll()