티스토리 뷰
문제 발생
상용 배포 당일
정신없던 배포 준비를 마친 후, 상용서버 배포를 진행했다.
그리고 몇 번의 핫픽스를 내보낸 후 FE 개발자 분이 안보이는 부분에 문제가 있다고 연락이 왔다.
FE에서 자기들이 쓰려고 FE의 서버로 보내는 API를 만들었는데 이 URL로 요청을 보내면 403 에러가 난다는 것.
403 Forbidden을 가장 많이 보는 경우는 CORS 문제다.
하지만 해당 요청이 들어온 CloudFront(이하 CF)의 옵션을 보면 CORS 문제가 아니다라는 걸 바로 알 수 있다.
AWS에서도 403 에러 관련 문서를 제공하는데 나에겐 큰 도움이 되지 않았었다.
함정이 도사리고 있어서 매우 고통스러웠던, 문제 해결과정을 알아보자..
문제 분석
1. 정말 CORS 문제가 아닌가?
이 도메인으로 접근하는 CF는 캐시 옵션을 사용하지 않는다.
항상 원본을 요청하기 때문에, 원본이 CORS를 발생시키지 않는다면 CF에서도 CORS는 발생할 수가 없다.
2. POST 요청이 막혀있는가?
CF에 디폴트로 허용된 HTTP Method는 GET, HEAD이다.
그래서 POST 요청은 기본적으로 막혀있는데, 이전에 비슷한 이슈가 발생해서 이 옵션을 풀어줬었다.
그래서 이 오류의 문제 후보가 될 수 없다.
3. WAF
WAF란? https://aws.amazon.com/ko/waf/
문제의 원인은 어떻게 보면 빨리 알아차렸어야했다.
왜냐하면 개발 서버에서는 정상 동작하기 때문이었다.
프로덕션 서버와 개발 서버의 차이 중 가장 눈에 띄는건 WAF의 존재 유무다.
상용서버의 구조 간략화 : 클라이언트 → CloudFront → WAF → ALB → k8s
위는 그동안 내가 생각하는 우리 서버의 구조였고, 이걸 기반으로 분석을 진행했었다.
그래서 첫 분석 당시 ALB 앞에 있는 WAF 로그를 확인해봤는데, WAF와 ALB 로그에는 해당 URI로 진입한 이력이 없었다.
그렇다면 CF에서 차단하고 있다는건데 원인이 뭘까?
AWS 권한 문제? Route 53 문제인가? 등등 생각해봤지만 아무리 생각해도 WAF 말곤 문제를 일으킬 만한 게 없었다.
그렇다면 숨겨져있는 WAF가 있다는 건데... 이 생각이 키워드가 됐다.
문제의 원인 찾아가기
조금 여유를 갖고, 로그를 하나씩 찾아보기 시작했다.
가장 필요한건 CF의 로그였다. 다행히 해당 CF의 표준 로깅은 설정이 되어있었다.
(표준 로깅 설정은 일반 > 설정 > 맨 우측에서 확인할 수 있다)
Athena로 가서 해당 S3를 기준으로 만들어 놓은 테이블(web_cf_logs)이 있어서 조회를 해봤다.
여기에 함정이 하나 있었다...
그러나 어찌된 이유인지 아무런 로그도 나오지 않았다.
(사실 문제 확인을 위해 가장 먼저 한 것 중 하나가 이거였는데, 아무것도 나오지 않아 표준 로깅이 꺼져있다 생각했었다)
이전 담당자가 테이블의 컬럼을 잘못 설정해서 아무것도 나오지 않았던 것이다.
S3에 저장된 로그 파일들을 하나씩 확인해 올바른 컬럼을 설정해주고
cloudfront_logs라는 테이블을 만들어주니 CF 로그 조회가 정상적으로 동작했다.
문제는 이 로그에 403 에러가 발생했다는 이력만 남아있고 원인이 남아있지 않았다.
그래도 이 로그를 통해 ALB쪽 WAF에 걸리지 않았다는 것을 100% 확신할 수 있었다.
그렇다면 어디에서 걸렸을까 고민하면서
CF 쪽 탭을 이것저것 만지다보니 보안 탭 쪽이 개발 서버와 상용 서버가 다르게 설정되어 있었다는 걸 알게 됐다.
로그를 봤을 때, ALB와 연결된 WAF에는 아무 이력이 남지 않았다.
그렇다면 이 WAF는 뭘까...?
CF에서 자체적으로 관리하던 WAF가 있었던 것이다. 이게 두번째 함정이었다.
숨겨져 있던 CloudFront WAF
가장 먼저 의심을 했던 WAF의 Web ACLs의 리전 설정을 보면 첫 탭에 CloudFront가 있다.
이걸 빨리 발견했으면 문제를 그래도 빨리 해결했을지도 모른다...
CF created WAF도 cloudwatch에 로그를 남기고 있었는데, FE에서 보낸 URI로 조회해보니 BLOCK 된 원인이 남아있었다.
AWSManagedRulesCommonRuleSet의 SizeRestrictions_BODY 룰의 제한을 받았다.
매니지드 룰셋 : https://docs.aws.amazon.com/ko_kr/waf/latest/developerguide/aws-managed-rule-groups-baseline.html
정리해보면, FE가 보내는 API의 RequestBody에는 이미지가 포함되어 있어서 RequestBody 크기가 8kb는 가뿐히 넘었기 때문에 WAF의 해당 룰셋에 의해 API가 전부 차단되고 있었다.
FE분들은 그동안 비슷한 방식으로 작업을 해왔다고 하는데, 막히지 않았던 이유가 운좋게도 text 기반 ReqeustBody를 사용해서 8kb가 넘지 않았기 때문이었다.
문제가 되는 URI를 전달받아, 이 URI를 허용하는 룰셋을 등록해주니 이 문제는 해결됐다.
왜 WAF를 두 개를 뒀을까?
그런데 의문점이있다. 왜 WAF를 CF와 ALB 두 곳에 뒀을까?
이유는 단순하다.
각각의 위치에서 막을 수 있는 것들이 다르기 때문이다.
미숙한 내가 일일히 설명하는 것보다 우아한 기술 블로그를 보는게 조금 더 정확 할 것 같다.
https://techblog.woowahan.com/2699/
WAF가 CF(CDN)에 위치한 이유는 우아한 기술 블로그에서 정말 잘 설명해줬다.
Cloudfront의 WAF는 L7 DoS및 임계치 조절, IP Blacklist 차단 규칙으로 WAF룰을 수립하고, ALB의 WAF는 웹 취약점 공격을 중점으로 룰을 구분하여 수립할 수 있습니다.
ALB-WAF에서는 BE로 오는 API 관련해서 문제를 일으킬 만한 사항들을 걸러내는 작업을 하고 있다.
아쉬운 점은 우리 서버의 WAF는 룰셋을 세부적으로 정의해서 잘 쓰고 있지는 못하다.
여유가 되면 이 부분들도 보강할 필요가 있어 보인다.
마치며
사실 WAF 보안탭에 설정되었던 WAF가 ALB WAF랑 같다고 생각했었다.
정리하면 상용서버는 아래와 같은 구조였다.
클라이언트 → CloudFront/WAF → ALB/WAF → k8s
바로 직전 글과 비슷한 느낌이지만 이번 이슈는 더 빨리 알아차릴만한 이슈였다.
꼼꼼하지 못한 내 성격과 배포 후 급하게 몰아치는 이슈들에 대응하다보니 어버버 거린게 문제였다.
다음날 일찍 출근에 차분하게 생각을 정리하면서 이슈를 트래킹하니 해결할 수 있었다.
WAF가 이런식으로도 붙을 수 있다는 걸 알게됐고, 로깅이 정말 중요하다는 것도 알게 됐다.
'개발 > AWS' 카테고리의 다른 글
Spot 인스턴스로 운영 되던 서버 문제 해결하기 (0) | 2024.08.26 |
---|---|
한 눈에 알아보는 AWS VPC(Virtual Private Cloud) 정리 (0) | 2024.04.16 |
Amazon S3 파일 주기적으로 삭제하기 : SpringBoot에서 미리 서명된 URL(pre-signed URL) 써보기 (0) | 2024.03.28 |
AWS S3 버킷을 퍼블릭하게 사용하기 : SpringBoot에서 퍼블릭하게 업로드하기 (0) | 2024.03.08 |
AWS EC2에 SSL/TLS(HTTPS) 인증서 적용기 (0) | 2024.01.26 |
- Total
- Today
- Yesterday
- OpenFeign
- OpenAI
- GIT
- 오블완
- 후쿠오카
- 람다
- CloudFront
- lambda
- Log
- Spring
- AWS
- springboot
- JWT
- 티스토리챌린지
- S3
- docker
- ChatGPT
- serverless
- AWS EC2
- EKS
- java
- cache
- Kotlin
- elasticsearch
- terraform
- MySQL
- 스프링부트
- openAI API
- AOP
- Elastic cloud
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |