47. DNS 이름 해석 흐름 — 도메인이 IP가 되기까지

changseop lee·6일 전

📚 네트워크 · 패킷 분석 › 01. TCP/IP 구조 이해 — 47편
이전 글: 46. 시퀀스 번호·ACK·윈도우 — TCP가 데이터를 빠짐없이 전달하는 방법 · 다음 글: 48. DHCP DORA 과정 — IP를 받는 4단계
참고(리눅스 시스템 기초): Linux 네트워크 구조 — 네트워크 설정 파일 위치 개요

1. 왜 알아야 하는가

사용자가 도메인을 입력한 순간부터 TCP 연결이 시작되기 전까지, 가장 먼저 일어나는 네트워크 행위가 이름 해석(Name Resolution) 입니다. 거의 모든 통신이 DNS를 거치기 때문에, DNS 흐름은 관제에서 "이 단말이 어디로 가려고 했는가"를 보여 주는 가장 이른 증거입니다.

그런데 이름 해석은 DNS 서버에만 묻는 단순한 과정이 아닙니다.

  • 로컬 /etc/hosts 파일이 DNS보다 먼저 답할 수 있습니다. 이 경우 네트워크에 DNS 패킷이 전혀 남지 않습니다.
  • 로컬 캐시가 답하면 역시 외부 질의가 없습니다.
  • 내부 DNS 서버(재귀 리졸버)는 클라이언트 대신 외부에 질의하므로, 외부 DNS 로그에는 클라이언트가 아니라 리졸버의 IP가 남습니다.

즉 "DNS 로그에 없으니 접속하지 않았다"는 판단은 틀릴 수 있습니다. 이 글은 도메인이 IP가 되는 경로를 단계별로 나누고, 각 단계에서 무엇을 확인할 수 있는지 정리합니다. DNS 레코드 종류와 DNS 악용 탐지는 02. 포트 · 프로토콜 분석 시리즈의 07. DNS 레코드와 질의 유형·08. DNS 악용 유형에서 다룹니다.


2. 핵심 개념

2-1. 등장 요소

요소위치역할
애플리케이션클라이언트getaddrinfo() 같은 라이브러리 함수로 이름 해석 요청
Stub Resolver클라이언트 (glibc 등)설정에 따라 hosts 파일·DNS 서버에 묻는 최소 기능 리졸버, 재귀 요청(RD=1) 전송
/etc/nsswitch.conf클라이언트hosts: 줄에서 조회 순서 결정 (예: files → dns)
/etc/hosts클라이언트수동 이름-IP 매핑, 보통 DNS보다 먼저 조회
로컬 캐시클라이언트 (systemd-resolved 등)최근 결과 재사용
재귀 리졸버 (Recursive Resolver)사내 DNS, ISP, 공용 DNS클라이언트 대신 루트부터 차례로 물어 최종 답을 찾고 캐시
루트 서버인터넷TLD 서버 위치 안내 (13개 이름의 서버, 애니캐스트로 다수 배치)
TLD 서버인터넷.com, .kr 등 하위 도메인의 권한 서버 위치 안내
권한 서버 (Authoritative)도메인 소유자·호스팅해당 도메인의 실제 답을 가진 서버 (응답에 AA 플래그)

2-2. 재귀 질의와 반복 질의

  • 재귀 질의(Recursive): "최종 답을 찾아서 달라." 클라이언트 → 재귀 리졸버 구간. 요청 헤더에 RD(Recursion Desired)=1, 리졸버가 지원하면 응답에 RA(Recursion Available)=1.
  • 반복 질의(Iterative): "당신이 아는 만큼만 알려 달라." 재귀 리졸버 → 루트/TLD/권한 서버 구간. 루트와 TLD는 답 대신 다음에 물어볼 서버(위임, Referral) 를 알려 줍니다.

2-3. 캐시와 TTL

모든 DNS 응답 레코드에는 TTL(초) 이 있습니다. 리졸버는 TTL 동안 결과를 캐시하고, 같은 질문에는 외부에 묻지 않고 캐시로 답합니다. 캐시에서 답할 때는 남은 TTL이 줄어든 값으로 전달됩니다.

  • 존재하지 않는 이름(NXDOMAIN)도 일정 시간 캐시됩니다(Negative Caching).
  • TTL이 짧으면 IP 변경이 빨리 반영되지만 질의가 늘어나고, 길면 반대입니다.
  • 관제 관점: 같은 도메인 질의가 캐시 덕분에 한 번만 보일 수 있어, 질의 횟수만으로 접속 횟수를 판단하면 안 됩니다.

2-4. 응답 코드(RCODE)

RCODE이름의미
0NOERROR정상 처리 (답이 비어 있을 수도 있음 — 이름은 있지만 요청한 유형의 레코드가 없음)
2SERVFAIL리졸버가 답을 얻지 못함 (권한 서버 장애, 검증 실패 등)
3NXDOMAIN그런 이름이 존재하지 않음
5REFUSED서버가 처리를 거부 (허용되지 않은 클라이언트의 재귀 요청 등)

3. 동작 원리

클라이언트(192.168.10.20)가 www.example.com에 접속할 때, 캐시가 모두 비어 있다고 가정한 흐름입니다.

DNS 재귀 질의 시퀀스
그림 1. stub 리졸버 → 재귀 리졸버 → 루트·TLD·권한 서버 순서의 이름 해석

[애플리케이션] curl http://www.example.com
      ↓ getaddrinfo("www.example.com")
[Stub Resolver] /etc/nsswitch.conf  hosts: files dns
      ↓ ① files → /etc/hosts 조회 ── 있으면 여기서 종료 (네트워크 패킷 없음)
      ↓ ② dns   → 로컬 캐시(systemd-resolved 등) ── 있으면 종료
      ↓ ③ /etc/resolv.conf 의 nameserver 로 재귀 질의 (UDP 53, RD=1)
[재귀 리졸버 192.168.10.53]  캐시 확인 → 없음
      ↓ ④ 루트 서버에 질의      → "com 은 TLD 서버에게"        (Referral)
      ↓ ⑤ .com TLD 서버에 질의  → "example.com 은 이 NS에게"   (Referral)
      ↓ ⑥ 권한 서버에 질의      → "www.example.com A 203.0.113.80, TTL 3600" (AA=1)
      ↓ ⑦ 결과를 TTL 동안 캐시
[재귀 리졸버] → 클라이언트에 응답 (RA=1)
      ↓
[Stub Resolver] → 애플리케이션에 IP 반환 → TCP 연결 시작 (08편 Handshake)

실제로는 리졸버가 루트·TLD 정보를 이미 캐시하고 있어 ④·⑤가 생략되는 경우가 대부분입니다. 또한 IPv4(A)와 IPv6(AAAA) 주소를 동시에 질의하는 것이 일반적이어서, 패킷에는 질의 두 개가 나란히 보입니다.

전송 계층은 기본적으로 UDP 53이며, 응답이 커서 잘리면(TC 플래그) TCP 53으로 다시 질의합니다. 포트·전송 방식의 세부 내용은 02. 포트 · 프로토콜 분석 시리즈에서 다룹니다.

3-1. 배포판별 구성 차이

항목Ubuntu (최근 버전)Rocky Linux
로컬 리졸버systemd-resolved 기본 사용기본은 NetworkManager가 resolv.conf 관리 (systemd-resolved 기본 비활성)
/etc/resolv.confnameserver 127.0.0.53 (스텁) 을 가리키는 심볼릭 링크실제 DNS 서버 IP 기록
실제 상위 DNS 확인resolvectl statuscat /etc/resolv.conf, nmcli dev show ens33
dig 패키지dnsutils (또는 bind9-dnsutils)bind-utils

Ubuntu에서 /etc/resolv.conf만 보면 DNS 서버가 127.0.0.53으로 보여 혼동하기 쉽습니다. 실제로 외부로 질의하는 상위 서버는 resolvectl status로 확인해야 합니다.


4. 실제 명령어 / 실습

실습 환경: 본인 소유 Linux VM (Rocky Linux 또는 Ubuntu), 인터페이스 이름은 ens33 예시입니다.

4-1. 도구 설치

# Rocky Linux
sudo dnf install -y bind-utils tcpdump

# Ubuntu
sudo apt update && sudo apt install -y dnsutils tcpdump

4-2. 조회 순서와 설정 확인

grep '^hosts' /etc/nsswitch.conf     # 조회 순서
cat /etc/hosts
ls -l /etc/resolv.conf               # Ubuntu는 심볼릭 링크 확인
cat /etc/resolv.conf

# Ubuntu (systemd-resolved)
resolvectl status ens33              # 이 인터페이스가 실제로 쓰는 DNS 서버
# Rocky (NetworkManager)
nmcli dev show ens33 | grep DNS

4-3. getent와 dig의 차이 확인

getent는 nsswitch 순서를 그대로 따르고, dig는 hosts 파일과 nsswitch를 무시하고 DNS 서버에 직접 묻습니다. 이 차이를 직접 확인합니다.

# 테스트용 hosts 항목 추가 (문서용 주소 사용, 실습 후 반드시 삭제)
echo '192.0.2.99  lab-test.example.com' | sudo tee -a /etc/hosts

getent hosts lab-test.example.com    # → 192.0.2.99 (hosts 파일에서 응답)
dig +short lab-test.example.com      # → hosts 무시, DNS 결과(없으면 빈 출력)

# 실습 후 제거
sudo sed -i '/lab-test.example.com/d' /etc/hosts

4-4. 캐시와 TTL 관찰

# 같은 질의를 간격을 두고 반복 → TTL 값 감소 확인
dig www.example.com A +noall +answer
sleep 5
dig www.example.com A +noall +answer

# Ubuntu: 로컬 캐시 통계 / 비우기
resolvectl statistics
sudo resolvectl flush-caches

4-5. 위임 경로 따라가기

# 루트부터 반복 질의를 직접 수행 (리졸버 대신 dig가 따라감)
dig +trace www.example.com

# 특정 서버에 재귀 없이 질의 → 위임(Referral) 응답 확인
dig @a.root-servers.net www.example.com A +norecurse

+trace는 외부 루트·TLD 서버에 직접 질의하므로, 사내망처럼 외부 53번 포트가 막힌 환경에서는 실패할 수 있습니다. 이것 자체가 "내부 리졸버만 쓰도록 통제된 망"이라는 확인이 됩니다.

4-6. 질의 패킷 캡처

sudo tcpdump -i ens33 -nn 'port 53'
# 다른 터미널에서
getent ahosts www.example.com        # A·AAAA 질의 동시 발생 확인

📷 [실습 화면 삽입] getent hosts 와 dig +short 결과가 다르게 나온 화면 — hosts 파일이 DNS보다 우선한다는 증거

📷 [실습 화면 삽입] dig +trace 출력에서 루트 → .com → 권한 서버 순서로 위임되는 구간


5. 결과 확인

dig www.example.com 출력 형식 예시(값은 환경마다 다름):

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41520
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
www.example.com.        2873    IN      A       203.0.113.80

;; Query time: 3 msec
;; SERVER: 192.168.10.53#53(192.168.10.53) (UDP)
항목해석
status: NOERRORRCODE 0
flags: qr rd ra응답(qr), 재귀 요청(rd), 재귀 가능(ra). aa가 없으므로 캐시·재귀 결과
TTL 2873원래 TTL에서 캐시 경과만큼 줄어든 값일 수 있음
Query time캐시 응답이면 매우 짧고, 재귀 과정이 있었으면 길어짐
SERVER실제로 질의를 받은 서버 (Ubuntu에서 127.0.0.53이면 로컬 스텁)
  • nsswitch의 hosts: 순서를 확인하고 설명할 수 있다
  • hosts 항목이 getent에는 반영되고 dig에는 반영되지 않음을 확인했다
  • 반복 질의 시 TTL이 줄어드는 것을 확인했다
  • dig +trace로 루트 → TLD → 권한 서버 위임 흐름을 확인했다
  • 캡처에서 A와 AAAA 질의가 함께 나가는 것을 확인했다
  • 실습용 hosts 항목을 삭제했다

6. 패킷 / 로그 분석

6-1. 필터

도구필터의미
Wiresharkdns.flags.response == 0질의만
Wiresharkdns.flags.rcode == 3NXDOMAIN 응답
Wiresharkdns.flags.rcode == 2SERVFAIL 응답
Wiresharkdns.qry.name contains "example"특정 이름 포함 질의
Wiresharkdns.time > 0.5응답까지 0.5초 넘게 걸린 질의
tcpdump'port 53 and not host 192.168.10.53'지정 리졸버가 아닌 곳으로 가는 DNS

6-2. 정상일 수 있는 경우 vs 의심해야 하는 경우

관찰정상일 수 있는 경우의심해야 하는 경우
단말이 사내 리졸버가 아닌 외부 DNS로 직접 질의정책상 허용된 공용 DNS 사용정책상 내부 리졸버만 허용인데 외부 53 통신 → 설정 변조·우회 가능성
/etc/hosts 변경관리자의 테스트·내부 서비스 매핑금융·업데이트·보안 제품 도메인을 임의 IP로 매핑 → 트래픽 가로채기 원리
/etc/resolv.conf DNS 서버 변경DHCP 갱신, 관리자 변경알 수 없는 외부 IP로 변경, 변경 주체가 일반 프로세스
NXDOMAIN 다수오타, 삭제된 도메인, 검색 도메인 접미사 시도한 단말에서 무작위 형태 이름의 NXDOMAIN 대량 발생 (패턴 분석은 02. 포트 · 프로토콜 분석 시리즈 08편에서 다룸)
도메인 질의 없이 IP로 직접 접속내부 시스템, 하드코딩된 서비스외부 IP로 DNS 없이 바로 연결 (hosts 매핑·하드코딩 여부 확인)
# 설정 파일 변경 시각 확인
ls -l --time-style=full-iso /etc/hosts /etc/resolv.conf /etc/nsswitch.conf

# 설정 파일 변경 감시 규칙 예시 (auditd 설치·활성 상태 전제)
sudo auditctl -w /etc/hosts -p wa -k dns_config
sudo auditctl -w /etc/resolv.conf -p wa -k dns_config
sudo ausearch -k dns_config

7. 보안관제 관점

흔적이 남는 곳

단계증거 위치한계
hosts 파일 응답호스트 파일 자체, 파일 무결성·감사 로그네트워크에는 흔적 없음
로컬 캐시 응답호스트 캐시(resolvectl statistics)개별 질의 로그는 기본적으로 남지 않음
클라이언트 → 사내 리졸버리졸버 질의 로그(BIND querylog, Windows DNS 로그 등), NSM 도구(Zeek dns.log, Suricata DNS 이벤트)리졸버 질의 로그는 부하 때문에 꺼져 있는 경우가 많음
리졸버 → 외부경계 방화벽·NSM출발지가 리졸버 IP라 실제 단말을 알 수 없음 → 리졸버 로그와 시간 기준 연결 필요

관제 판단 포인트

  • DNS 질의는 실제 접속 이전의 의도를 보여 줍니다. 차단된 도메인을 질의했다는 사실만으로도 어느 단말이 문제인지 좁힐 수 있습니다.
  • 외부 경계에서 본 DNS 출발지가 리졸버 하나로 모이는 구조를 이해하지 못하면, 리졸버 서버를 감염 단말로 오인하는 오탐이 생깁니다.
  • 사내 정책상 "단말 → 외부 53 직접 통신 차단"이 되어 있어야 DNS 로그가 의미 있는 증거가 됩니다. 이 정책 자체가 점검 대상입니다.
  • DNS over HTTPS(443)나 DNS over TLS(853)를 쓰는 애플리케이션은 기존 DNS 로그에 흔적이 남지 않을 수 있습니다. 이는 가시성 한계로 기록해 둡니다(암호화 통신은 02. 포트 · 프로토콜 분석 시리즈 06. HTTPS와 TLS Handshake에서 다룸).

한계와 오탐 주의

  • 캐시 때문에 질의 1회가 접속 여러 번일 수 있고, 질의가 있었다고 접속까지 했다는 보장도 없습니다. DNS 증거는 방화벽 세션·프록시 로그와 함께 판단합니다.
  • 브라우저·OS의 미리 가져오기(prefetch) 기능은 사용자가 클릭하지 않은 도메인도 질의할 수 있습니다.

8. 핵심 정리

  • 이름 해석은 애플리케이션 → Stub Resolver → nsswitch 순서(/etc/hosts → DNS) → 재귀 리졸버 → 루트·TLD·권한 서버 순으로 진행됩니다.
  • /etc/hosts나 로컬 캐시에서 답하면 네트워크에 DNS 패킷이 남지 않으므로, "DNS 로그에 없음 = 접속 안 함"이 아닙니다.
  • getent는 nsswitch를 따르고 dig는 DNS에 직접 묻기 때문에, 두 결과 비교로 hosts 파일 영향을 확인할 수 있습니다.
  • 응답 TTL 동안 캐시가 재사용되므로 질의 횟수는 접속 횟수와 다르며, NXDOMAIN도 캐시됩니다.
  • 관제에서는 리졸버 질의 로그로 실제 단말을 특정하고, hosts·resolv.conf 변조와 외부 DNS 직접 질의를 설정 이상 지표로 봅니다.

다음 글: 15. 웹 요청 하나의 전체 경로 — 01 시리즈 종합

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글