Jaeilit

DNS를 Route 53에서 Cloudflare로 이전하면서 디도스 방어하기 본문

로또헌터

DNS를 Route 53에서 Cloudflare로 이전하면서 디도스 방어하기

Jaeilit 2025. 1. 20. 21:31
728x90

막상 사이트를 배포하고 나니 비정상적인 트래픽으로 인한 과금이 걱정되기 시작했습니다. 특히 디도스(DDoS) 공격처럼 악의적인 사용자가 서버에 수많은 요청을 동시에 보내거나, 봇이 API를 과도하게 호출하는 경우 AWS 요금이 예상치 못하게 과금될 수 있습니다. 아직은 걱정할 단계가 아닐지 모르지만, 과금이 발생한 후에 대책을 세우는 것보다 미리 예산을 방어하는 것이 현명하다고 판단했습니다.

 

현재 사이트의 아키텍처로는 가비아에서 구입한 도메인의 DNS 설정을 AWS Route53에서 관리하고 루트 도메인은 Next.js로 작성된 프론트엔드를 배포한 Vercel과 연결했고, 백엔드 서버는 'api'라는 서브도메인을 만들어 EC2와 연결해두었습니다.

route 53 호스팅존
acm 인증서

Next.js는 보안상 기본적으로 HTTPS로 통신하도록 설정되어 있어서 서버가 HTTP일 경우 Mixed Content 오류를 발생합니다(HTTP와 HTTPS가 혼용되는 것을 막습니다). 이에 맞춰 백엔드 서버도 HTTPS 통신이 필요했기 때문에, AWS Certificate Manager(ACM)에서 SSL 인증서를 발급받았습니다. Application Load Balancer(ALB)를 설정하여 모든 HTTP(80포트) 진입 트래픽을 HTTPS(443포트)로 자동 전환하도록 했고, 최종적으로 ALB의 대상 그룹을 EC2에서 실행 중인 서버의 8080포트로 연결하도록 구성했습니다.

 

 

AWS에서는 ShieldWAF(Web Application Firewall), 두 가지 서비스로 다양한 유형의 공격을 방어할 수 있습니다.

AWS Shield는 네트워크 및 전송 계층(L3/L4)에서 발생하는 DDoS 공격을 방어하는 서비스입니다. 대표적으로 SYN Flood 나 UDP Flood 등을 차단할 수 있습니다. Shield의 요금 플랜은 두 가지 버전으로 제공됩니다:

  • Shield Standard: 모든 AWS 고객에게 무료로 제공되며, 가장 일반적이고 자주 발생하는 네트워크 및 전송 계층의 DDoS 공격을 자동으로 방어합니다.
  • Shield Advanced: 월 3,000달러(약 437만원, 2024년 1월 기준)의 구독형 서비스로, 더 정교한 DDoS 공격 방어, 24/7 보안 전문가 지원, DDoS 관련 AWS 비용 보호 등 고급 기능을 제공합니다.

AWS WAF는 애플리케이션 계층(L7)에서 발생하는 웹 공격을 방어합니다. 주요 방어 대상은 다음과 같습니다:

  • SQL 인젝션: 악의적인 SQL 구문을 주입해 데이터베이스를 공격하는 시도
  • XSS(Cross-Site Scripting): 웹사이트에 악성 스크립트를 삽입하는 공격
  • 악성 봇 트래픽
  • 특정 IP나 지역에서의 접근
  • 비정상적인 요청 패턴

WAF는 사용량 기반 과금 방식을 채택하고 있습니다. 요금은 다음 세 가지 요소를 기준으로 계산됩니다:

  1. WAF 규칙 수
  2. 처리된 웹 요청 수
  3. 사용한 보안 규칙의 종류

AWS는 공식 문서에서 다양한 사용 사례별 예상 비용을 제공하고 있어, 이를 참고하면 예산 계획에 도움이 됩니다.

 

 

Shield Standard와 WAF만으로도 기본적인 보안은 가능하지만, 비용 효율적인 대안을 찾아보니 Cloudflare가 가장 떠올랐습니다.

 

Cloudflare는 CDN, DNS, DDoS 방어 등을 통합으로 제공하는 서비스입니다. 무료 플랜에서도 DDoS 방어는 물론 전세계 CDN을 통한 성능 최적화, SSL/TLS 인증서 제공, DNS 관리, 봇 차단과 기본적인 방화벽 기능들을 제공합니다. Shield Standard가 L3, L4 계층만 방어하는 것과 달리, Cloudflare는 무료 플랜에서도 L3, L4, L7 계층까지 통합적인 방어가 가능한 것이 큰 장점입니다.

현재 제 서비스는 AWS를 기반으로 운영되고 있지만, DNS를 Cloudflare로 이전하면 더 나은 보안과 성능 최적화를 무료로 얻을 수 있을 것 같습니다.

Route53에서는 기본적으로 호스팅 영역 하나당 월 0.5달러의 비용이 발생하는데, 현재 루트 도메인과 서브도메인으로 2개의 호스팅 영역을 사용하고 있어 월 1달러가 청구되고 있습니다. Cloudflare로 이전하면 이 DNS 관리 비용도 절감할 수 있습니다.

 

DNS를 Route 53에서 Cloudflare로 이전하기

AWS Route 53에서 관리하던 DNS를 Cloudflare로 이전하는 과정을 진행하며, 올바른 설정 방법과 주의해야 할 점을 아래와 같이 공유합니다.

 

1. DNS 이전

도메인 구입처에서 구입한 최상위 도메인을 AWS 서비스와 연결 시킬 때 Route53에 호스팅존을 만들고 NS를 도메인 구입처에서 제공받은 NS로 변경하는 작업을 합니다.

 

Cloudflare에 가입한 뒤, 도메인 이름을 입력하면 Cloudflare가 검증을 위해 2개의 NS 값(예: alice.ns.cloudflare.com, bob.ns.cloudflare.com)을 제공합니다. 도메인 검증(전파)은 24~48시간이 걸린다고 안내하고 있지만 올바르게 설정했다면 제 경험상 30분 내외로 검증 이메일을 받을 수 있습니다. 무언가 잘못됐다면 검증 버튼을 계속 누르거나 절대 그냥 기다리지 마세요,

 

이슈: Cloudflare NS 값을 Route 53에 잘못 설정한 실수

저는 cf에서 제공 받은 NS 값을 Route53 NS에 대체했습니다. 이건 잘못 된 방법이고 기다리면 되겠지하면서 기다리다가 며칠을 그냥 날렸습니다.

 

이유는 간단합니다

가비아는 제가 도메인을 처음 구입한 곳이고, 이곳에서 네임서버를 관리 합니다.

즉, 가비아의 NS설정에 cf에서 준 NS를 정확히 입력하지 않으면 cf가 도메인의 dns를 관리 할 수 없습니다. 하지만 저는 Route53과 연결 되어있던 가비아를 제가 끊어버리는 격이 되었고 따라서, 가비아의 NS는 어디에도 연결되지 않는 상태가 되었습니다. 그 결과 Route53과 cf가 서로 연결된 상태가 되버려 계속 검증에 실패했던 것입니다.

 

올바르게 해결하려면 cf에서 제공 받은 ns 값을 가비아에 설정을 입력해야합니다. 이건 최상위 도메인의 네임 서버를 cf로 전환하는 작업입니다. 도메인 검증 이메일을 받았다면 이제는 dns관리를 cf에서 하게 됩니다.

 

2. Route53 호스팅존 제거 및 alb 이전

처음에는 Route53이 필요 없다고 판단해 호스팅 존을 삭제했습니다. 동시에 ALB도 삭제했는데, 이후 EC2에 SSL을 적용할 필요성을 느껴 ALB를 다시 설정하기로 했습니다.

SSL 인증은 ALB 없이도 구현할 수 있습니다. 예를 들어, AWS Certificate Manager(ACM)이나 Let’s Encrypt를 통해 인증서를 발급받아 EC2 인스턴스 내 Nginx 또는 Apache와 같은 웹 서버에 직접 등록하는 방식이 가능합니다. 하지만, 나중에 부하 분산(로드 밸런싱)이 필요할 가능성을 고려해 미리 ALB를 설정하기로 결정했습니다.

 

현재 상태
지금까지의 작업으로 사용자가 도메인에 접속하면 Cloudflare(CF)에서 DNS 설정을 읽고, 올바른 리소스로 트래픽을 라우팅하도록 설정했습니다. CF는 도메인 요청을 처리하고 ALB 또는 EC2로 트래픽을 전달할 준비가 완료된 상태입니다.

 

프론트엔드에서 사용하는 루트 도메인은 Vercel로 라우팅되어 바로 접속이 가능합니다. 그러나 API 서브 도메인의 요청은 CF에서 ALB로 전달한 후 EC2의 애플리케이션 서버로 라우팅해야 합니다.

  • 루트 도메인: Vercel 라우팅
  • API 서브 도메인: Cloudflare → ALB → EC2

루트 도메인 - 버셀 라우팅

 

ALB 연결 설정
기존에 Route53에서 ALB를 사용할 때는 A 레코드에 별칭(alias)으로 ALB의 DNS 이름(e.g., hunter-lb-396642792.ap-northeast-2.elb.amazonaws.com)을 입력했습니다. 하지만 Cloudflare에서는 별칭(alias) 설정이 불가능하므로, ALB의 DNS 이름을 CNAME 레코드로 추가해야 했습니다.

  • 기존 설정 방식
    • Route53: A 레코드(별칭) → ALB
  • Cloudflare 설정 방식
    • CF: CNAME 레코드 → ALB 이름

기존 alb 연결 방식 a레코드 별칭
cf cname alb 이름 입력

이 설정을 완료하면 api.* 서브도메인에 접속 시 트래픽이 ALB로 라우팅됩니다.

  1. HTTP 요청(80포트): ALB의 HTTP 리스너에서 HTTPS(443포트)로 리다이렉트 처리.
  2. HTTPS 요청(443포트): ALB는 SSL 인증서를 사용하여 요청을 처리하고, 대상 그룹(Target Group)에 설정된 EC2의 3030포트로 트래픽을 전달합니다.

결론적으로, 사용자가 api.* 도메인으로 접속하면 ALB를 거쳐 EC2의 애플리케이션(3030포트)에 요청이 안전하게 전달됩니다.

80포트 리스너 규칙

 

이슈: api 도메인 접속 시 무한 로딩 -> 504 타임아웃 발생

설정대로라면 API 도메인 접속이 정상적으로 이루어져야 했지만, 계속 무한 로딩이 발생하면서 결국 504 Gateway Timeout 에러가 나타났습니다.

 

의심할수 있는 부분

  • ALB 대상그룹 EC2 헬스체크
  • 80포트에서443으로 리다이렉트가 잘 되고 있는지
  • 443에서 3030으로 라우팅 되고있는지

1.  ALB 대상그룹 EC2 헬스체크

alb 대상그룹 헬스체크
브라우저에서 도메인 접속

ALB 대상그룹 EC2 헬스 체크는 대상 그룹 지정때 설정 해둔 '/' 나 특정 엔드 포인트로 확인할 수 있는데 요청을 했을 때 응답이 없다면 ALB에서는 비정상 상태가 됩니다. 헬스 체크 설정에서 지정된 엔드포인트(/)로 요청했을 때 정상 응답을 확인했습니다. EC2는 헬스 체크를 통과하고 있었으므로 ALB와 EC2 간 기본적인 연결에는 문제가 없었습니다.

 

2.  80포트에서443으로 리다이렉트가 잘 되고 있는지

curl

80포트에서 443으로 리다이렉트가 잘 되는지 확인하고싶어서 curl 명령어로 API 도메인에 접속을 시도했습니다. 결과적으로 301 리다이렉트가 발생했고, Location 헤더에 표시된 경로가 설정한 대로 올바르게 표시되고 있었습니다. 이를 통해 DNS 및 ALB로의 트래픽 유입은 정상적으로 이루어지고 있음을 확인했습니다.

 

3. 443에서 3030으로 라우팅 되고있는지

서버 로그를 확인해보니 ALB의 헬스체크 외에는 제가 접근하는 로그가 찍히지 않고 있다는 것을 발견했습니다. HTTPS(443)로 들어온 트래픽이 앱 포트로 제대로 전달되지 않는다는 것을 의미했습니다.

 

처음에는 "Cloudflare를 통해 들어오는 트래픽이라 SSL 인증서 문제인가? AWS ACM이 아닌 Cloudflare 인증서를 새로 설정해야 하나?" 라고 생각했습니다. 하지만 다시 생각해보니, ALB의 헬스체크가 정상적으로 동작한다는 것은 ALB에서 앱 포트로의 연결 자체는 문제가 없다는 뜻입니다. 즉, 문제는 외부에서 들어오는 HTTPS 트래픽이 ALB를 거쳐 앱 포트로 전달되는 과정에 있다고 추론할 수 있었습니다.

 

  • ALB 헬스체크가 성공하는 것으로 보아 ALB→EC2 연결 자체는 정상
  • 실제 트래픽이 안 들어오는 것으로 보아 외부→ALB→EC2로 이어지는 전체 통신 흐름에 문제가 있다는 추론

 

ALB 내부에서 문제를 찾으려고 했습니다. 그럼 리스너 규칙, 대상그룹, 보안그룹에서 찾아야하는데 앞 서 살펴본 1,2에서처럼 리스너 규칙이 문제였다면 curl 접속 시 301 리다이렉트가 안됐을테고, 대상그룹이 문제였다면 헬스체크가 안됐을테니 보안그룹을 살펴보기로 했습니다. 보안그룹은 ec2 설정과 동일한 것으로 사용하고 있었고 http 80, https 443, 앱 포트 "0.0.0.0/0" (모든 IP 허용) 으로 특정 ip를 막는다거나 필터링 없이 다 열어두고 사용하고 있었기에 문제가 없다고 생각했습니다.

 

여러 방면에서 시도하다보니 동일한 보안 그룹 사용으로 트래픽이 충돌 할 수 있다는 글을 봤습니다.

이미 며칠간의 고생으로 지쳐있었는데 지푸라기라도 잡는 심정으로 보안그룹을 분리하여 alb용 http80 https443 포트만 열어두는 보안그룹을 설정해서 alb에 적용했더니 504 타임아웃이 안뜨고 접속이 가능했습니다.

 

  • ALB 보안그룹: 인바운드 80, 443 포트 (0.0.0.0/0)
  • EC2 보안그룹: 인바운드 앱 포트 (ALB 보안그룹으로부터만 허용)

 

보안 그룹을 나눠서 ALB는 외부 트래픽만 받도록하고 EC2는 앱 포트만 열어두는 방식이 AWS에서도 권장하는 아키텍처라고 합니다.

 

끝으로

며칠간의 노력 끝에 결국 AWS Route53에서 CF로 DNS를 이전 했습니다. 

처음에는 단순한 설정일 줄 알았는데 인프라나 서버쪽은 CS지식이 많이 필요하다는 것을 또 새삼 느꼈습니다. 면접 준비로 도메인을 입력하면 벌어지는 일 들에 대해서 DNS 서버로 가서 IP 변환을 해서 브라우저에 리소스를 전달한다 라는 플로우를 공부하는데 이건 더 깊게 알지 못하면 너무 헤맬 수 있는 문제였던 것 같습니다. AWS 인프라도 이해를 하고 있어야한다는 점도 너무 장벽이 높게 느껴졌습니다. 더 부딪혀보도록 하려고 합니다.

 

728x90