diff --git a/.env.example b/.env.example index c4d1315..4ee7811 100644 --- a/.env.example +++ b/.env.example @@ -30,4 +30,4 @@ MINIO_SECRET_KEY= MINIO_BUCKET_NAME= MINIO_PRESIGNED_URL_EXPIRATION= -TRUSTED_PROXIES= +SENTRY_DSN= diff --git a/README.md b/README.md index e467a92..c06871f 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ MINIO_SECRET_KEY= MINIO_BUCKET_NAME= MINIO_PRESIGNED_URL_EXPIRATION= -TRUSTED_PROXIES= +SENTRY_DSN= ``` ## 프론트엔드 diff --git a/build.gradle.kts b/build.gradle.kts index ed7b5b3..4a71349 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("org.springframework.boot") version "3.4.0" id("io.spring.dependency-management") version "1.1.6" id("org.jlleitschuh.gradle.ktlint") version "12.1.2" + id("io.sentry.jvm.gradle") version "5.3.0" kotlin("plugin.jpa") version "1.9.25" } diff --git a/src/main/kotlin/me/daegyeo/maru/auth/adapter/in/web/AuthController.kt b/src/main/kotlin/me/daegyeo/maru/auth/adapter/in/web/AuthController.kt index 531ceeb..65153b5 100644 --- a/src/main/kotlin/me/daegyeo/maru/auth/adapter/in/web/AuthController.kt +++ b/src/main/kotlin/me/daegyeo/maru/auth/adapter/in/web/AuthController.kt @@ -82,8 +82,12 @@ class AuthController( response.addCookie( Cookie(Auth.ACCESS_TOKEN_COOKIE, null).apply { + path = "/" maxAge = 0 isHttpOnly = true + secure = true + domain = domainEnv + setAttribute("SameSite", "Lax") }, ) diff --git a/src/main/kotlin/me/daegyeo/maru/infrastructure/config/ForwardedConfig.kt b/src/main/kotlin/me/daegyeo/maru/infrastructure/config/ForwardedConfig.kt new file mode 100644 index 0000000..3959810 --- /dev/null +++ b/src/main/kotlin/me/daegyeo/maru/infrastructure/config/ForwardedConfig.kt @@ -0,0 +1,13 @@ +package me.daegyeo.maru.infrastructure.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.filter.ForwardedHeaderFilter + +@Configuration +class ForwardedConfig { + @Bean + fun forwardedHeaderFilter(): ForwardedHeaderFilter { + return ForwardedHeaderFilter() + } +} diff --git a/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/ExceptionHandleFilter.kt b/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/ExceptionHandleFilter.kt index f9e7ca2..77b5ffd 100644 --- a/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/ExceptionHandleFilter.kt +++ b/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/ExceptionHandleFilter.kt @@ -1,6 +1,7 @@ package me.daegyeo.maru.infrastructure.filter import com.fasterxml.jackson.databind.ObjectMapper +import io.sentry.Sentry import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse @@ -8,9 +9,13 @@ import me.daegyeo.maru.shared.error.BaseError import me.daegyeo.maru.shared.error.CommonError import me.daegyeo.maru.shared.error.ErrorResponse import me.daegyeo.maru.shared.exception.ServiceException +import me.daegyeo.maru.shared.util.IPAddress +import org.slf4j.LoggerFactory import org.springframework.web.filter.OncePerRequestFilter class ExceptionHandleFilter : OncePerRequestFilter() { + private val log = LoggerFactory.getLogger(this::class.java) + override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, @@ -20,9 +25,13 @@ class ExceptionHandleFilter : OncePerRequestFilter() { filterChain.doFilter(request, response) } catch (e: ServiceException) { sendErrorResponse(response, e.error) + log.warn( + "[${request.method}] ${IPAddress.getClientIp(request)} ${request.requestURI} (${request.contentType}) - ${response.status}", + ) } catch (e: Exception) { e.printStackTrace() sendErrorResponse(response, CommonError.INTERNAL_SERVER_ERROR) + Sentry.captureException(e) } } diff --git a/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/HttpLoggingFilter.kt b/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/HttpLoggingFilter.kt index 8991f1e..e7b9638 100644 --- a/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/HttpLoggingFilter.kt +++ b/src/main/kotlin/me/daegyeo/maru/infrastructure/filter/HttpLoggingFilter.kt @@ -3,6 +3,7 @@ package me.daegyeo.maru.infrastructure.filter import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse +import me.daegyeo.maru.shared.util.IPAddress import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import org.springframework.web.filter.OncePerRequestFilter @@ -18,11 +19,8 @@ class HttpLoggingFilter : OncePerRequestFilter() { ) { filterChain.doFilter(request, response) - val realIp = request.getHeader("X-Real-IP") - val forwardedIp = request.getHeader("X-Forwarded-For") - val ip = realIp ?: forwardedIp ?: request.remoteAddr log.info( - "[${request.method}] $ip ${request.requestURI} (${request.contentType}) - ${response.status}", + "[${request.method}] ${IPAddress.getClientIp(request)} ${request.requestURI} (${request.contentType}) - ${response.status}", ) } } diff --git a/src/main/kotlin/me/daegyeo/maru/shared/util/IPAddress.kt b/src/main/kotlin/me/daegyeo/maru/shared/util/IPAddress.kt new file mode 100644 index 0000000..c74d8a9 --- /dev/null +++ b/src/main/kotlin/me/daegyeo/maru/shared/util/IPAddress.kt @@ -0,0 +1,24 @@ +package me.daegyeo.maru.shared.util + +import jakarta.servlet.http.HttpServletRequest + +object IPAddress { + fun getClientIp(request: HttpServletRequest): String { + var ip = request.getHeader("X-Forwarded-For") + + if (!ip.isNullOrEmpty() && ip != "unknown") { + val ips = ip.split(",") + ip = ips[0].trim() + } + + if (ip.isNullOrEmpty() || ip == "unknown") { + ip = request.getHeader("X-Real-IP") + } + + if (ip.isNullOrEmpty() || ip == "unknown") { + ip = request.remoteAddr + } + + return ip ?: "unknown" + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 00733bf..ab06d86 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,11 +1,6 @@ server: port: ${PORT:8080} - forward-headers-strategy: FRAMEWORK - tomcat: - remoteip: - protocol-header: X-Forwarded-Proto - remote-ip-header: X-Forwarded-For - trusted-proxies: ${TRUSTED_PROXIES} + forward-headers-strategy: framework spring: profiles: default: prod @@ -73,3 +68,6 @@ minio: bucket-name: ${MINIO_BUCKET_NAME} presigned-url-expiration: ${MINIO_PRESIGNED_URL_EXPIRATION} domain: ${DOMAIN} +sentry: + dsn: ${SENTRY_DSN} + send-default-pii: true