구독해서 새 게시물에 대한 알림을 받으세요.

2024년 6월 20일 Cloudflare 사고

2024-06-26

10분 읽기
이 게시물은 English, 繁體中文, 日本語简体中文로도 이용할 수 있습니다.

2024년 6월 20일(목요일) 두 가지 독립적인 이벤트로 인해 인터넷 자산 및 Cloudflare 서비스의 대기 시간과 오류율이 114분 동안 증가했습니다. 영향이 최고조에 달했던 30분 동안 Cloudflare에서는 저희 CDN에 대한 HTTP 요청 중 1.4~2.1%가 일반적인 오류 페이지를 수신했으며 99번째 백분위수 첫 번째 바이트 시간(TTFB) 대기 시간이 3배 증가한 것을 확인했습니다.

Cloudflare incident on June 20, 2024

이들 이벤트는 다음과 같은 이유로 발생했습니다.

  1. 자동 네트워크 모니터링에서 성능 저하를 감지하여 최적이 아닌 상태로 트래픽 라우팅을 변경하여 17:33~17:50(UTC) 사이에 백본 정체를 유발함

  2. 14시:14~17:06(UTC) 사이에 배포된 새로운 분산 서비스 거부(DDoS) 완화 메커니즘으로 인해 레이트 리미팅 시스템에 존재하는 잠재적인 버그가 트리거되었습니다. 이 버그는 특정 형태의 HTTP 요청을 처리하는 프로세스가 17:47~19:27(UTC) 사이에 무한 루프 상태 상태로 들어가게 하는 것이었습니다

이들 이벤트의 영향은 전 세계 많은 Cloudflare 데이터 센터에서 관찰되었습니다.

백본 정체 이벤트와 관련하여, 저희는 이미 영향을 받은 데이터 센터의 백본 용량을 확장하고 조치를 취할 때 대체 네트워크 경로에서 가용 용량에 대한 정보를 더 많이 사용할 수 있도록 네트워크 완화 조치를 개선하고 있었습니다. 이 블로그 게시물 나머지 부분에서는 이들 이벤트 중 두 번째이며 더욱 영향이 컸던 이벤트에 대해 자세히 살펴보겠습니다.

Cloudflare 보호 메커니즘 정기 업데이트의 일환으로 저희는 새로운 DDoS 규칙을 제정하여 Cloudflare 인프라에서 관찰된 특정 유형의 남용을 방지했습니다. 이 DDoS 규칙은 예상대로 작동했지만, 의심되는 특정 트래픽의 경우 기존 레이트 리미팅 구성 요소에 있는 잠재적 버그가 노출되었습니다. 분명히 말씀드리지만, 이 의심스러운 트래픽이 의도적으로 이 버그를 악용했다고 믿을 이유가 없으며, 어떠한 종류의 위반 증거도 없습니다.

불편을 드려 죄송하며 이러한 문제가 재발하지 않도록 하기 위해 이미 조치를 취했습니다.

배경

의심스러운 트래픽의 레이트 리미팅

HTTP 요청의 프로필과 요청된 인터넷 자산의 구성에 따라, Cloudflare에서는 방문자가 특정 기간 내에 생성할 수 있는 요청 수를 제한하여 Cloudflare 네트워크 및 고객의 원본을 보호할 수 있습니다. 이러한 레이트 리미팅은 고객이 구성에 따라, 또는 의심스러운 활동을 감지하는 DDoS 규칙에 대응하여, 활성화할 수 있습니다.

일반적으로 이러한 레이트 리미팅은 방문자의 IP 주소에 따라 적용됩니다. 많은 기관과 인터넷 서비스 공급자(ISP)가 하나의 IP 주소 뒤에 많은 장치와 개인 사용자를 보유하고 있을 수 있으므로 IP 주소 기반의 인터넷 리미팅은 합법적인 트래픽을 의도하지 않게 차단할 수 있는 광범위한 도구 역할을 합니다.

Cloudflare 네트워크에서의 트래픽 분산

Cloudflare에서는 지속해서 실시간 용량 모니터링 및 재밸런싱 기능을 제공하는 다수의 시스템을 보유하고 있어, 최대한 많은 트래픽을 최대한 신속하게 효율적으로 처리할 수 있습니다.

그 첫 번째 시스템은 Cloudflare의 에지 Load Balancer인 Unimog입니다. 저희 Anycast 네트워크에 도착하는 모든 패킷은 Unimog를 거치며, Unimog는 패킷을 적절한 서버로 전달하여 해당 패킷을 처리하도록 합니다. 해당 서버는 컴퓨팅 용량의 가용성에 따라, 패킷이 처음에 Cloudflare 네트워크에 도착했던 위치와 다른 위치에 있을 수 있습니다. Unimog는 각 데이터 센터 내에서 모든 활성 서버에서 CPU 부하를 균일하게 유지하는 것을 목표로 합니다.

전 세계에 걸쳐 Cloudflare 네트워크를 관찰하기 위해 저희는 Traffic Manager에 의존합니다. Cloudflare에서는 모든 데이터 센터 위치에서 전체 CPU 활용, HTTP 요청 대기 시간, 대역폭 사용 등 다양한 신호를 받아들여서 재분산 결정을 합니다. Traffic Manager에는 과도한 트래픽 이동을 방지하기 위한 안전 제한 기능이 내장되어 있으며, 결정을 내릴 때 목적지 위치에 미칠 예상 결과 부하도 고려합니다.

사고 타임라인 및 영향

모든 타임스탬프는 2024-06-20 UTC입니다.

  • 14:14 DDoS 규칙 점진적 배포 시작됨

  • 17:06 DDoS 규칙 전 세계에 배포됨

  • 17:47 첫 번째 HTTP 요청 처리 프로세스가 포이즈닝됨

  • 18:04 감지된 높은 CPU 부하에 따라 사고가 자동으로 선언됨

  • 18:34 서비스 재시작이 서버에서 복구되는 것으로 표시됨, 전체 재시작이 한 데이터 센터에서 테스트됨

  • 18:44 서비스 재시작 후 데이터 센터의 CPU 부하가 정규화됨

  • 18:51 중단된 프로세스가 많은 모든 서버의 지속적인 전역 다시 로드 시작

  • 19:05 전역 아이볼 HTTP 오류율이 서비스 사용 불가 2.1%/전체 3.45%로 최고치 기록

  • 19:05 최초의 Traffic Manager 작업 복구 서비스

  • 19:11 전역 아이볼 HTTP 오류율이 서비스 사용 불가 1%/전체 1.96%로 절반으로 감소

  • 19:27 전역 아이볼 HTTP 오류율이 기준선 수준으로 감소

  • 19:29 DDoS 규칙 배포가 프로세스 포이즈닝의 잠재적 원인으로 확인됨

  • 19:34 DDoS 규칙이 완전히 비활성화됨

  • 19:43 엔지니어가 중단된 프로세스가 많은 서버의 일상적인 재시작을 중지시킴

  • 20:16 사고 대응 중단

아래에서는 Cloudflare의 내부 메트릭 중 일부가 미치는 영향에 대한 보기를 제공합니다. 첫 번째 그래프에는 포이즈닝을 겪고 있는 서비스에 도달할 수 없어 오류 응답이 전송된 모든 아이볼(외부 장치에서 인바운드) HTTP 요청의 비율이 나와 있습니다. 초기에는 요청의 0.5%로 증가했고, 이후 2.1%까지 증가했지만, 서비스 재로드로 인해 복구가 시작되었습니다.

오류를 더 폭넓게 볼 수 있도록, 원본 서버의 응답을 포함해 동일한 기간 동안 네트워크에서 아이볼로 반환된 모든 5xx 응답을 확인할 수 있습니다. 이 이벤트는 3.45%로 최고치를 기록했으며, Traffic Manager가 재라우팅 활동을 마치면서 19:25~20:00(UTC) 사이에 점진적으로 회복되는 모습을 더 명확하게 확인할 수 있습니다. 19:25(UTC)의 하락은 마지막 대규모 재로드와 일치하며, 이후 오류 증가는 주로 업스트림 DNS 제한 시간 초과 및 연결 제한으로 인해 발생하며, 이는 높은 부하와 불균형한 부하와 일치합니다.

TTFB에서 50, 90, 99 백분위수에서 측정한 결과를 보면, p99에서의 대기 시간이 거의 3배 가까이 증가한 것을 알 수 있습니다.

오류에 대한 기술적 설명 및 발생한 방식

이벤트 중 과도한 CPU를 사용했던 HTTP 요청 처리 프로세스의 전역 비율

이에 앞서 6월 20일 14:14~17:06(UTC) 사이에 저희는 점진적으로 네트워크의 새로운 DDoS 규칙을 활성화했습니다. Cloudflare에서는 최근 HTTP DDoS 공격을 완화하는 새로운 방법을 구축하고 있습니다. 이 방법은 공격의 일부로 잘못 식별된 합법적인 클라이언트가 어떻게든 진행할 수 있도록 하기 위해 레이트 리미팅과 쿠키의 조합을 사용하는 것입니다.

이 새로운 메서드를 사용하면 의심스러운 것으로 간주되는 HTTP 요청은 다음과 같은 주요 단계를 통해 실행됩니다.

  1. 유효한 쿠키가 있는지 확인하고, 그렇지 않으면 요청이 차단됩니다

  2. 유효한 쿠키가 있으면 나중에 평가할 쿠키 값에 따른 레이트 리미팅 규칙을 추가합니다

  3. 현재 적용된 DDoS 완화가 모두 실행된 후 레이트 리미팅 규칙이 적용됩니다

당사에서는 레이트 리미팅 규칙 없이 요청을 차단하는 것이 더 효율적이고, 그래서 다른 규칙 유형이 적용될 기회를 제공하기 때문에 이 '비동기' 워크플로우를 사용합니다.

따라서 전반적인 흐름은 다음과 같은 의사 코드로 요약할 수 있습니다.

레이트 리미팅 규칙을 평가할 때는 올바른 카운터를 조회하고 목표 속도와 비교하는 데 사용되는 각 클라이언트에 대한_키_를 만들어야 합니다. 일반적으로 이 키는 클라이언트 IP 주소이지만, 여기에 사용된 쿠키 값과 같은 다른 옵션도 사용할 수 있습니다. 이를 위해 저희는 실제로는 레이트 리미팅 로직의 기존 부분을 재사용했습니다. 의사 코드에서는 다음과 같은 모습입니다.

for (rule in active_mitigations) {
   // ... (ignore other rule types)
   if (rule.match_current_request()) {
       if (!has_valid_cookie()) {
           // no cookie: serve error page
           return serve_error_page();
       } else {
           // add a rate-limit rule to be evaluated later
           add_rate_limit_rule(rule);
       }
   }
}


evaluate_rate_limit_rules();

이 간단한 생성 함수에는 두 가지 문제가 있었는데, 특정 형식의 클라이언트 요청과 결합되면 HTTP 요청을 처리하는 프로세스에서 무한 루프가 초래되었습니다.

function get_cookie_key() {
   // Validate that the cookie is valid before taking its value.
   // Here the cookie has been checked before already, but this code is
   // also used for "standalone" rate-limit rules.
   if (!has_valid_cookie_broken()) { // more on the "broken" part later
       return cookie_value;
   } else {
       return parent_key_generator();
   }
}
  1. DDoS 로직에서 생성된 레이트 리미팅 규칙은 예상되지 않은 방식으로 내부 API를 사용하고 있습니다. 이로 인해 위 의사 코드의 parent_key_generatorget_cookie_key 함수 자체를 가리키게 되었으며, 이는 해당 코드 경로를 사용하면 함수가 무한정 자체를 호출한다는 것을 의미합니다

  2. 이러한 레이트 리미팅 규칙은 쿠키의 유효성을 검사한 후에만 추가되므로 이를 두 번째로 검사해도 동일한 결과가 나타나야 합니다. 문제는 여기서 사용된 has_valid_cookie_broken 함수가 실제로는 다른 것이며, 클라이언트가 일부 쿠키는 유효하지만 다른 쿠키는 유효하지 않은 여러 쿠키를 전송하는 경우 둘 다 동일하지 않을 수 있습니다.

따라서 이 두 가지 문제를 결합하면 손상된 유효성 검사 함수가get_cookie_key에 쿠키가 유효하지 않음을 알려주고 else 분기를 사용하여 동일한 함수를 계속해서 호출하게 됩니다.

많은 프로그래밍 언어가 이와 같은 루프를 방지하기 위해 마련한 보호 기능은 함수 호출 스택이 얼마나 깊어질 수 있는지에 대한 런타임 보호 제한입니다. 이미 이 제한에 도달한 후 함수를 호출하려고 하면 런타임 오류가 발생합니다. 위의 로직을 읽었을 때 초기 분석에 따르면 이 경우 한계에 도달하여 결국 동일한 함수 호출이 반복적으로 포함된 스택으로 요청이 오류를 일으켰습니다.

하지만 여기에서는 그렇지 않습니다. 이 로직이 작성된 Lua를 비롯한 일부 언어에서는 적절한 테일 콜(tail call)이라는 최적화도 구현합니다. 테일 콜은 함수가 취하는 마지막 행동이 다른 함수를 실행하는 것입니다. 나중에 상위 함수에 실행 컨텍스트를 반환하거나 로컬 변수를 사용하지 않을 것이 확실하므로 해당 함수를 스택의 다른 레이어로 추가하는 대신 스택의 최상위 프레임을 이 함수 호출로 대체할 수 있습니다.

최종 결과는 스택의 크기를 늘리지 않는 요청 처리 로직의 루프입니다. 대신 사용 가능한 CPU 리소스를 100% 사용하며 종료되지 않습니다. HTTP 요청을 처리하는 프로세스가 작업을 적용해야 하는 단일 요청을 수신하고 유효한 쿠키와 유효하지 않은 쿠키가 혼합되어 있으면 해당 프로세스는 포이즈닝 되어 더 이상 요청을 처리할 수 없게 됩니다.

모든 Cloudflare 서버에는 이러한 프로세스가 수십 개 있으므로 포이즈닝 된 프로세스 하나는 큰 영향을 미치지 않습니다. 하지만 다음과 같은 다른 현상이 발생하기 시작합니다.

  1. 서버의 CPU 사용률이 증가하면 Unimog는 서버가 수신하는 신규 트래픽의 양을 줄이고 트래픽을 다른 서버로 이동시키므로 특정 시점에 더 많은 신규 연결이 일부 프로세스가 포이즈닝된 서버에서 포이즈닝 된 프로세스가 적거나 없는 서버로 이동하여 CPU 사용률이 낮아집니다.

  2. 데이터 센터의 CPU 사용이 점진적으로 증가하면 Traffic Manager가 트래픽을 다른 데이터 센터로 리디렉션하기 시작합니다. 이러한 이동이 포이즈닝 된 프로세스를 수정하지 못하여 CPU 사용률이 높은 상태로 유지되므로 Traffic Manager는 점점 더 많은 트래픽을 다른 곳으로 리디렉션하게 됩니다.

  3. 두 경우 모두 리디렉션된 트래픽에는 프로세스를 포이즈닝 시키는 요청이 포함되므로, 이 리디렉션된 트래픽이 전송된 서버와 데이터 센터에서 같은 방식으로 장애가 시작됩니다.

몇 분 만에 여러 데이터 센터에 많은 포이즈닝 된 프로세스가 발생했고 Traffic Manager가 최대한 많은 트래픽을 포이즈닝 된 프로세스로부터 멀리 리디렉션했지만, 그 이상의 작업은 제한되었습니다. 이는 부분적으로는 내장된 자동화 안전 제한 때문이기도 하지만, 표적으로 삼을 수 있는 가용 용량이 충분한 데이터 센터를 찾는 것이 점점 더 어려워지고 있었기 때문이기도 했습니다.

프로세스가 포이즈닝 된 첫 사례는 17:47(UTC)에 있었고 사고가 선언된 지 5분 후 18:09(UTC)에 Traffic Manager가 유럽 밖으로 많은 트래픽을 라우팅하고 있었습니다.

18:09(UTC)를 기준으로 한 Traffic Manager 용량 작업의 요약 맵. 각 원은 트래픽이 해당 데이터 센터를 향해서 또는 반대로 라우팅되는 데이터 센터를 나타냅니다. 원의 색상은 해당 데이터 센터의 CPU 부하를 나타냅니다. 이들 사이의 주황색 리본은 다시 라우팅된 트래픽의 양과 시작/종료 위치를 나타냅니다.

HTTP 요청 서비스에서 CPU를 포화 상태로 만든 프로세스의 비율을 보면 그 이유를 알 수 있습니다. 트래픽이 가장 많은 시간대에 서유럽에서는 이미 10%, 동유럽에서는 4%의 용량이 사용되었습니다:

모든 HTTP 요청 처리 프로세스 중 CPU가 포화 상태인 비율(지역별)

일부 지역에서 부분적으로 포이즈닝 된 서버가 요청 부하로 인해 어려움을 겪었고, 나머지 프로세스가 이를 따라잡지 못해 Cloudflare에서는 최소한의 HTTP 오류 응답만 반환하게 됐습니다.

Cloudflare 전역 CPU 사용률이 일정 수준에 도달한 후 18:04(UTC)에 Cloudflare 엔지니어들이 자동으로 알림을 받아 조사를 시작했습니다. 저희 사고 대응팀원 다수가 이미 백본 네트워크 혼잡으로 인하여 진행되던 사고를 처리하고 있었으며, 초기에 네트워크 혼잡 이벤트와 관련 가능성이 있는지 조사했습니다. CPU 사용률이 가장 높은 위치가 트래픽이 가장 낮은 위치라는 사실을 깨닫기까지 어느 정도 시간이 걸렸고, 네트워크 이벤트가 트리거의 원인일 가능성이 배제되었습니다. 이 시점에서 초점은 두 가지 주요 흐름으로 옮겨갔습니다.

  1. 오염된 프로세스를 다시 시작하면 복구가 가능한지 평가하고, 복구가 가능한 경우 영향을 받은 서버에서 서비스를 대량으로 다시 시작하도록 유도하는 것으로 말입니다

  2. 이러한 CPU 포화 상태로 들어가는 프로세스의 트리거 식별하기

하나의 샘플 서버를 재시작하는 것이 도움이 되는 것을 검증한 것은 첫 번째 사고가 선언된 지 25분 후였습니다. 그로부터 5분 후에 저희는 처음에는 전체 데이터 센터를 한꺼번에 재시작했고, 식별 방법이 개선되면서 포이즈닝 된 프로세스가 많은 서버를 대규모로 재시작하기 시작했습니다. 일부 엔지니어는 영향을 받은 서버에서 영향을 받은 서비스를 정기적으로 재시작하는 한편, 다른 엔지니어는 트리거를 파악하기 위해 진행 중인 병행 노력에 동참하기 위해 이동했습니다. 19:36(UTC)에 새로운 DDoS 규칙이 전 세계적으로 비활성화되었으며, 대규모의 재시작과 모니터링을 한 번 더 실행한 후 사고가 해결된 것으로 선언되었습니다.

동시에, 사고에서 제시된 조건이 Traffic Manager에 있는 잠재적 버그를 트리거했습니다. 트리거되면 시스템에서는 무중단 재시작을 시작하고 활동을 중지하여 예외로부터 복구를 시도합니다. 해당 버그는 18:17(UTC)에 처음 트리거된 다음 18:3518:57(UTC) 사이에 여러 번 트리거되었습니다. 이 범위의 두 기간(18:3518:52 UTC 및 18:56~19:05 UTC) 동안 시스템에서는 새로운 트래픽 작업을 실행하지 않았습니다. 이는 가장 큰 영향을 받은 데이터 센터에서 서비스를 복구했지만, 거의 모든 트래픽은 여전히 데이터 센터에서 다시 라우팅되고 있었음을 의미합니다. 대기 중인 엔지니어에게 18:34(UTC)에 해당 문제에 대한 알림이 전송되었습니다. 19:05(UTC)까지 트래픽 팀에서는 수정 사항을 작성하고 테스트하여 배포했습니다. 복원 후 첫 번째 조치는 서비스 복원에 긍정적인 영향을 미쳤습니다.

복원 및 후속 조치

요청 포이즈닝이 네트워크에 즉각적으로 미친 영향을 해결하기 위해, Cloudflare에서는 조건을 트리거한 변경 사항이 식별되고 롤백될 때까지 해당 서비스의 대량 롤링 재시작을 실시했습니다. 새로운 유형의 DDoS 규칙을 활성화하는 변경 사항은 여전히 완전히 롤백되어 있으며, 규칙은 손상된 쿠키 유효성 검사를 수정하고 이러한 상황이 반복되지 않는다는 확신이 있을 때까지 재활성화되지 않게 됩니다.

Cloudflare에서는 이러한 사고를 매우 중요하게 생각하며, 사고가 미친 영향이 얼마나 컸는지 잘 알고 있습니다. 저희는 이러한 특정 상황을 해결하기 위해 취할 수 있는 몇 가지 조치와 향후 이러한 종류의 문제가 재발할 수 있는 위험을 확인했습니다.

  • 설계: DDoS 모듈에 사용 중인 레이트 리미팅 구현은 레거시 구성 요소이며, 고객이 인터넷 자산에 구성하는 레이트 리미팅 규칙은 더 새로운 기술과 보호 기능을 갖춘 최신 엔진을 사용합니다.

  • 설계: 우리는 테일 콜을 통해 무한히 루프되는 기능을 제한하기 위해 프로세스 포이즈닝을 경험한 서비스 내외의 옵션을 탐색하고 있습니다. 장기적으로 Cloudflare에서는 이 서비스를 완전히 대체하는 초기 구현 단계에 진입하고 있습니다. 이 대체 서비스의 설계를 통해 단일 요청의 중단 없는 총 실행 시간에 제한을 적용할 수 있습니다.

  • 프로세스: 새로운 규칙의 활성화는 검증을 위해 소수의 프로덕션 데이터 센터에서 처음으로 진행되었으며, 그 후 몇 시간 후에 모든 데이터 센터에서 진행되었습니다. 우리는 잠재적인 변경 관련 영향 반경을 최소화하기 위해 계속해서 준비 및 출시 절차를 개선해 나가겠습니다.

결론

Cloudflare에서는 CDN과 네트워크 서비스를 사용하는 상당한 고객에 영향을 미치는 두 번의 연속적인 사고를 경험했습니다. 첫 번째는 네트워크 백본 정체로, 시스템에서 자동으로 해결했습니다. 저희는 결함을 트리거하는 DDoS 규칙을 파악하고 비활성화하는 동안, 결함이 있는 서비스를 정기적으로 재시작하여 두 번째 사고를 완화했습니다. 이번 사고로 인해 발생한 중단으로 Cloudflare 서비스를 이용하는 Cloudflare 고객과 최종 사용자에게 불편을 드려 송구스럽게 생각하고 있습니다.

결함이 있는 서비스의 잠복 버그가 활성화되는 데 필요한 조건은 더 이상 Cloudflare의 운영 환경에서는 불가능하며, 저희는 최대한 빨리 추가 수정 및 감지 기능을 적용하려고 노력하고 있습니다.

Cloudflare에서는 전체 기업 네트워크를 보호하고, 고객이 인터넷 규모의 애플리케이션을 효과적으로 구축하도록 지원하며, 웹 사이트와 인터넷 애플리케이션을 가속화하고, DDoS 공격을 막으며, 해커를 막고, Zero Trust로 향하는 고객의 여정을 지원합니다.

어떤 장치로든 1.1.1.1에 방문해 인터넷을 더 빠르고 안전하게 만들어 주는 Cloudflare의 무료 앱을 사용해 보세요.

더 나은 인터넷을 만들기 위한 Cloudflare의 사명을 자세히 알아보려면 여기에서 시작하세요. 새로운 커리어 경로를 찾고 있다면 채용 공고를 확인해 보세요.
Post Mortem (KO)Outage (KO)

X에서 팔로우하기

Lloyd Wallis|@LloydW93
Cloudflare|@cloudflare

관련 게시물