본문으로 건너뛰기

안동민 개발노트

본문 시작

DNS 심화와 실무

라운드 로빈·GeoDNS·장애 조치와 DoH·DoT·DNSSEC의 역할을 이해하고 캐시와 보안 문제를 진단합니다.

DNS의 기본 동작과 레코드를 이해했으니, 이제 실무에서 자주 마주치는 라우팅, 프라이버시, 보안, 장애 진단을 살펴보겠습니다.

DNS는 단순한 “도메인 → IP 변환기”가 아니라 분산 데이터베이스, 캐시, 정책 기반 응답, 보안 검증이 함께 움직이는 인프라입니다.


DNS 라운드 로빈

하나의 이름에 여러 개의 A/AAAA 레코드를 두면 응답에 여러 IP가 포함될 수 있고, 권한 DNS 서버나 리졸버는 응답 순서를 바꾸어 줄 수 있습니다.

이를 흔히 DNS 라운드 로빈이라고 부릅니다.

DNS 라운드 로빈은 여러 IP를 느슨하게 나눠 준다

권한 DNS는 같은 이름에 여러 A/AAAA 레코드를 응답할 수 있다. 실제 분산 비율은 캐시 TTL과 클라이언트 선택 방식에 좌우된다.

  1. 권한 DNS

    www.example.com에 대해 여러 주소를 함께 응답한다.

  2. 리졸버 캐시

    TTL 동안 응답 묶음을 보관하므로 즉시 새 비율로 바뀌지 않는다.

  3. 클라이언트 선택

    first, random, happy-eyeballs 정책에 따라 실제 접속 IP가 정해진다.

기대 효과주의할 한계운영 보완
가벼운 접속 분산정확한 로드밸런싱은 아님헬스 체크와 LB를 함께 둔다
구현이 단순함캐시 때문에 장애 IP가 남을 수 있음짧은 TTL과 모니터링이 필요함

다만 DNS 라운드 로빈은 L4/L7 로드밸런서처럼 연결 단위로 정교하게 분산하지 않습니다.

리졸버, 운영체제, 브라우저가 응답을 캐시할 수 있고, 클라이언트가 응답 목록 중 어떤 주소를 선택하는지도 구현에 따라 달라집니다.

또한 단순 라운드 로빈만으로는 서버 장애를 자동 감지하지 못하므로, 실제 운영에서는 헬스 체크, 짧은 TTL, 로드밸런서, Anycast, CDN과 함께 설계합니다.


GeoDNS와 글로벌 서비스 라우팅

GeoDNS는 질의 위치나 네트워크 정보를 기준으로 다른 응답을 돌려주는 DNS 운영 방식입니다.

예를 들어 한국 사용자는 서울 리전, 미국 사용자는 버지니아 리전의 IP를 받도록 할 수 있습니다.

GeoDNS는 보이는 리졸버 정보를 기준으로 응답한다

권한 DNS는 보통 최종 사용자 IP가 아니라 재귀 리졸버 IP 또는 ECS 일부 정보를 보고 지역별 응답을 고른다.

  1. 1. 사용자

    사용자 서울에서 example.com을 질의한다.

  2. 2. 재귀 리졸버

    재귀 리졸버 질의를 모아 권한 DNS에 전달한다.

  3. 3. 권한 DNS

    권한 DNS 리졸버 위치나 ECS를 보고 리전을 선택한다.

  4. 4. 응답

    응답 서울, 도쿄, 버지니아 중 하나의 IP를 반환한다.

주의할 점은 권한 DNS 서버가 보통 최종 사용자의 IP가 아니라 재귀 리졸버의 IP를 본다는 것입니다.

EDNS Client Subnet(ECS)을 쓰면 일부 클라이언트 네트워크 정보가 권한 서버에 전달될 수 있지만, 성능과 프라이버시의 절충이 생기며 모든 리졸버가 같은 방식으로 지원하는 것도 아닙니다.


DNS 라우팅 정책과 장애 조치

클라우드 DNS와 CDN은 단순 응답 외에도 가중치, 지연 시간, 지리 위치, 헬스 체크 기반 장애 조치 같은 정책을 제공합니다.

이 정책들은 DNS 응답을 바꾸는 방식이므로 TTL과 캐시의 영향을 항상 받습니다.

DNS 라우팅 정책은 새 응답을 고르지만 TTL이 전환 속도를 정한다

가중치, 지연 시간, 지역, 장애 조치는 권한 DNS의 응답 선택 규칙이다. 이미 배포된 값은 TTL 동안 캐시에 남아 전환이 늦게 보일 수 있다.

  1. 질의

    사용자는 보통 권한 DNS를 직접 묻지 않고 재귀 리졸버를 거친다.

  2. 정책 엔진

    비율, 지연 시간, 지역, 헬스 체크를 기준으로 후보 IP를 고른다.

  3. 응답 캐시

    선택된 답은 TTL 동안 리졸버와 클라이언트에 남는다.

정책주로 쓰는 목적주의할 점
Weighted점진 이전, A/B 테스트리졸버 캐시 때문에 순간 비율은 다를 수 있다.
Latency / Geo가까운 리전이나 지역별 엔드포인트 선택사용자 위치가 아니라 리졸버 위치 기준일 수 있다.
Failover주 리전 장애 시 백업 응답이미 받은 IP는 TTL 만료 전까지 남는다.
Multi-value여러 건강한 IP 반환정교한 부하 분산은 L4/L7 로드밸런서가 더 적합하다.
DNS 라우팅 정책판단 기준적합한 사용 사례주의할 점
단순(Simple)고정 레코드단일 서버, 내부 도메인장애 감지 없음
라운드 로빈여러 값의 순서나 목록가벼운 분산, 실습 환경정확한 비율 보장 아님
가중치(Weighted)설정한 비율점진적 이전, A/B 테스트캐시 때문에 순간 비율은 흔들림
지리적(Geolocation)리졸버/ECS 기반 위치지역별 콘텐츠, 규제 분리최종 사용자 위치와 다를 수 있음
지연 시간(Latency)측정된 리전 지연 시간글로벌 API, CDN실시간 네트워크 상태와 차이 가능
장애 조치(Failover)헬스 체크 결과주/백업 리전TTL 동안 이전 응답이 남을 수 있음
다중 값(Multi-value)헬스 체크를 통과한 여러 값DNS 수준의 단순 고가용성로드밸런서 대체재로 과신 금지

DNS over HTTPS와 DNS over TLS

전통적인 DNS는 보통 53번 포트의 UDP 또는 TCP로 질의와 응답을 주고받습니다.

이 구간이 평문이면 같은 네트워크의 관찰자는 어떤 이름을 질의했는지 볼 수 있고, 공격자는 응답을 위조하려고 시도할 수 있습니다.

DoT와 DoH는 클라이언트와 리졸버 사이만 암호화한다

암호화 DNS는 질의 내용을 중간망에서 읽기 어렵게 만든다. 다만 최종 접속 IP나 계정 로그인, 애플리케이션 트래픽까지 숨기는 기술은 아니다.

  1. 기본 DNS

    UDP/TCP 53. 네트워크 관찰자가 질의 이름을 볼 수 있다.

  2. DoT

    TLS 위 DNS, 전용 포트 853. 운영 정책이 명확하다.

  3. DoH

    HTTPS 위 DNS, 보통 443. 웹 트래픽과 같이 흐르지만 앱별 정책 분리가 필요하다.

  4. 1 사용자 기기

    사용자 기기 DNS 메시지를 만든 뒤 선택한 리졸버로 보낸다.

  5. 2 암호화 터널

    암호화 터널 TLS 또는 HTTPS가 질의 이름을 중간망에서 가린다.

  6. 3 리졸버 처리

    리졸버 처리 리졸버는 복호화 후 일반 DNS 해석을 수행한다.

숨겨지는 것남는 메타데이터운영 판단
DNS 질의 이름과 응답 내용리졸버 IP, 접속 시각, 최종 목적지 IP프라이버시와 통제 정책을 함께 봐야 함
중간망의 DNS 변조신뢰할 리졸버 선택 문제기업망은 허용 리졸버와 로그 정책을 정해야 함

DNS over TLS(DoT)는 DNS 메시지를 TLS 연결 위에 실어 보내며 기본 포트는 853입니다.

DNS over HTTPS(DoH)는 DNS 질의를 HTTPS 요청으로 보냅니다.

둘 다 클라이언트와 선택한 리졸버 사이의 DNS 질의를 암호화하지만, 리졸버 운영자는 여전히 질의를 볼 수 있고, 목적지 IP나 TLS SNI 같은 다른 신호가 항상 사라지는 것은 아닙니다.

프로토콜일반 포트전송 방식보호하는 구간운영상 특징
전통 DNS53UDP/TCP기본적으로 암호화 없음단순하고 널리 지원되지만 관찰 쉬움
DoT853DNS over TLS클라이언트 ↔ 리졸버 DNS 메시지DNS 전용 포트라 정책 적용이 비교적 쉬움
DoH443DNS over HTTPS클라이언트 ↔ DoH 서버 HTTP 교환HTTPS와 같은 포트를 써서 앱별 설정이 쉬움
dns_privacy_check.sh
# 현재 시스템의 DNS 서버 확인
# Windows
ipconfig /all | findstr "DNS Servers"

# Linux/macOS
cat /etc/resolv.conf

# Cloudflare의 JSON API 예시입니다. RFC 8484의 application/dns-message 형식과는 다릅니다.
curl -s "https://cloudflare-dns.com/dns-query?name=example.com&type=A" \
  -H "Accept: application/dns-json"

DNS 캐시 포이즈닝과 DNSSEC

DNS 캐시 포이즈닝은 재귀 리졸버가 권한 서버의 진짜 응답보다 먼저 도착한 위조 응답을 믿고 캐시에 저장하게 만드는 공격입니다.

현대 리졸버는 트랜잭션 ID, 질의 이름, 질의 타입, 출발지 포트 무작위화, 응답 출처 확인 같은 조건으로 위조 난도를 높입니다.

캐시 포이즈닝은 가짜 응답을 캐시에 먼저 넣는 공격이다

공격자는 리졸버가 권한 서버의 진짜 응답을 받기 전에, 질의와 맞아 보이는 위조 응답을 먼저 도착시키려 한다.

  1. 1 리졸버 질의

    리졸버 질의 TXID + qname + qtype source port 권한 서버로 질의를 보낸다.

  2. 2 공격자 경쟁

    공격자 경쟁 spoofed answer TXID, 포트, 이름, 타입을 맞춘 위조 응답을 밀어 넣는다.

  3. 3 캐시 오염

    캐시 오염 victim.example → fake IP 성공하면 TTL 동안 잘못된 IP가 재사용된다.

공격자가 노리는 것방어가 줄이는 위험
응답을 먼저 도착시키기랜덤 포트와 TXID로 추측 난이도를 높인다.
가짜 권한 정보를 섞기bailiwick 검사가 권한 밖 데이터를 막는다.
데이터 변조 숨기기DNSSEC 검증이 서명 불일치를 잡는다.

DNSSEC(DNS Security Extensions)는 DNS 응답에 서명을 붙여 데이터 출처와 무결성을 검증하는 확장입니다.

리졸버는 루트부터 TLD, 도메인 영역까지 이어지는 신뢰 체인을 따라 DS, DNSKEY, RRSIG 같은 레코드를 확인합니다.

DNSSEC은 루트에서 영역까지 서명 체인을 검증한다

DNSSEC은 DNS 데이터를 암호화하지 않는다. 대신 DS, DNSKEY, RRSIG로 “이 응답이 해당 영역의 키로 서명됐는지” 확인한다.

  1. Root

    Trust Anchor 검증 리졸버가 신뢰하는 시작 키다.

  2. TLD

    DS 상위 영역이 하위 영역 키 해시를 가리킨다.

  3. Zone

    DNSKEY 영역의 공개키로 서명 검증을 준비한다.

  4. Answer

    RRSIG + RRset 응답 레코드 묶음이 바뀌지 않았는지 확인한다.

필요 조건실패 시 증상
부모 DS와 자식 DNSKEY가 일치SERVFAIL로 보일 수 있다.
RRSIG 유효 기간과 키 롤오버 관리정상 레코드도 검증 실패가 된다.
dnssec_check.sh
# DNSSEC 관련 레코드와 응답 확인
dig +dnssec example.com

# 검증 리졸버가 Authenticated Data 플래그를 세웠는지 확인
dig +dnssec +adflag example.com | grep "flags"

# DNSKEY와 DS 확인
dig DNSKEY example.com +short
dig DS example.com +short

DNSSEC은 무결성출처 인증을 제공하지만 기밀성은 제공하지 않습니다.

즉, 응답이 변조되지 않았는지 검증하는 기술이지, 어떤 도메인을 질의했는지 숨기는 기술은 아닙니다.

질의 프라이버시까지 고려하려면 DoT나 DoH 같은 암호화 DNS 전송과 함께 봐야 합니다.


DNS 실무 문제 해결

DNS 장애는 “사이트가 안 열린다”로 보이지만 원인은 다양합니다.

로컬 캐시, 리졸버 장애, 권한 DNS 설정 오류, DNSSEC 검증 실패, 도메인 만료, 방화벽, 실제 서버 장애를 단계별로 분리해야 합니다.

DNS 장애는 캐시, 리졸버, 권한 서버를 나눠 확인한다

ping 하나로 DNS 문제를 확정하기는 어렵다. 같은 이름을 여러 위치에서 조회해 어느 계층에서 다른 답이 나오는지 좁힌다.

  1. 1 로컬

    컬 기기 캐시 배제 hosts OS cache browser cache

  2. 2 리졸버

    리졸버 공용 DNS와 비교 dig @1.1.1.1 dig @8.8.8.8

  3. 3 권한

    권한 원본 상태 조회 dig +trace dig @ns1 zone

  4. 4 서비스

    서비스 DNS 밖 문제 분리 curl -v tls / http status

문제흔한 증상확인할 것대응 방향
캐시 오래됨서버 이전 후 옛 IP로 접속TTL, OS/브라우저/리졸버 캐시TTL 사전 조정, 캐시 플러시
NXDOMAIN도메인 없음등록 만료, 오타, 위임 누락도메인/존/NS 설정 확인
SERVFAIL해석 실패권한 서버 장애, DNSSEC 검증 실패권한 서버와 DS/DNSKEY 체인 확인
느린 DNS첫 접속 지연리졸버 응답 시간, 네트워크 경로다른 리졸버 비교, 로컬 캐시 확인
잘못된 IP엉뚱한 서버로 접속레코드 오타, 오래된 캐시, 하이재킹권한 서버 직접 조회, 보안 점검
dns_health_check.py
import socket
import time

def dns_health_check(domains):
    """여러 도메인의 기본 DNS 해석 상태 확인"""
    results = []

    for domain in domains:
        start = time.time()
        try:
            ip = socket.gethostbyname(domain)
            elapsed = (time.time() - start) * 1000
            results.append((domain, ip, f"{elapsed:.1f}ms", "OK"))
        except socket.gaierror as e:
            elapsed = (time.time() - start) * 1000
            results.append((domain, "-", f"{elapsed:.1f}ms", str(e)))

    print(f"{'도메인':<25} {'IP':<18} {'시간':<10} {'상태'}")
    print("-" * 70)
    for domain, ip, time_str, status in results:
        print(f"{domain:<25} {ip:<18} {time_str:<10} {status}")

domains = [
    "google.com",
    "github.com",
    "naver.com",
    "example.com",
    "nonexistent-domain-test.xyz",
]
dns_health_check(domains)

다음 장에서는 웹 개발자가 매일 사용하지만 깊이 이해하는 경우가 드문 프로토콜, HTTP를 자세히 살펴보겠습니다.