2023년 1월 24일, 서비스 토큰을 관리하는 코드를 릴리스하는 도중 에러가 발생해 여러 Cloudflare 서비스가 121분 동안 이용 불가능한 상태가 되었습니다. 이 사고로 Cloudflare의 Workers 플랫폼 요소, Zero Trust 솔루션, 콘텐츠 전송 네트워크(CDN)의 제어창 기능 등 다양한 Cloudflare 제품의 성능이 저하되었습니다.
Cloudflare는 서비스 토큰 기능을 제공해 자동화된 서비스가 다른 서비스에 인증할 수 있도록 합니다. 고객들은 서비스 토큰을 사용해서, 예를 들면 데이터 센터 내에서 구동 중인 애플리케이션과 퍼블릭 클라우드 제공자 내의 자원 간의 상호작용에 보안을 확보할 수 있습니다. 이번 릴리스의 내용 중 일부로서 Cloudflare는 관리자에게 토큰이 마지막으로 사용된 시기를 보여 주는 주요 기능을 도입해 사용자들이 사용되지 않은 토큰을 안전하게 정리할 수 있도록 했습니다. 이 변경사항으로 인해 서비스 토큰과 관련된 다른 메타데이터가 의도치 않게 덮어씌워졌고, 영향을 받은 계정의 토큰을 사고 기간 동안 사용할 수 없게 되었습니다.
이 릴리스가 다른 서비스에 영향을 미친 원인은 Cloudflare 자체가 Cloudflare 상에서 가동하고 있기 때문입니다. 서비스 토큰은 계정의 인증 기능에 영향을 주고, 영향을 받은 계정 중 두 개는 여러 Cloudflare 서비스를 구동하고 있었습니다. 이 계정들의 서비스 토큰이 덮어쓰기되자, 해당 계정 상에서 구동되는 서비스에서 요청 실패와 여러 다른 에러가 발생하기 시작했습니다.
제한된 일부의 고객과 최종 사용자가 이 사고의 직접적인 영향을 받았으며 다른 고객들 또한 서비스 악화를 경험하기는 하였지만, Cloudflare의 네트워크 및 서비스에 가해진 전반적인 영향은 크지 않았습니다. 그렇지만, Cloudflare는 영향을 받은 고객들은 많은 불편을 겪었다는 사실을 알고 있습니다. Cloudflare는 여러분에게 왜 이러한 일이 일어났고 이러한 일이 다시 일어나지 않도록 방지하기 위해 Cloudflare가 어떠한 조치를 취하고 있는지 알 수 있도록 무엇이 문제였는지 문서로 남기려 합니다.
서비스 토큰이란 무엇인가요?
사용자는 일반적으로 애플리케이션이나 ID 공급자에 로그인할 때 사용자 이름과 비밀번호를 입력합니다. 비밀번호를 통해 해당 사용자는 자신이 사용자 이름을 소유하고 있으며 해당 서비스를 사용할 수 있어야 한다는 점을 입증할 수 있습니다. 하드 키나 장치 상태와 같은 추가적인 인증 계층이 추가될 수는 있지만, 이 워크플로우는 인간이 서비스에게 자신이 자신이 주장하는 그 인물이 맞다는 것을 증명하는 과정으로 이루어져 있습니다.
하지만, 서비스에 인증을 해야 하는 사용자에는 인간만이 있는 것은 아닙니다. 애플리케이션도 자주 다른 애플리케이션과 대화를 해야 합니다. 예를 들어, 사용자에게 예정된 여행 계획을 보여주는 애플리케이션을 구축했다고 상상해 봅시다.
항공사는 자신들의 시스템에 항공편과 비행 시간에 대한 정보를 가지고 있습니다. 이들은 모든 개별적인 비행에 대한 정보를 인터넷에 공개하고 싶어하지 않으며, 여러분의 애플리케이션을 자사 개인 네트워크에 초대하고 싶어하지도 않습니다. 마찬가지로 호텔에서도 방 예약에 대한 정보를 유효하고 인증받은 서드파티 서비스에만 제공한다는 것을 확실히 하고자 합니다.
여러분의 애플리케이션에는 이러한 외부 시스템에 인증을 할 믿을 수 있는 방법이 필요합니다. 서비스 토큰은 서비스의 사용자 이름과 비밀번호와도 같은 역할을 해서 이 문제를 해결합니다. 사용자 이름과 비밀번호처럼 서비스 토큰은 클라이언트 ID와 클라이언트 비밀이라는 두 부분으로 이루어져 있습니다. 인증 요청과 함께 ID와 비밀이 전송되어야 합니다. 또한 토큰에는 기간이 할당되며, 이 기간이 지나면 토큰이 유효성을 잃고 순환되어야 합니다. 여러분은 애플리케이션에 서비스 토큰을 부여하고, 필요한 업스트림 시스템에서 해당 토큰을 인증하면 여러분의 서비스가 항공편 및 호텔 정보를 가져와 공동 리포트를 통해 최종 사용자에게 이를 제공할 수 있습니다.
관리자가 Cloudflare 서비스 토큰을 생성하면 Cloudflare는 클라이언트 ID와 클라이언트 비밀 쌍을 생성합니다. 그런 다음 고객들은 요청하는 서비스가 보호된 자원에 접근해야 할 때 두 값 모두를 HTTP 헤더로 전송하도록 서비스를 구성할 수 있습니다. 요청하는 서비스는 Cloudflare 네트워크 상에서 Worker의 형태나 퍼블릭 클라우드 제공자와 같은 별도의 장소 등 어떠한 환경에서도 구동할 수 있습니다. 고객들은 Cloudflare의 역방향 프록시 뒤에서 보호받는 해당 자원을 배포해야 합니다. Cloudflare의 네트워크는 구성된 서비스로 향하는 모든 요청의 HTTP 헤더를 검사합니다. HTTP 헤더가 존재하는 경우, Cloudflare는 헤더의 진실성을 인증한 뒤 요청을 차단하거나 진행할 수 있도록 허용합니다. 또한, Cloudflare는 이 인증 이벤트를 기록합니다.
사고 타임라인
모든 타임스탬프는 UTC입니다
2023/01/24 16:55에 Access 엔지니어링 팀이 서비스 토큰 메타데이터를 의도치 않게 덮어쓰기 시작한 릴리스를 실행해 사고가 발생했습니다.
2023/01/24 17:05에 Access 엔지니어링 팀의 일원이 이와는 관련이 없는 문제를 인지하고 릴리스를 롤백해 서비스 토큰 메타데이터의 추가적인 덮어쓰기를 중단했습니다.
서비스 토큰 값은 서비스 토큰 그 자체가 업데이트되기 전까지 Cloudflare 네트워크 상에서 업데이트되지 않습니다(자세한 내용은 후술). 이로 인해 메타데이터가 덮어쓰기된 서비스 토큰에 막대한 영향을 미쳤습니다.
2023/01/24 17:50: Cloudflare WARP의 첫 유효하지 않은 서비스 토큰이 Cloudflare의 전역 네트워크에 동기화되었습니다. WARP 및 Zero Trust 사용자들이 그 영향을 받기 시작했습니다.
WARP 장치 상태 업로드가 0으로 떨어져 내부 경보가 발동했습니다
2023/01/24 18:12에 성공적인 WARP 장치 상태 업로드 수가 크게 떨어졌기 때문에 사고가 선언되었습니다.
2023/01/24 18:19: Cloudflare API의 첫 유효하지 않은 서비스 토큰이 Cloudflare의 전역 네트워크에 동기화되었습니다. 캐시 제거, Cache Reserve, 이미지 및 R2에 영향이 나타나기 시작했습니다. 이 제품에 대한 경보가 발동하였으며, 이를 통해 사고의 더 큰 범위가 드러났습니다.
2023/01/24 18:21에 덮어쓰기된 서비스 토큰이 초기 조사 도중 발견되었습니다.
2023/01/24 18:28에 모든 영향을 받은 제품을 포함하도록 사고의 수준이 격상되었습니다.
2023/01/24 18:51에 Cloudflare WARP 계정의 서비스 토큰을 원래의 값으로 되돌리는 초기 해결책이 파악되어 실행되었습니다. WARP와 Zero Trust가 이 조치의 영향을 받았습니다. WARP와 Zero Trust가 사고의 영향에서 벗어났습니다.
2023/01/24 18:56에 동일한 해결책이 Cloudflare API 계정에 적용되었습니다. 캐시 제거, Cache Reserve, 이미지와 R2가 이 조치의 영향을 받았습니다. 캐시 제거, Cache Reserve, 이미지와 R2가 사고의 영향에서 벗어났습니다.
2023/01/24 19:00에 Cloudflare API 계정에 업데이트가 이루어졌으며, 이 과정에서 Cloudflare API 계정이 잘못 덮어쓰기되었습니다. 캐시 제거, Cache Reserve, 이미지와 R2에 대한 영향이 다시 시작되었습니다. 이후 모든 Cloudflare 내부 계정에 대한 변경 사항이 사고가 해결될 때까지 잠겼습니다.
2023/01/24 19:07에 Cloudflare API가 올바른 서비스 토큰 값을 포함하도록 업데이트되었습니다. 캐시 제거, Cache Reserve, 이미지와 R2가 사고의 영향에서 벗어났습니다.
2023/01/24 19:51에 모든 영향을 받은 계정의 서비스 토큰이 데이터베이스 백업에서 복구되었습니다. 사고가 종료되었습니다.
무엇이 릴리스되었고 사고의 원인이 무엇이었나요?
Access 팀은 서비스 토큰에 "마지막으로 확인된 날짜" 필드를 추가하는 새로운 변경 사항을 출시하고 있었습니다. 이는 어떤 서비스 토큰이 활성화되어 사용 중인지 판별하는 것을 돕기 위한, 많은 요청을 받았던 주요 기능이었습니다.
문제가 있습니까?
"마지막으로 확인된 날짜" 값은 계정의 로그인 이벤트 Kafka 대기열의 모든 새로운 로그인 이벤트를 스캔해 얻어졌습니다. 서비스 토큰을 사용하는 로그인 이벤트가 감지되면 해당하는 서비스 토큰의 마지막으로 확인된 날짜 값이 업데이트되었습니다.
서비스 토큰의 "마지막으로 확인된 날짜" 값을 업데이트하기 위해서는 해당하는 서비스 토큰의 정보를 수집하기 위해 읽기/쓰기 트랜잭션이 이루어집니다. 서비스 토큰 읽기 요청은 보안상의 이유로 기본적으로 "클라이언트 비밀" 값을 삭제합니다. 그 뒤, 서비스 토큰에 "마지막으로 확인된 날짜" 업데이트가 이루어질 때 "클라이언트 비밀" 값을 포함하지 않는 읽기 정보를 사용해 쓰기가 이루어지는 동안 빈 "클라이언트 비밀" 값을 서비스 토큰에 업데이트했습니다.
다음은 올바른 서비스 토큰 값과 올바르지 않은 서비스 토큰 값의 예입니다.
Access 서비스 토큰 값 예시
서비스 토큰 "클라이언트 비밀" 데이터베이스에는 "null이 아님" 체크가 있었지만, 이 경우 빈 텍스트 문자열은 null 값으로 트리거를 발생시키지 않았습니다.
{
"1a4ddc9e-a1234-4acc-a623-7e775e579c87": {
"client_id": "6b12308372690a99277e970a3039343c.access",
"client_secret": "<hashed-value>", <-- what you would expect
"expires_at": 1698331351
},
"23ade6c6-a123-4747-818a-cd7c20c83d15": {
"client_id": "1ab44976dbbbdadc6d3e16453c096b00.access",
"client_secret": "", <--- this is the problem
"expires_at": 1670621577
}
}
이 버그 때문에 "마지막으로 확인된 날짜" 릴리스가 적용된 10분 동안 인증에 서비스 토큰을 사용한 모든 Cloudflare 계정의 "클라이언트 비밀" 값이 빈 문자열로 설정되었습니다. 이 빈 "클라이언트 비밀"이 인증에 사용되려면 그 뒤 서비스 토큰이 변경되어야 했습니다. 이 상황에 처한 계정은 총 4개 존재했으며, 모두 Cloudflare 내부 계정이었습니다.
이 문제를 어떻게 해결했나요?
임시 해결책으로 Cloudflare는 서비스 토큰이 덮어쓰기된 계정에 대해 올바른 토큰 값을 수동으로 복구할 수 있었습니다. 이를 통해 영향을 받은 Cloudflare 서비스 상에서의 즉각적인 영향이 중단되었습니다.
그 뒤, 데이터베이스 팀이 이전 데이터베이스 사본에서 모든 영향을 받은 계정의 서비스 토큰을 복구하는 해결책을 적용할 수 있었습니다. 이를 통해 이 사고로 인해 발생한 모든 영향이 종료되었습니다.
왜 이것이 다른 Cloudflare 서비스에 영향을 주었나요?
서비스 토큰은 계정의 인증 기능에 영향을 줍니다. 영향을 받은 계정 중 두 개는 여러 Cloudflare 서비스를 구동하고 있었습니다. 이 계정들의 서비스 토큰이 덮어쓰기되자, 해당 계정 상에서 구동되는 서비스에서 요청 실패와 여러 다른 에러가 발생하기 시작했습니다.
Cloudflare WARP 등록
Cloudflare는 모든 사용자가 장치에 설치해 인터넷 트래픽의 프라이버시를 향상할 수 있는 모바일과 데스크탑 정방향 프록시인 Cloudflare WARP(Cloudflare의 "1,1,1,1" 앱)을 제공합니다. 누구나 Cloudflare 계정 없이도 이 서비스를 설치할 수 있으며, Cloudflare는 활동을 사용자와 매핑하는 로그를 보관하지 않습니다.
사용자가 WARP를 사용해 접속하면 Cloudflare는 장치에서 키를 수령하고 인증하는 서비스를 사용하여 장치의 등록을 인증합니다. 그 다음, 해당 서비스는 새롭게 등록된 장치에 Cloudflare 네트워크 액세스 권한을 제공하라고 Cloudflare의 네트워크에게 알려주는 다른 시스템과 통신합니다
사고 도중 등록 서비스는 더 이상 장치를 인증해 줄 Cloudflare 네트워크 상의 서비스와 통신을 할 수 없었습니다. 그 결과 사용자들은 새로운 장치를 등록하거나 또는 앱을 새로운 장치에 설치할 수 없었으며, 앱의 새로운 버전으로 업그레이드(이때에도 재등록이 발생)할 때도 문제를 겪었을 수 있습니다.
Cloudflare Zero Trust 장치 상태 및 재인증 정책
Cloudflare는 고객들이 장치에 에이전트가 있는지의 유무와 관계없이 배포할 수 있는 포괄적인 Zero Trust 솔루션을 제공합니다. 일부 사용 사례는 Cloudflare 에이전트를 장치에 사용하고 있을 때에만 사용 가능합니다. 이 에이전트는 동일한 Cloudflare WARP 솔루션의 Enterprise 버전으로, 에이전트가 장치 상태를 전송하거나 수신해야 할 때 유사한 성능 저하를 겪었습니다. 이는 Cloudflare Zero Trust의 세 가지 사용 사례에 영향을 미쳤습니다.
먼저, 소비자 제품과 유사하게 새로운 제품을 등록할 수 없었으며, 기존 장치를 취소할 수 없었습니다. 또한, 관리자는 등록된 장치의 설정을 변경할 수 없었습니다. 이 사례 모두에서 사용자에게 오류가 표시되었습니다.
두 번째로, Cloudflare의 Zero Trust 솔루션을 사용해 기존의 사설 네트워크를 대체한 많은 고객들은 세션 지속 시간 정책을 사용해 사용자의 신원을 지속적으로 인증하는 규칙을 추가하기도 했습니다. 이 규칙의 목적은 사용자들에게 재인증을 하도록 해 오래 지속된 세션이 내부 시스템에 대한 지속적인 접근 권한을 가지는 것을 막는 것입니다. 장치의 에이전트가 Cloudflare의 제어창 신호를 기반으로 사용자에게 재인증을 하라는 알림을 보냅니다. 사고 도중 이러한 신호가 전송되지 않았으며 사용자들은 성공적으로 재인증을 할 수 없었습니다.
마지막으로, 장치 상태 규칙을 사용했던 고객들 역시 그 영향을 경험했습니다. 장치 상태 규칙은 Access 혹은 Gateway 정책을 사용하는 고객들이 WARP 에이전트를 사용해 장치가 기업 규정 준수 규칙을 만족할 수 있도록 지속적으로 적용할 수 있게 해 주었습니다.
에이전트는 장치 상태를 관리하는 Cloudflare 서비스에 이러한 신호를 전송합니다. Cloudflare의 Zero Trust 액세스 제어 제품은 이 신호를 수신하고 다른 규칙과 함께 이를 평가해 사용자가 주어진 자원에 접근할 수 있는지를 판단하기 위해 서비스 토큰을 사용합니다. 이 사고 도중에는 이러한 규칙의 기본값이 차단 조치로 설정되었으며, 이는 즉 이러한 정책을 통해 변경된 트래픽이 사용자에게는 손상된 것처럼 나타났다는 것입니다. 일부 사례에서 이는 장치에서 모든 인터넷으로 향하는 트래픽이 완전히 차단되어 사용자가 아무것도 접근할 수 없게 되었다는 것을 의미했습니다.
Cloudflare Gateway는 사용자를 위해 5분마다 장치 상태를 캐싱해 Gateway 정책을 적용합니다. Gateway가 각 요청마다 장치 상태를 검증할 필요 없이 정책을 적용할 수 있도록 장치 상태가 캐싱됩니다. 어떤 Gateway 정책 유형이 매칭되었는지에 따라 사용자는 두 가지 다른 결과를 경험했습니다. 네트워크 정책과 매칭되었다면 사용자는 연결 끊김을 경험했고, HTTP 정책과 매칭되었다면 사용자에게 5XX 에러 페이지가 표시됐습니다. 사고가 해결될 때까지 Cloudflare의 분당 5XX 에러 수는 기준선보다 최대 50,000건 상승했으며, 1050만 건 이상의 상태 읽기 에러가 발생했습니다.
분당 게이트웨이 5XX 에러 수
총 Gateway 장치 상태 에러 수
Cloudflare R2 스토리지 및 Cache Reserve
Cloudflare R2 스토리지는 개발자가 비정형 대용량 데이터를 일반적인 클라우드 스토리지 서비스와 관련된 비싼 송신 대역폭 수수료 없이 저장할 수 있도록 해 줍니다.
사고 도중 R2 서비스는 Cloudflare 인프라의 다른 부분으로 아웃바운드 API 요청을 할 수 없었습니다. 그 결과 R2 사용자들이 R2에 요청을 할 때 요청 실패율이 상승했습니다.
또한, 많은 Cloudflare 제품은 데이터 저장에 R2를 사용하고 있었고 영향을 받았습니다. 예를 들어, Cache Reserve 사용자들이 이 기간 동안 영향을 받았으며 주 캐시에 저장돼 있지 않은 모든 항목에서 원본 부하가 상승했음을 경험했습니다. 이 사고 동안 Cache Reserve 서비스로의 읽기/쓰기 작업 대부분이 영향을 받아 Cache Reserve로 들어오고 나가는 항목이 실패하게 되었습니다. 하지만, Cache Reserve는 R2 에러를 감지하는 경우 고객 원본으로 폴백하기 때문에 이 기간 동안 사용자 트래픽은 여전히 처리될 수 있었습니다.
Cloudflare 캐시 제거
Cloudflare의 콘텐츠 전송 네트워크(CDN)는 전 세계 Cloudflare 데이터 센터의 Cloudflare 네트워크에 있는 인터넷 자산의 콘텐츠를 캐싱해 응답을 받기 위해 사용자의 요청이 이동해야 하는 거리를 줄입니다. 일부 상황에서 고객들은 Cloudflare가 캐싱한 것을 제거하고 다른 데이터로 대체하려고 합니다.
관리자가 Cloudflare의 네트워크와 상호작용하는 Cloudflare 제어창은 캐시 제거 서비스에 인증 및 접속하기 위해 서비스 토큰을 사용합니다. 사고 도중 서비스 토큰이 유효하지 않은 동안 많은 제거 요청이 실패했습니다. Cloudflare는 초당 평균 20개, 최대 초당 70개의 제거 요청이 실패하는 영향을 확인했습니다.
이러한 일이 다시 일어나지 않도록 Cloudflare는 어떠한 일을 하고 있나요?
Cloudflare는 이러한 사고를 진지하게 취급하고, 이 사고가 미친 영향을 이해하고 있습니다. Cloudflare는 미래에 유사한 문제가 발생할 위험에 대처하기 위해 취할 수 있는 여러 단계들을 확인했습니다. 이 사고의 결과로, Cloudflare는 다음과 같은 교정 계획을 도입하고 있습니다.
테스트: Access 엔지니어링 팀은 새로운 주요 기능을 출시하기 전에 서비스 토큰 덮어쓰기와 관련된 유사한 문제를 자동으로 잡아낼 단위 테스트를 도입할 것입니다.
경보: Access 팀은 기능을 완전히 출시하기 전에 문제를 감지하기 위해 서비스 토큰 인증 요청 실패 횟수가 급격히 증가할 때 자동으로 알려주는 경보를 구현할 것입니다.
절차: Access 팀은 특정 데이터베이스 테이블에서 더 빠른 롤백이 가능한 절차상의 개선을 확인했습니다.
도입: 모든 관련된 데이터베이스 필드는 기존의 "null 아님 검사"와 더불어 빈 문자열에 대한 검사를 포함하도록 업데이트될 것입니다
이 사고로 인해 발생한 중단으로 여러 Cloudflare 서비스 고객에게 불편을 드려 송구스럽게 생각하고 있습니다. Cloudflare는 추후 더 나은 안전성을 보장하고 이런 문제가 다시는 발생하지 않도록 이러한 개선사항을 능동적으로 적용하고 있습니다.