Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 모니터링 서버 구축 #357

Merged
merged 4 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions main/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ dependencies {

// csv 관련
implementation 'com.opencsv:opencsv:5.5.2'

// prometheus
implementation 'io.micrometer:micrometer-registry-prometheus'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import java.util.Arrays;
import java.util.stream.Stream;

import lombok.RequiredArgsConstructor;

import org.sopt.makers.crew.main.common.jwt.JwtAuthenticationEntryPoint;
import org.sopt.makers.crew.main.common.jwt.JwtAuthenticationFilter;
import org.sopt.makers.crew.main.common.jwt.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
Expand All @@ -25,122 +28,132 @@
@EnableWebSecurity
public class SecurityConfig {

private final JwtTokenProvider jwtTokenProvider;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtTokenProvider jwtTokenProvider;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

@Value("${management.endpoints.web.base-path}")
private String actuatorEndPoint;

private static final String[] SWAGGER_URL = {
"/swagger-resources/**",
"/favicon.ico",
"/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/swagger-ui/index.html",
"/docs/swagger-ui/index.html",
"/swagger-ui/swagger-ui.css",
};
private static final String[] SWAGGER_URL = {
"/swagger-resources/**",
"/favicon.ico",
"/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/swagger-ui/index.html",
"/docs/swagger-ui/index.html",
"/swagger-ui/swagger-ui.css",
};

private static final String[] AUTH_WHITELIST = {
"/health",
"/health/v2",
"/meeting/v2/org-user/**",
"/auth/v2",
"/auth/v2/**"
};
private String[] getAuthWhitelist() {
return new String[] {
"/health",
"/health/v2",
"/meeting/v2/org-user/**",
"/auth/v2",
"/auth/v2/**",
actuatorEndPoint + "/health"
};
}

@Bean
@Profile("dev")
SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(AUTH_WHITELIST)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}
@Bean
@Profile("dev")
SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(getAuthWhitelist())
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}

@Bean
@Profile("test")
SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(AUTH_WHITELIST)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}
@Bean
@Profile("test")
SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(getAuthWhitelist())
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}

@Bean
@Profile("prod")
SecurityFilterChain prodSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(AUTH_WHITELIST)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}
@Bean
@Profile("prod")
SecurityFilterChain prodSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrfConfig) -> csrfConfig.disable())
.cors(Customizer.withDefaults())
.sessionManagement(
(sessionManagement) -> sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(Stream
.of(SWAGGER_URL)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(Stream
.of(getAuthWhitelist())
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.addFilterBefore(
new JwtAuthenticationFilter(this.jwtTokenProvider,
this.jwtAuthenticationEntryPoint),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint));
return http.build();
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
Arrays.asList("https://playground.sopt.org/", "http://localhost:3000/",
"https://sopt-internal-dev.pages.dev/", "https://crew.api.dev.sopt.org", "https://crew.api.prod.sopt.org"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PATCH", "DELETE", "PUT", "OPTIONS"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(false);
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
Arrays.asList("https://playground.sopt.org/", "http://localhost:3000/",
"https://sopt-internal-dev.pages.dev/", "https://crew.api.dev.sopt.org",
"https://crew.api.prod.sopt.org"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PATCH", "DELETE", "PUT", "OPTIONS"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(false);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

}
26 changes: 25 additions & 1 deletion main/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,28 @@ notice:
playground:
server:
url: ${DEV_PLAYGROUND_URL}
endpoint: ${PLAYGROUND_ENDPOINT}
endpoint: ${PLAYGROUND_ENDPOINT}

management:
server:
port: ${ACTUATOR_PORT}
endpoints:
enabled-by-default: false
jmx:
exposure:
exclude: "*"
web:
exposure:
include: info, health, prometheus
Comment on lines +81 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 prod와 의견 동일합니다!

base-path: ${ACTUATOR_PATH}
endpoint:
health:
enabled: true
info:
enabled: true
prometheus:
enabled: true
prometheus:
metrics:
export:
enabled: true
27 changes: 26 additions & 1 deletion main/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,29 @@ notice:
playground:
server:
url: ${PROD_PLAYGROUND_URL}
endpoint: ${PLAYGROUND_ENDPOINT}
endpoint: ${PLAYGROUND_ENDPOINT}


management:
server:
port: ${ACTUATOR_PORT}
endpoints:
enabled-by-default: false
jmx:
exposure:
exclude: "*"
web:
exposure:
include: info, health, prometheus
Comment on lines +79 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 설정으로 다음과 같이 세팅이 되는건지 궁금합니다 ㅎㅎ

애플리케이션 health check

  • ${ACTUATOR_PATH}/health

Prometheus 메트릭 수집

  • ${ACTUATOR_PATH}/prometheus

애플리케이션 정보

  • ${ACTUATOR_PATH}/info

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 그렇습니다!!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요!
그러면 화이트리스트에서 actuatorEndPoint + "/**" 방식으로 모두 접근 권한을 풀어줄 건지, 아니면 특정 endpoint만(ex. 프로메테우스는 화이트리스트에서 제외) 접근 권한을 풀어줄 건지도 얘기해보면 좋을 것 같아요.

제 생각에는 actuatorEndPoint의 value값도 uuid고, 환경변수로 관리하고 있기 때문에, 모두 접근 권한을 풀어줘도 될 것 같은데 민규님의 생각도 궁금합니다!

base-path: ${ACTUATOR_PATH}
endpoint:
health:
enabled: true
info:
enabled: true
prometheus:
enabled: true
prometheus:
metrics:
export:
enabled: true
27 changes: 26 additions & 1 deletion main/src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,29 @@ notice:
playground:
server:
url: ${DEV_PLAYGROUND_URL}
endpoint: ${PLAYGROUND_ENDPOINT}
endpoint: ${PLAYGROUND_ENDPOINT}


management:
server:
port: ${ACTUATOR_PORT}
endpoints:
enabled-by-default: false
jmx:
exposure:
exclude: "*"
web:
exposure:
include: info, health, prometheus
Comment on lines +83 to +86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 prod와 의견 동일합니다!

base-path: ${ACTUATOR_PATH}
endpoint:
health:
enabled: true
info:
enabled: true
prometheus:
enabled: true
prometheus:
metrics:
export:
enabled: true
Loading