1P by GN⁺ 10시간전 | ★ favorite | 댓글 1개
  • 2026년 1월 8일, Cloudflare의 1.1.1.1 서비스 업데이트가 DNS 응답 내 레코드 순서를 바꾸면서 전 세계 일부 사용자에게 DNS 해석 실패가 발생
  • 문제의 원인은 CNAME 레코드가 A/AAAA 레코드 뒤로 이동한 코드 변경으로, 일부 DNS 클라이언트 구현이 순서 의존적이었기 때문
  • 특히 glibc의 getaddrinfo 함수Cisco 스위치의 DNSC 프로세스가 영향을 받아, 후자의 경우 재부팅 루프까지 발생
  • RFC 1034는 “CNAME이 앞에 올 수 있다(possibly preface)”고만 명시해, 레코드 순서에 대한 명확한 표준이 부재
  • Cloudflare는 CNAME을 항상 먼저 배치하는 방식으로 복귀하고, IETF에 명확한 표준 정의를 위한 Internet-Draft를 제출

1. 사건 개요

  • 2026년 1월 8일, 1.1.1.1의 메모리 사용량 절감 업데이트가 배포되면서 DNS 응답 내 레코드 순서 변경이 발생
    • 변경은 2025년 12월 2일 코드베이스에 도입, 12월 10일 테스트 환경 배포, 2026년 1월 7일 글로벌 릴리스 시작
    • 1월 8일 18:19 UTC에 사고 선언, 18:27에 롤백 시작, 19:55에 복구 완료
  • 대부분의 현대 DNS 클라이언트는 응답 내 레코드 순서를 무시하지만, 일부 구현은 CNAME이 항상 먼저 와야 한다고 가정
  • 이 순서가 바뀌자 일부 클라이언트가 응답을 비어 있는 것으로 처리하며 해석 실패 발생

2. CNAME 체인과 캐시 동작

  • 도메인 질의 시, 예를 들어 www.example.com은 여러 CNAME 체인을 거쳐 최종 IP로 해석됨
    • 예: www.example.com → cdn.example.com → server.cdn-provider.com → 198.51.100.1
  • 각 CNAME 링크는 TTL(Time-To-Live) 을 가지며, 일부만 만료될 수 있음
  • 1.1.1.1은 만료된 부분만 재조회하고, 기존 캐시와 새 레코드를 병합하여 응답 구성

3. 코드 변경의 세부 내용

  • 기존 코드에서는 CNAME 체인을 먼저 삽입하고, 그 뒤에 A/AAAA 레코드를 추가
    • answer_rrs.extend_from_slice(&self.records); // CNAMEs first
  • 변경된 코드에서는 CNAME을 마지막에 추가
    • entry.answer.extend(self.records); // CNAMEs last
  • 이로 인해 응답 내에서 CNAME이 하단으로 이동, 일부 클라이언트가 이를 처리하지 못함

4. 영향을 받은 구현 사례

  • glibc의 getaddrinfo 함수는 응답을 순차적으로 파싱하며, CNAME이 먼저 등장해야 다음 이름을 추적 가능
    • CNAME이 뒤에 오면 “예상 이름”이 갱신되지 않아 결과가 비어 있음
  • Cisco Catalyst 스위치 3개 모델DNSC 프로세스도 영향을 받아, 1.1.1.1을 사용 중인 경우 치명적 오류로 재부팅 루프 발생
    • Cisco는 관련 서비스 문서를 공개

5. 영향을 받지 않은 구현

  • systemd-resolved는 응답을 OrderedSet 구조체로 파싱해, CNAME 위치와 관계없이 전체 집합을 탐색 가능
    • 따라서 레코드 순서 변경에도 정상 동작

6. RFC 1034의 모호성

  • RFC 1034(1987)은 “응답은 하나 이상의 CNAME으로 preface될 수 있다”고 기술
    • 그러나 MUST/SHOULD 같은 규범적 키워드가 없어 명시적 요구사항이 아님
  • RFC 2119(1997) 이후에야 이러한 키워드가 표준화되어, 당시 문서는 명확한 의무 표현이 부재
  • Cloudflare는 초기 구현에서 CNAME을 먼저 배치했으나, 테스트로 이를 보장하지는 않음

7. RRset과 메시지 섹션의 구분

  • RFC 1034 §3.6은 RRset(동일 이름·타입·클래스의 레코드 집합) 의 순서는 중요하지 않다고 명시
  • 그러나 서로 다른 RRset 간의 순서에 대해서는 언급이 없음
  • §6.2.1의 예시도 동일 이름의 A 레코드 두 개만 다루며, CNAME과 A의 상대적 순서는 다루지 않음
  • 따라서 CNAME과 A 레코드 간 순서 정의가 공백 상태

8. CNAME 체인 내부 순서 문제

  • CNAME이 여러 단계일 경우, 체인 자체의 순서가 뒤섞여도 순차 파싱이 실패
    • 예: cdn.example.com CNAME server.cdn-provider.com이 먼저 오면, www.example.com CNAME cdn.example.com을 찾지 못함
  • RFC 1034은 CNAME 체인 순서에 대한 요구사항도 없음

9. 리졸버의 동작 기준

  • RFC 1034 §5.2.2는 “리졸버는 CNAME을 만나면 새 이름으로 쿼리를 재시작해야 한다”고 명시
  • 그러나 이는 전체 리졸버(예: 1.1.1.1) 를 대상으로 한 설명이며,
    glibc 같은 스텁 리졸버는 이 로직을 구현하지 않음
  • 결과적으로 일부 스텁 리졸버가 RFC의 기대 동작을 따르지 않음

10. DNSSEC의 명시적 규정과 비교

  • RFC 4035(DNSSEC)는 RRSIG 레코드 포함 우선순위를 “MUST”로 명시
    • 단, 이는 “포함 여부”에 대한 규정이지 “순서”에 대한 것은 아님
  • DNSSEC은 명확한 포함 규칙을 제공하지만, 비서명 영역(Unsigned zones) 에서는 여전히 RFC 1034의 모호성이 남음

11. Cloudflare의 결론과 조치

  • RFC상 CNAME 순서가 필수는 아니지만, 일부 클라이언트가 이를 전제하므로
    CNAME을 항상 먼저 배치하는 정책으로 복귀
  • 향후 동일 문제 방지를 위해 IETF에 Internet-Draft 제출
  • Cloudflare는 이 사건을 통해 40년 된 프로토콜의 모호성이 여전히 실무에 영향을 미침을 확인

12. 부가 정보

  • Cloudflare는 1.1.1.1을 포함한 Connectivity Cloud를 통해
    기업 네트워크 보호, 인터넷 규모 애플리케이션 가속, DDoS 방어, Zero Trust 구현 지원을 제공
  • 무료 앱 1.1.1.1을 통해 더 빠르고 안전한 인터넷 접속 가능
Hacker News 의견들
  • 나는 RFC의 문구가 그렇게 모호하지 않음을 느꼈음
    “possibly preface”라는 표현은 “CNAME 레코드가 있다면 그걸 먼저 붙인다”는 의미로 읽히며, “원하면 아무 데나 붙여도 된다”는 뜻으로는 해석되지 않음

    • 나도 그렇게 생각함. “prefix할 수 있다”는 말이 “suffix해도 된다”는 뜻은 아님
      하지만 이건 단순한 문장 해석 문제가 아니라, 세계에서 가장 중요한 DNS 서버 중 하나를 운영하는 팀이 CNAME 응답 로직을 바꿨다는 점이 핵심임
      테스트에서 분명히 깨졌을 텐데, 아무도 “이 순서가 중요한가?”라고 묻지 않았다는 게 놀라움
      Cloudflare가 최근엔 투명하게 문제를 공개하지만, 이번 건은 “재밌는 사실 공유”처럼 들림
      대규모 시스템에서 이런 게 테스트를 통과했다는 건 꽤 큰 실수로 보임
    • 기사에서는 모호함이 다른 문장에서 비롯된다고 설명함 — “응답 섹션의 RR 순서 차이는 중요하지 않다”는 구절 때문임
      예시가 일반화될 수 있어서, “모든 경우에 순서가 상관없다”로 오해될 여지가 있음
      결국 한 사람의 “명확한 이해”가 다른 사람에게는 “문서를 다 읽었나?”로 보일 수 있음
      이런 사례가 규범적 언어(nomative language) 의 중요성을 보여줌
    • 네가 모호하지 않다고 느낄 수 있지만 실제로는 모호했고, 이를 수정하려는 시도도 있었음
      관련 논의는 IETF 메일링 리스트에서 볼 수 있음
    • 나도 네 의견에 동의함. RFC의 예시 6.2.1 해석이 잘못된 것 같음
      “순서 차이는 중요하지 않다”는 말은 특정 예시에서만 해당되는 것이고, 일반 규칙을 무시하라는 뜻은 아님
      RFC 1034가 RRset을 정의했다고 하지만 실제로는 그런 용어 정의가 없음
      Cloudflare의 해석은 예외 조항을 일반 규칙으로 오해한 것으로 보임
      다만 CNAME 체인의 순서에 대한 명확한 규정은 없어서, 그 부분은 약간의 모호함이 남아 있음
    • 그건 기사에서도 언급된 내용임. RFC가 규범적 단어(MUST, SHOULD) 가 표준화되기 전의 문서라는 점을 짚고 있음
  • 이건 Hyrum’s LawPostel’s Law가 동시에 작용한 사례 같음
    “API 사용자가 충분히 많으면, 시스템의 모든 관찰 가능한 동작에 누군가는 의존하게 된다”는 것과
    “보내는 건 보수적으로, 받는 건 관대하게”라는 원칙이 충돌한 결과임

    • 하지만 Postel의 법칙은 이제 해로운 원칙으로 여겨지는 추세임
    • 맞음, 하지만 Hyrum’s Law의 핵심은 세상에 수많은 엣지 케이스가 존재한다는 점임
      이번엔 glibc의 resolver가 깨졌다는 게 포인트임 — 결코 희귀한 상황이 아님
      Cloudflare가 테스트를 제대로 안 했다는 게 진짜 뉴스임
    • 결국 이는 사람 수준의 누수된 추상화(leaky abstraction) 문제임
    • Hyrum’s Law를 완벽히 표현한 xkcd 만화가 있음
  • 이번 버그는 나에게 오래된 기억을 떠올리게 했음
    2011년 Cloudflare가 RFC를 무시하고 도메인 apex에 CNAME을 허용했을 때임
    그때의 블로그 글을 보면 “RFC 따위는 무시하고 현실 문제를 해결하겠다”고 했음
    하지만 CNAME은 이름 수준(alias) 개념이라 apex에 두면 NS, MX, SOA 캐싱이 깨짐
    당시 많은 엔지니어가 경고했지만 “move fast and break things”였음
    그래도 이번엔 CNAME 체인과 A 레코드 순서를 세밀히 다루는 걸 보니 발전했음

    • 네 말에 공감함. 예전 BIND에서 “cannot have CNAME and other data” 오류를 수도 없이 봤음
      CNAME과 alias 개념은 오래전부터 혼란을 줬고, RFC1034의 비규범적 언어가 그 혼란을 키웠음
      이런 모호함이 누적된 결과지만, 대형 서비스 제공자가 이런 실수를 한 건 여전히 용납하기 어려움
    • 하지만 명세를 의도적으로 어긴 게 과연 “버그”인가?
      오히려 명세 자체가 문제였다고 생각함
  • glibc의 일반적인 DNS lookup이 레코드 순서에 의존한다는 게 놀라움
    20년 넘게 큰 문제가 없었던 게 신기함
    아마 대부분의 DNS 운영자들이 테스트 중에 순서가 중요하다는 걸 경험적으로 알았을 것임

    • 아마 이런 문제는 자주 있었지만, 규모가 작은 서비스에서는 그냥 넘어갔을 것임
      Cloudflare처럼 전 세계 수백만 기기에 영향을 준 경우는 처음이라 주목받은 것임
      다만 Cloudflare가 이번에 glibc 같은 오픈소스 resolver에 패치를 보냈는지는 궁금함
    • 서버 측에서 순서를 유지하는 게 일반적이었고, 그렇지 않은 서버는 호환성 문제로 금방 수정했음
      CNAME은 정말 다루기 까다로운 존재임 (DJB의 노트 참고)
    • 인터넷은 실제로 몇 개의 주요 권한 서버 구현체에 의존하고 있어서, 다들 같은 순서 규칙을 따름
    • 순서 제약을 없애려면 DNS 이름을 빠르게 조회할 수 있는 자료구조가 필요함
      glibc 같은 단순 resolver는 캐시가 없기 때문에, 이걸 구현하려면 큰 코드 변경이 필요함
  • Cloudflare가 “RFC는 CNAME 순서를 요구하지 않는다”고 주장한 건 언어 꼬투리 잡기처럼 들림
    “MUST”가 없다고 해서 “아무 순서나 가능하다”는 건 억지임
    잘못을 인정하고 넘어가는 게 세상을 더 낫게 만든다고 생각함
    언어 논쟁으로 책임을 회피하는 건 오히려 신뢰를 잃게 함

  • Cloudflare는 RFC1034를 제대로 이해하지 못한 듯함
    그들의 DNS 인터페이스는 CNAME이 있으면 A, AAAA만 막고, 다른 레코드는 허용함
    그래서 CNAME과 TXT가 함께 존재할 때 캐시 의존적 결과가 발생함
    internet.nl에서도 이런 문제를 발견했음
    일부 사용자는 mxtoolbox 가이드를 따라 설정했는데, 이는 RFC1034와 충돌함

    • 그 가이드는 아마 두 가지 다른 설명이 섞인 것 같음
      하나는 “DMARC 서비스를 CNAME으로 위임하는 법”, 다른 하나는 “직접 호스팅하는 법”임
      스크린샷이 혼동을 일으킨 듯함
  • Cloudflare가 결국 “CNAME은 다른 레코드보다 앞에 와야 한다”는 결론에 도달한 건 합리적임

    • 나도 그 결론이 반가움. 모두가 틀렸더라도, 때로는 현실에 맞춰야 함
  • 회사에서 DNS를 관리하면서 여러 DNS의 한계를 체감했음
    특히 SERVFAIL이 발생하면, 클라이언트가 어떤 서버가 문제인지 구분하지 못함
    그 결과, 여러 DNS 서버와 캐시 계층이 불필요한 재시도 폭주를 일으킴
    또한 search path가 잘못된 도메인으로 NXDOMAIN 질의를 반복함

    • 나도 비슷한 문제를 겪었음. 캐싱 서버만 보다가 하루 넘게 원인을 찾지 못했는데, 결국 권한 서버(auth server) 문제였음
    • BIND 9에는 이를 완화하는 servfail-ttl 옵션이 있음
      RFC2308 섹션 7.1에 따르면 SERVFAIL 응답을 캐시하는 게 허용되어 있음
      오래된 표준이지만 여전히 유효한 접근임
  • Cloudflare는 종종 표준을 깨고, 그걸 정당화하는 새 RFC를 작성
    RFC 8482 같은 사례는 업계의 불명예라고 생각함
    “이번에도 혼란을 막기 위해 새 Internet-Draft를 제출했다”는 말이 아이러니하게 들림

  • 1.1.1.1 같은 대규모 DNS 서버라면 glibc 같은 실제 resolver를 포함한 통합 테스트가 있어야 함
    그런데 왜 이런 문제가 프로덕션에서만 발견된 걸까 의문임

    • 아마 캐시 만료 순서가 꼬인 뒤 glibc로 재질의할 때만 발생하는 희귀한 조합이었을 것임
      개별 테스트는 통과했지만, 두 조건이 겹치는 케이스는 빠졌을 가능성이 큼