함정 문제처럼 들리지만 함정이 아닌 수수께끼가 하나 있다. 당신의 도메인은 example.com이다. 네임서버는 ns1.example.com과 ns2.example.com이다. 리졸버가 당신 웹사이트의 IP를 원하니 당신 네임서버에 물어야 한다. 네임서버에 물으려면 ns1.example.com의 주소가 필요하다. 그 주소를 얻으려면… example.com의 네임서버에 물어야 한다. 그게 ns1.example.com이다. 그런데 아직 주소가 없어서 못 닿는다.
억지로 꾸민 예외 상황이 아니다. 이게 기본값이다. 네임서버 이름을 짓는 가장 흔한 방식이 그 서버를 자기가 책임지는 존 안에 넣어 버리고, 그렇게 하면 1987년에 기술된 DNS가 스스로는 풀 수 없는 순환 의존성이 생긴다. 그런 도메인은 전부 첫 조회에서 교착에 빠져야 한다.
당연히 교착에 빠지지 않는다 — 인터넷은 돌아간다. 그걸 구하는 건 글루 레코드(glue record) 라는, 작고 조금 어색한 데이터 조각이다. 그리고 그건 대부분의 사람이 아무 이유도 못 찾은 채 도메인이 깜깜해지는 그날까지 들여다볼 일 없는 곳에 산다.
위임, 그리고 그게 무너지는 지점
DNS는 오른쪽에서 왼쪽으로 풀리는 트리다. www.example.com을 찾으려면 리졸버는 루트에서 시작하고, 루트는 com을 누가 운영하는지 알려준다. com 서버에 물으면 example.com을 누가 운영하는지 알려준다. 그들에게 물으면 마침내 답을 얻는다. 아래로 내려가는 각 단계가 위임(delegation) 이다. 부모 존은 당신 레코드를 갖고 있지 않고, 그걸 가진 서버를 가리키기만 한다.
가리키는 일은 NS 레코드로 한다. com 존에는 당신 도메인에 대해 이런 게 들어 있다.
example.com. NS ns1.example.com.
example.com. NS ns2.example.com.
이걸 찬찬히 읽으면 함정이 평문으로 그대로 드러난다. com 존이 리졸버에게 “example.com을 알려면 ns1.example.com에 물어라”라고 말한다. 그런데 ns1.example.com은 example.com 아래의 이름 — 리졸버가 닿으려 하지만 아직 못 닿는 바로 그 존이다. NS 레코드가 질문에 답하는데, 그 답이 질문에 먼저 답해야만 풀 수 있는 이름이다. 위임이 제 꼬리를 문다.
이 일은 네임서버의 이름이 자기가 서빙하는 도메인 안에 있을 때만 생긴다. 네임서버가 ns1.somedns.net·ns2.somedns.net이었다면 역설은 없다. somedns.net은 자기 위임을 가진 다른 존이고, 리졸버가 독립적으로 찾아가면 된다. 하지만 네임서버를 자기 도메인 이름으로 짓는 게 표준이다 — 등록기관이 그리로 유도하고, 대형 제공자도 그렇게 하고, 깔끔해 보인다 — 그리고 그 깔끔한 선택이, 아무도 대비해 두지 않았다면 DNS를 깨뜨릴 선택이다.
해법은 부모의 손에 있다
누군가는 대비해 뒀다. 답은 부모 존이 이름만이 아니라 주소까지 건네게 하는 것이다.
com 서버가 위임을 돌려줄 때 NS 레코드에서 멈추지 않는다. 네임서버의 A(그리고 AAAA) 주소 레코드도 함께 넣는데, 이건 응답의 additional section(추가 섹션) 이라는 곳에 실려 온다.
;; AUTHORITY SECTION:
example.com. NS ns1.example.com.
example.com. NS ns2.example.com.
;; ADDITIONAL SECTION:
ns1.example.com. A 203.0.113.10
ns2.example.com. A 203.0.113.11
추가 섹션의 저 주소 레코드 두 개가 글루다. com 레지스트리가 당신 네임서버의 IP 사본을 들고 있다가 위임할 때마다 함께 내주므로, 리졸버는 ns1.example.com을 평소 방식으로 풀 필요가 없다. 이름과 주소를 한 호흡에 받는다. 자식이 스스로 못 대는 단 하나의 사실을 부모가 대신 대주니, 순환 의존성이 깨진다.
이름 한번 잘 지었다. 글루는 위임의 이음매를 가로질러 두 존을 붙여 두는 접착제다 — 자식 데이터의 일부를 부모에 저장해 두는 것, 바로 그 자식에게 닿을 수 없어서 그렇게 한다.
글루가 필요할 때와 아닐 때
글루는 네임서버 이름이 위임되는 존 안에 있을 때만 필요하다. 이걸 2023년에 마침내 못 박은 표준 RFC 9471은 in-domain 네임서버라 부르고, 옛 문헌은 in-bailiwick이라 부른다. example.com을 서빙하는 ns1.example.com이 in-domain이다. 글루가 반드시 있어야 하고, 없으면 그 도메인은 해석 불가다.
네임서버가 완전히 다른 존에 살면 — example.com을 서빙하는 ns1.cloudflare.com — 글루가 아예 필요 없다. 리졸버는 그냥 cloudflare.com을 자기 위임으로 풀어 주소를 얻고 넘어간다. 역설도, 글루도, 부모가 저장할 것도 없다. 대형 매니지드 DNS 제공자가 네임서버에 자기 도메인을 쓰는 조용한 이유 하나가 이거다. 글루를 통째로 우회한다 — 그리고 글루는 고장 날 수 있는 물건이다.
중간 경우도 있다 — 당신이 함께 소유한 형제 도메인(sibling)에 네임서버를 두는 경우 — 글루가 있을 수도 없을 수도 있다. 규칙은 공간이 되면 넣으라고(SHOULD) 할 뿐 의무로 두지 않는다. 하지만 기억할 날카로운 선은 이거다. 네임서버를 자기 도메인 안에 지으면, 이제 당신은 글루에 의존한다. 대부분이 그렇고, 대부분이 그 사실을 모른다.
보안의 반전: 믿어선 안 되는 글루
여기서 흥미로워진다. 추가 섹션은 공격자에게도 선물이고, 그걸 방어하는 방식이 리졸버가 글루를 다루는 태도를 빚었기 때문이다.
추가 섹션의 레코드는 정의상 요청하지 않은 것이다. 당신은 example.com을 물었는데, 응답엔 묻지도 않은 주소들이 함께 있다. 악의적이거나 탈취된 네임서버가 추가 섹션에 www.yourbank.com. A 6.6.6.6 같은 레코드를 채워 넣어 리졸버 캐시를 오염시키는 걸 무엇이 막는가?
답은 베일리윅 검사(bailiwick checking) 다. 리졸버는 보낸 서버의 권한 범위(bailiwick) 안에 있는 글루 — 대략, 그 존이 말할 권한이 있는 데이터 — 만 받아들인다. com 서버가 ns1.example.com의 주소를 건네면 그건 범위 안이다. com이 만들 책임이 있는 위임의 일부니까. 하지만 같은 서버가 www.yourbank.com의 주소를 슬쩍 끼워 넣으려 하면 제정신인 리졸버는 버린다. com의 위임은 무관한 이름의 주소를 주장할 자격이 없기 때문이다.
이게 늘 진지하게 다뤄진 건 아니다. 1990년대 리졸버는 범위 밖 추가 레코드를 보이는 대로 캐시했고, 초기 캐시 포이즈닝 공격은 바로 위의 수법을 썼다. 베일리윅 검사가 그 해법이었다. 그걸로 끝은 아니었다. 2008년 카민스키 공격은 베일리윅 검사를 통과하는 글루도 무기가 될 수 있음을 보였다: 대상 도메인 아래 무작위 이름에 대한 위조 위임 응답을 진짜 서버와 경주시키되, 각 위조 응답에 공격자를 가리키는 범위 안 글루를 실으면, 언젠가 하나가 이기고 리졸버가 그걸 캐시한다. 리졸버가 출발 포트까지 무작위로 바꾸고, 어떤 글루를 믿을지 까다롭게 구는 이유다. 글루는 필요하지만, 글루는 또한 아무도 요청하지 않은 패킷 구역에 도착하는 신뢰할 수 없는 입력이고, 그걸 복음처럼 믿는 게 털리는 길이다.
발등의 지뢰: 글루는 낡는다
이제 운영상의 실패, 혼란스러운 지원 티켓을 만들어 내는 그것이다.
당신의 글루는 두 곳에 산다. 하나는 당신 존 안의 ns1.example.com A 레코드로, DNS 제공자 콘솔에서 여느 레코드처럼 편집한다. 다른 하나는 부모 레지스트리에 올라앉은 글루 사본으로, 이건 등록기관(registrar) 을 통해 설정한다 — 보통 “네임서버 등록”, “호스트 레코드”, “차일드 호스트”, 운이 좋으면 아예 “글루 레코드”라는 메뉴에서. 그 등록기관 설정은 EPP로 레지스트리에 밀어 넣어지고, 당신 존을 편집하는 것과는 완전히 별개의 행위다.
이제 이런 그림을 그려 보라. 네임서버를 새 IP로 옮긴다. 당신 존의 A 레코드를 갱신한다. 콘솔에선 다 맞아 보인다. 그런데 도메인이 말이 안 되는 이유로 간헐적으로 실패하기 시작한다. 레지스트리의 글루가 여전히 죽은 옛 IP를 가리키고 있기 때문이다. 부모의 글루를 집어 오는 리졸버는 낡은 주소를 받고, 이미 당신 진짜 주소를 아는 리졸버는 멀쩡히 동작한다. 들쭉날쭉하고, 미치겠고, 당신이 자연히 확인할 레코드 — 당신 존 안의 그것 — 는 완벽히 맞다. 틀린 사본은 존재하는지도 잊고 있던 그것이다.
진단하려면 당신 네임서버가 아니라 부모에게 무엇을 내주고 있는지 물어야 한다. 당신 위임에 대해 TLD 서버에 질의해 추가 섹션의 주소를 보라. 같은 네임서버에 대한 당신 존의 A 레코드와 어긋나면 낡은 글루를 찾은 것이고, 고칠 곳은 DNS가 아니라 등록기관이다.
글루는 선택이 아니다 (2023년부터 공식적으로)
DNS 역사의 대부분 동안, 위임에서 글루를 다루는 정확한 규칙은 덜 명세돼 있었고, 구현들이 이따금 세게 무는 방식으로 엇갈렸다. 특히 이런 물음이다. 필요한 글루가 응답에 다 안 들어가면 서버는 어떻게 해야 하나? 어떤 서버는 안 맞는 글루를 그냥 떨궈 불완전한 위임을 보냈다 — 리졸버에게 풀 수 없는 이름만 남기고, 뭔가 빠졌다는 힌트조차 주지 않은 채. 도메인은 조용히, 오로지 위임이 크다는 이유만으로 실패했다.
2023년 9월에 발표된 RFC 9471 — 초안 이름이 더없이 직설적인 “glue-is-not-optional(글루는 선택이 아니다)” — 은 1987년 원 명세를 갱신해 그 틈을 막는다. 권위 서버는 위임에서 in-domain 네임서버의 글루를 전부 돌려줘야 한다. 그리고 그 글루가 메시지 크기에 안 들어가면 목록을 조용히 잘라선 안 되고, TC(truncated, 잘림) 비트를 세워야 한다 — 리졸버에게 “이 답은 불완전하니 TCP로 다시 물어라”라고 알리는 것이다. 필요한 걸 빠뜨린 빠른 답보다, 완전한 글루를 얻는 조금 느린 재시도를 강제하는 게 낫다.
서른여섯 살 먹은 프로토콜이 “그래, 정말로 글루를 보내야 한다, 전부 다”라고 말하려고 2023년 RFC가 필요했다는 사실이, 이 작은 장치가 얼마나 조용히 하중을 견디는지 말해 준다. 아무도 글루를 생각하지 않는다. 추가 섹션에 눈에 안 띄게 앉아, 자기참조 위임이 교착에 빠지지 않게 막는 그 한 가지 일을 한다 — IP가 바뀌거나, 레지스트리 설정이 어긋나거나, 위임이 너무 커지기 전까지는. 그러면 갑자기 세상에서 가장 기본적인 조회가 완결되지 못한다.
글루가 가르치는 교훈은 DNS가 옷만 갈아입고 계속 가르치는 그 교훈이다. 인터넷은 수십 년 전에 합의된 작은 관습들로 붙어 있고, 그중 대부분은 잘 돌아갈 때 안 보이며, 실패는 결코 당신이 보고 있는 곳에 없다. 당신은 존을 확인한다. 문제는 부모에 있다. 당신은 네임서버를 확인한다. 문제는 레지스트리가 보관하고 있는 줄도 몰랐던 레코드다. 글루는 여러 의미에서 잘 지은 이름이다 — 그게 이 물건을 붙들고 있고, 당신은 그게 떨어져 나갈 때에야 비로소 그걸 알아챈다.