IL-0340 Cloudflare · 2023-10-04networking
Cloudflare 1.1.1.1 리졸버가 4시간 동안 일부 유효한 DNS 질의에 SERVFAIL 을 냈다
루트 존은 모든 최상위 도메인으로 가는 길을 알려 주는 DNS 존이고, DNSSEC 로 서명돼 있다. Cloudflare 의 재귀 DNS 인프라는 루트 서버에 닿지 못할 때를 대비하고 해석을 빠르게 하려고 루트 존 사본을 받아 둔다. 새 루트 존은 보통 하루 두 번 나오고, 1.1.1.1 에서는 주 DNS 로직 위에서 도는 WebAssembly 앱 static_zone 이 새 판을 받아 내준다. static_zone 은 새 판을 파싱하는 데 성공해야 옛 판을 버린다.
2023년 9월 21일, 미리 알려진 루트 존 관리 변경으로 루트 존에 ZONEMD 레코드가 처음 들어왔다. ZONEMD 는 루트 존 내용의 체크섬이다. Cloudflare 는 루트 존을 바이너리 형식이 아닌 표현 형식으로 내부에 배포해 왔고, 에지에서 루트 존을 파싱하는 라이브러리는 모르는 레코드 형식을 만나면 파서 오류를 냈다. 존은 통째로 읽혀야 하므로 새 루트 존은 쓰이지 못했다. 메모리에 옛 판이 없는 서버는 요청마다 루트 서버에 직접 질의했지만, 옛 판이 있는 서버는 9월 21일 06:30 UTC 에 마지막으로 받은 판을 계속 썼다.
10월 4일 07:00 UTC 에 그 판의 DNSSEC 서명이 만료되자 일부 리졸버가 서명을 검증하지 못하고 SERVFAIL 을 냈다. 07:57 에 외부 신고가 들어왔고 08:03 에 내부 장애가 선언됐다. 08:50 의 override 규칙은 다른 노드로 넘긴 질의에 태그가 실리지 않아 효과가 없었다. 10:30 에 1.1.1.1 이 루트 존 파일을 미리 읽지 않게 하자 10:32 에 응답이 정상으로 돌아왔고, 11:02 에 장애를 닫았다. Cloudflare 는 공격이 아니라 내부 소프트웨어 오류였다고 밝혔다.
- 심각도
- –
- 영향 범위
- DNS resolution through 1.1.1.1 and products built on it, including WARP, Zero Trust and third-party DNS resolvers that use 1.1.1.1; SERVFAIL peaked at 15% of all queries
- 영향 사용자
- some users of 1.1.1.1
- 지역
- mainly larger data centers such as Ashburn, Virginia; Frankfurt, Germany; and Singapore
- 감지까지
- 1h 3m
- 복구까지
- 3h 32m
증상
2023년 10월 4일 07:00 UTC 부터 1.1.1.1 을 직접 쓰거나 WARP, Zero Trust, 또는 1.1.1.1 을 쓰는 다른 회사의 DNS 리졸버를 거치는 일부 사용자가 유효한 질의에 SERVFAIL DNS 응답을 받았다. 07:57 에 예상하지 못한 SERVFAIL 을 알리는 외부 신고가 처음 들어왔다.
평소에도 전체 질의의 3% 쯤은 DNSSEC 체인 문제, 권한 서버 연결 실패, 권한 서버의 느린 응답 같은 정당한 이유로 SERVFAIL 이 난다. 장애 중 SERVFAIL 은 전체 질의의 15% 까지 올랐고, Cloudflare 리졸버가 내는 SERVFAIL 의 비율은 12% 늘었다.
영향은 고르지 않았다. 주로 버지니아주 애시번, 독일 프랑크푸르트, 싱가포르처럼 큰 데이터 센터에 몰렸다. 9월 21일 이후 1.1.1.1 서비스를 재시작한 리졸버 서버는 루트 서버에 직접 질의했으므로 영향이 없었다. 큰 데이터 센터에는 그동안 재시작하지 않은 서버가 많았다. 리졸버가 TTL 이 지난 인기 레코드도 캐시에서 계속 내주는 serve stale 을 써서 전면 장애로 번지지는 않았다.
08:50 에 오래된 루트 존으로 응답하지 않게 하려고 override 규칙 phase = pre-cache set-tag rec_disable_static 을 배포했지만 SERVFAIL 은 그치지 않았다. 캐시 성능을 위해 다른 노드로 넘기는 질의에는 rec_disable_static 태그가 실리지 않았고, 그 노드의 static_zone 앱은 계속 오래된 정보로 답했다.
원인
- 촉발
- 9월 21일 루트 존에 ZONEMD 라는 새 리소스 레코드 형식이 처음 들어왔고, 1.1.1.1 리졸버가 이 레코드를 파싱하지 못해 새 루트 존을 쓰지 못했다. 10월 4일 07:00 UTC 에 계속 쓰던 9월 21일 판 루트 존의 DNSSEC 서명이 만료됐다.
감지
07:57 UTC 에 예상하지 못한 SERVFAIL 을 알리는 외부 신고가 처음 들어왔고, 08:03 에 Cloudflare 내부 장애가 선언됐다. static_zone 이 오래된 루트 존을 내주는 상황을 알리는 알림은 없었다.
조치
- 08:50 UTC 에 override 규칙으로 1.1.1.1 이 오래된 루트 존 파일로 응답하지 않게 하려 했다. 다른 노드로 넘긴 질의에는 rec_disable_static 태그가 실리지 않아 static_zone 앱이 계속 오래된 정보로 답했다.
- 10:30 UTC 에 1.1.1.1 이 루트 존 파일을 아예 미리 읽지 않게 했다. 10:32 에 응답이 정상으로 돌아왔다.
후속 조치
- static_zone 이 오래된 루트 존 파일을 내줄 때 알리는 알림을 더한다.
- 루트 존을 받아 와 내부에 배포하는 방식을 다시 검토해, 새 RRTYPE 을 문제없이 다루고 파이프라인이 잠깐 끊겨도 최종 사용자에게 보이지 않게 한다.
- 루트 존 파싱이 실패할 때 어떻게 되는지 충분히 테스트하지 않았으므로 테스트 범위와 관련 절차를 개선한다.
- RFC 8806 이 설명하는 대로 캐시한 루트 존 데이터의 수명을 관리해, 일정 시점이 지난 오래된 루트 존 사본을 쓰지 않게 한다.
교훈
Cloudflare 는 무엇이든 바뀌지 않으리라고 가정하지 말라는 것을 이 사고의 분명한 교훈으로 꼽았다. 최종 실행 파일에 들어가는 긴 라이브러리 사슬 어디에든 버그가 있거나, 입력이 바뀔 때 제대로 동작할 만큼 일찍 갱신되지 않은 것이 있을 수 있다고 했다. 회귀를 잡아내는 테스트와 입력이 바뀌어도 부드럽게 실패하는 시스템이 중요하다고 했다. DNS 와 BGP 처럼 인터넷에서 가장 중요한 시스템의 형식 변경은 영향을 줄 것이라고 늘 가정해야 한다고 했다. 오래된 루트 존을 내주는 것을 더 잘 감시했다면, 이미 있는 캐시 덕분에 영향이 없었을 것이라고 했다.
출처
참고 문서
관련 사례
IL-0250slack.com 에 DNSSEC 를 켠 뒤 일부 재귀 리졸버에서 slack.com 이름이 풀리지 않아 일부 사용자가 24시간 동안 Slack 에 접속하지 못했다Slack · 2021같은 원인 · 공통 재귀 DNS 리졸버 서비스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0340
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0340/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.