{"id":"IL-0340","org":"Cloudflare","date":"2023-10-04","docs":["https://www.internic.net/domain/root.zone"],"title":{"en":"Cloudflare's 1.1.1.1 resolver returned SERVFAIL to some valid DNS queries for four hours","ko":"Cloudflare 1.1.1.1 리졸버가 4시간 동안 일부 유효한 DNS 질의에 SERVFAIL 을 냈다"},"domain":["networking"],"impact":{"scope":"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","users":"some users of 1.1.1.1","region":"mainly larger data centers such as Ashburn, Virginia; Frankfurt, Germany; and Singapore"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Cloudflare's main lesson is never to assume an input will not change. Modern systems pull in long chains of libraries that may not be ready when their input changes, so they need tests that catch regressions and components that fail gracefully on new input, and format changes in DNS and BGP should be expected to have an impact. Cloudflare also writes that with better monitoring of the stale root zone, the existing caching would have prevented any impact.","ko":"Cloudflare 는 무엇이든 바뀌지 않으리라고 가정하지 말라는 것을 이 사고의 분명한 교훈으로 꼽았다. 최종 실행 파일에 들어가는 긴 라이브러리 사슬 어디에든 버그가 있거나, 입력이 바뀔 때 제대로 동작할 만큼 일찍 갱신되지 않은 것이 있을 수 있다고 했다. 회귀를 잡아내는 테스트와 입력이 바뀌어도 부드럽게 실패하는 시스템이 중요하다고 했다. DNS 와 BGP 처럼 인터넷에서 가장 중요한 시스템의 형식 변경은 영향을 줄 것이라고 늘 가정해야 한다고 했다. 오래된 루트 존을 내주는 것을 더 잘 감시했다면, 이미 있는 캐시 덕분에 영향이 없었을 것이라고 했다."},"related":[],"sources":[{"url":"https://blog.cloudflare.com/1-1-1-1-lookup-failures-on-october-4th-2023/","kind":"blog","archived":"https://web.archive.org/web/20260808165158/https://blog.cloudflare.com/1-1-1-1-lookup-failures-on-october-4th-2023/"}],"summary":{"en":"Cloudflare's recursive DNS infrastructure keeps its own copy of the DNS root zone, which is signed with DNSSEC, so that it can resolve names faster and still work if the root servers cannot be reached. Software in Cloudflare's core network retrieves new root zone versions, normally published twice a day, and redistributes them to data centers worldwide, where a WebAssembly app called static_zone running on top of the 1.1.1.1 DNS logic loads and serves them. static_zone keeps the latest version in memory and drops the old one only after a new one parses. Because Cloudflare distributed the root zone internally in its text presentation format rather than the binary format, the parser library at the edge returned a parser error on the unknown ZONEMD record instead of skipping it.\n\nAfter the ZONEMD record appeared on 21 September, the root zone was still retrieved and distributed as usual, but no 1.1.1.1 server could load the new versions. Servers that had a version in memory kept using the one from before the change indefinitely; servers started after the change had nothing to fall back on and sent each query to the root servers directly. At 07:00 UTC on 4 October the DNSSEC signatures in the 21 September copy expired, and servers still using it could no longer validate DNSSEC and answered SERVFAIL. Cloudflare declared an internal incident at 8:03 UTC. An override rule at 8:50 UTC was meant to stop answers from the stale root zone, but its tag was not forwarded with queries passed between nodes. At 10:30 UTC the team stopped 1.1.1.1 from preloading the root zone entirely, responses were normal again at 10:32 UTC, and the incident was closed at 11:02 UTC.","ko":"루트 존은 모든 최상위 도메인으로 가는 길을 알려 주는 DNS 존이고, DNSSEC 로 서명돼 있다. Cloudflare 의 재귀 DNS 인프라는 루트 서버에 닿지 못할 때를 대비하고 해석을 빠르게 하려고 루트 존 사본을 받아 둔다. 새 루트 존은 보통 하루 두 번 나오고, 1.1.1.1 에서는 주 DNS 로직 위에서 도는 WebAssembly 앱 static_zone 이 새 판을 받아 내준다. static_zone 은 새 판을 파싱하는 데 성공해야 옛 판을 버린다.\n\n2023년 9월 21일, 미리 알려진 루트 존 관리 변경으로 루트 존에 ZONEMD 레코드가 처음 들어왔다. ZONEMD 는 루트 존 내용의 체크섬이다. Cloudflare 는 루트 존을 바이너리 형식이 아닌 표현 형식으로 내부에 배포해 왔고, 에지에서 루트 존을 파싱하는 라이브러리는 모르는 레코드 형식을 만나면 파서 오류를 냈다. 존은 통째로 읽혀야 하므로 새 루트 존은 쓰이지 못했다. 메모리에 옛 판이 없는 서버는 요청마다 루트 서버에 직접 질의했지만, 옛 판이 있는 서버는 9월 21일 06:30 UTC 에 마지막으로 받은 판을 계속 썼다.\n\n10월 4일 07:00 UTC 에 그 판의 DNSSEC 서명이 만료되자 일부 리졸버가 서명을 검증하지 못하고 SERVFAIL 을 냈다. 07:57 에 외부 신고가 들어왔고 08:03 에 내부 장애가 선언됐다. 08:50 의 override 규칙은 다른 노드로 넘긴 질의에 태그가 실리지 않아 효과가 없었다. 10:30 에 1.1.1.1 이 루트 존 파일을 미리 읽지 않게 하자 10:32 에 응답이 정상으로 돌아왔고, 11:02 에 장애를 닫았다. Cloudflare 는 공격이 아니라 내부 소프트웨어 오류였다고 밝혔다."},"trigger":{"en":"On 21 September 2023, as a planned change in root zone management, a ZONEMD resource record, in effect a checksum of the zone's contents, was added to the root zone for the first time. On 4 October at 07:00 UTC the DNSSEC signatures in the last root zone copy that 1.1.1.1 had loaded expired.","ko":"9월 21일 루트 존에 ZONEMD 라는 새 리소스 레코드 형식이 처음 들어왔고, 1.1.1.1 리졸버가 이 레코드를 파싱하지 못해 새 루트 존을 쓰지 못했다. 10월 4일 07:00 UTC 에 계속 쓰던 9월 21일 판 루트 존의 DNSSEC 서명이 만료됐다."},"severity":"unknown","symptoms":["dns-resolution-failures","high-error-rate"],"timeline":{"detected_after":"1h 3m","resolved_after":"3h 32m"},"date_kind":"incident","detection":{"en":"External reports of unexpected SERVFAILs started coming in at 7:57 UTC, and Cloudflare declared an internal incident at 8:03 UTC. Nothing had alerted on static_zone serving a stale root zone during the two weeks before.","ko":"07:57 UTC 에 예상하지 못한 SERVFAIL 을 알리는 외부 신고가 처음 들어왔고, 08:03 에 Cloudflare 내부 장애가 선언됐다. static_zone 이 오래된 루트 존을 내주는 상황을 알리는 알림은 없었다."},"components":["recursive-dns-resolver"],"root_cause":"software-bug","remediation":[{"text":{"en":"Deployed an override rule meant to stop 1.1.1.1 from answering with the stale root zone, which did not cover forwarded queries","ko":"08:50 UTC 에 override 규칙으로 1.1.1.1 이 오래된 루트 존 파일로 응답하지 않게 하려 했다. 다른 노드로 넘긴 질의에는 rec_disable_static 태그가 실리지 않아 static_zone 앱이 계속 오래된 정보로 답했다."}},{"text":{"en":"Stopped 1.1.1.1 from preloading the root zone file entirely","ko":"10:30 UTC 에 1.1.1.1 이 루트 존 파일을 아예 미리 읽지 않게 했다. 10:32 에 응답이 정상으로 돌아왔다."}}],"action_items":[{"text":{"en":"Add alerts for when static_zone serves a stale root zone file","ko":"static_zone 이 오래된 루트 존 파일을 내줄 때 알리는 알림을 더한다."}},{"text":{"en":"Re-evaluate how the root zone is ingested and distributed internally so new record types are handled","ko":"루트 존을 받아 와 내부에 배포하는 방식을 다시 검토해, 새 RRTYPE 을 문제없이 다루고 파이프라인이 잠깐 끊겨도 최종 사용자에게 보이지 않게 한다."}},{"text":{"en":"Improve test coverage for what happens when the root zone fails to parse","ko":"루트 존 파싱이 실패할 때 어떻게 되는지 충분히 테스트하지 않았으므로 테스트 범위와 관련 절차를 개선한다."}},{"text":{"en":"Limit how long stale copies of the root zone can be used, following RFC 8806","ko":"RFC 8806 이 설명하는 대로 캐시한 루트 존 데이터의 수명을 관리해, 일정 시점이 지난 오래된 루트 존 사본을 쓰지 않게 한다."}}],"contributing":["failure-delayed-by-cache","unsafe-failure-mode","monitoring-blind-spot","test-coverage-gap"],"recurrence_of":null,"symptoms_text":{"en":"From 07:00 to 11:00 UTC on 4 October 2023, some users of Cloudflare's 1.1.1.1 DNS resolver received SERVFAIL responses to valid queries. This reached users of WARP and Zero Trust and of third-party DNS resolvers that use 1.1.1.1, and the first external reports of unexpected SERVFAILs arrived at 7:57 UTC.\n\nNormally about 3% of 1.1.1.1 answers are SERVFAIL, from real DNSSEC problems, unreachable or slow authoritative servers and similar causes. During the incident the rate of SERVFAIL responses grew by 12% and peaked at 15% of all queries. The failures were not spread evenly: they were concentrated in the larger data centers such as Ashburn, Virginia; Frankfurt, Germany; and Singapore, because many servers there had not restarted the 1.1.1.1 service in the previous two weeks. Serve stale (RFC 8767), which keeps answering popular records from cache after their TTL, prevented a total outage.\n\nThe failure surfaced two weeks after its cause. On 21 September the root zone started to carry a new record type, ZONEMD, and the resolver software could not parse it, so servers that already held a copy of the root zone in memory kept using it; that copy had last been pulled at 6:30 UTC on 21 September. Nothing went wrong until the DNSSEC signatures in that copy expired at 07:00 UTC on 4 October, after which DNSSEC validation failed. Servers restarted in between had no copy to fall back on and resolved through the root servers directly, so they did not fail.\n\nThe first fix did not work. At 8:50 UTC engineers deployed an override rule, phase = pre-cache set-tag rec_disable_static, to make the static_zone app stop answering from the stale root zone. The tag rec_disable_static was not carried on queries that a node forwards to another node when it misses its own cache, so static_zone kept serving stale data until the team stopped 1.1.1.1 from preloading the root zone at all at 10:30 UTC.","ko":"2023년 10월 4일 07:00 UTC 부터 1.1.1.1 을 직접 쓰거나 WARP, Zero Trust, 또는 1.1.1.1 을 쓰는 다른 회사의 DNS 리졸버를 거치는 일부 사용자가 유효한 질의에 SERVFAIL DNS 응답을 받았다. 07:57 에 예상하지 못한 SERVFAIL 을 알리는 외부 신고가 처음 들어왔다.\n\n평소에도 전체 질의의 3% 쯤은 DNSSEC 체인 문제, 권한 서버 연결 실패, 권한 서버의 느린 응답 같은 정당한 이유로 SERVFAIL 이 난다. 장애 중 SERVFAIL 은 전체 질의의 15% 까지 올랐고, Cloudflare 리졸버가 내는 SERVFAIL 의 비율은 12% 늘었다.\n\n영향은 고르지 않았다. 주로 버지니아주 애시번, 독일 프랑크푸르트, 싱가포르처럼 큰 데이터 센터에 몰렸다. 9월 21일 이후 1.1.1.1 서비스를 재시작한 리졸버 서버는 루트 서버에 직접 질의했으므로 영향이 없었다. 큰 데이터 센터에는 그동안 재시작하지 않은 서버가 많았다. 리졸버가 TTL 이 지난 인기 레코드도 캐시에서 계속 내주는 serve stale 을 써서 전면 장애로 번지지는 않았다.\n\n08:50 에 오래된 루트 존으로 응답하지 않게 하려고 override 규칙 phase = pre-cache set-tag rec_disable_static 을 배포했지만 SERVFAIL 은 그치지 않았다. 캐시 성능을 위해 다른 노드로 넘기는 질의에는 rec_disable_static 태그가 실리지 않았고, 그 노드의 static_zone 앱은 계속 오래된 정보로 답했다."},"updated_at":"2026-11-14T12:33:59.537239Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0340","org_slug":"cloudflare"}