📌 개요
저는 kubernetes 환경을 통해서, spring boot 서버를 하나가 아닌, 다중으로 두어 로드밸런서를 통해 부하 분산을 하게 환경 구축을 하였습니다.
이 로드밸런싱과 oauth2 의 충돌 문제점이 발생하여, 트러블 슈팅을 기록하게 되었습니다.
문제 지점 이해를 돕기 위한 시스템 아키텍처의 일부입니다.
1) frontend-svc 를 통해 유저들이 프론트엔드 웹페이지 접근을 한다.
2) nginx 리버스프록시를 통해 backend-svc 서비스를 가리키게 한다.
-> 이때 backend-svc 는 로드밸런서로 구성되어 있기 때문에, 존재하는 다중 파드에게 부하를 분산하여 요청을 보낸다.
2번 과정중, 빨간 부분이 바로 문제지점 입니다.
이제 왜 해당 로드밸런싱과 oauth2 인증 과정 중에 문제가 발생했는지 기록해 보겠습니다!
❓ Oauth2 인증과정
이해를 돕기 위해서 kakao 개발자 공식 문서에 있는 내용을 가져왔습니다.
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api
그림과 같이 소셜 인증 과정은 크게 3단계로 이루어집니다.
✅ 1단계: 사용자는 서버에서 카카오 OAuth2 인증 서버로 redirect 되고, 권한 부여 후 인가 코드와 함께 서버로 다시 redirect된다.
✅ 2단계: 서버는 카카오의 토큰 엔드포인트에게 요청을 보내 인가 코드를 액세스 토큰으로 교환하려고 시도한다.
✅ 3단계 : 획득한 토큰을 통해서 로그인이 성공된다.
바로 이때, 1번 과정에서 인가코드 획득과 함께 서버와 세션정보를 주고 받게 되는데, 이 세션정보가 서버측에 저장되어 있지 않으면 2단계 토큰 발급에 문제가 발생합니다.
좀 더 간략히 도식화를 해보자면 아래와 같습니다.
실제로 1단계의 인가 코드를 넘겨 주는 과정에서 많은 OAuth2 제공업체(카카오 포함)는 승인 요청을 시작할 때 state 매개변수를 보냅니다. 이는 요청이 서버에서 발생했는지 확인하여 CSRF 공격을 방지하는 데 사용된다고 합니다.
(실제로 소셜 인증과정 중 uri 를 살펴보면 state 가 함께 전송되는 것을 확인할 수 있었습니다!)
하지만 서버 1이 인증 요청을 시작했지만,
서버 2가 반환된 인증 코드의 유효성을 검사하려고 시도하는 경우 서버 1에서 생성된 state 매개변수는 서버 2에 액세스할 수 없기 때문에 유효성 검사가 실패하고 토큰 교환이 거부됩니다.
이에 따라서, 서버끼리 세션정보에 대한 동기화 과정, 혹은 고정된 서버의 전략이 필요하다고 판단하였습니다.
📌 Oauth2 소셜 인증을 위한 Sticky Session 서비스 도입
이에 따라, 제가 채택한 전략은 Sticky Session 서비스 도입 전략입니다.
해당 방법에 대해선 크게 어렵지는 않습니다!
일단 기존 서비스에는 nginx 리버스 프록시 설정을 아래와 같이, /api 로 시작하는 요청이 들어오면
백엔드 로드밸런서 서비스를 가리키게 설정했었습니다.
이에 위에 말한 state 세션 정보 불일치 문제가 발생하여 1단계 인가코드까지 받는것은 성공하였으나,
2단계 토큰을 획득하지 못하고 에러가 발생하였습니다.
👊 K8S Sticky Session Service 추가
약 1시간동안 서버를 고정시키도록 설정
kind: Service
apiVersion: v1
metadata:
name: social-svc-fix
spec:
selector:
type: backend
deployment: blue
ports:
- port: 8080
protocol: TCP
targetPort: 8080
sessionAffinity: ClientIP # 60분간 서버 고정
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600
👊 Nginx Reverse Proxy 추가
/social 로 요청이 들어올 시, sticky session svc 를 가리키도록 설정
location /social {
rewrite ^/social(.*)$ $1 break;
proxy_pass http://social-svc-fix:8080/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
👊 리다이렉트 경로 재 설정
1) 프론트엔드 측
sticky 서비스로 찾아갈 수 있도록 변경
2) 백엔드 측
2-1) spring boot kakao - redirect-uri 를 frontend IP 혹은 도메인 주소로 변경
2-2) 카카오 개발자 애플리케이션에서 redirect URI 를 프론트엔드 IP 혹은 도메인 주소로 변경
💯 어? 서버 IP , 도메인을 적는게 아니라 프론트엔드 IP / 도메인을 적는다구요?
아마 기존의 저희는 모두 서버와 redirect 를 하기 위해, 서버의 IP 를 적어줬지만,
기존 사용자가 고정된 서비스를 가리키기 위해선 프론트엔드 IP주소를 적는다면 해결이 될 것이라고 판단하였습니다.
해당 구성을 통해서 분산 서버에 대해 소셜 인증을 마무리 할 수 있었습니다.
해당 방법말고도 다양한 방법으로 분산 서버의 소셜 인증에 대한 방법들이 존재할 것이니, 다음번엔 다른방법으로도 시도해보고자 합니다 :)
다소 긴 트러블 슈팅 포스팅을 읽어주셔서 감사합니다🙇♀️🙇♂️
'[TIL]' 카테고리의 다른 글
TIL - CDC로 향하는 가는 첫 번째 과정(1) : OracleDB 트랜잭션 로그(Redo log)를 읽어서 Kafka에 적재하기 (0) | 2025.01.10 |
---|---|
[ TIL ] RECOVER_YOUR_DATA : RDS 해킹 일지 (0) | 2024.11.13 |
JWT 보안성 강화시키기 (Access Token, Refresh Token -RTR, BlackList 그리고 Redis) (0) | 2024.10.02 |
ELK 에 대해서 알아보자! ( ElasticSearch - Logstash - Kibana ) (2) | 2024.09.22 |
Cookie 에 Http Only 와 secure 를 적용시켜보자! [ Spring boot + Vue 3.x] (0) | 2024.08.26 |