[RedHat] 서버의 DNS 요청 프로세스 추적 실전 가이드

tshark를 이용해서 서버에서 DNS 조회를 하고 있는 모든 패킷을 추적하고 어느 프로세스가 사용하고 있는지 분석해 보도록 하겠습니다.

 

DNS 요청 트래픽 분석이 필요한 이유

최근 내부 DNS 서버의 서비스 종료 통보를 받으면서, 서버들이 어떤 도메인을 참조하고 있는지 파악할 필요가 생겼습니다. 그러나 현재 환경에서는 애플리케이션이 어떤 도메인을 사용하는지 확인할 수 있는 수단이 부족합니다. 이 글에서는 tshark를 활용해 DNS 트래픽을 캡처하고 분석하여 서버의 도메인 사용 현황을 파악하는 방법을 설명합니다. 또한 gethostlatency를 통해 DNS 요청을 발생시킨 프로세스까지 추적하는 방법도 함께 다룹니다.


tshark 설치 및 기본 환경

분석 도구로 사용할 tshark는 네트워크 트래픽을 캡처하고 필터링하는 기능을 갖춘 강력한 CLI 기반 도구입니다. dnf 명령으로 wireshark-cli 패키지를 설치하면 함께 포함됩니다.


# dnf install wireshark-cli

이 문서에서 사용하는 테스트 환경은 다음과 같습니다.

  • Red Hat Enterprise Linux 8

  • Red Hat Enterprise Linux 9



tshark로 DNS 요청과 응답 필터링

tshark를 사용하면 실시간으로 DNS 요청과 응답을 캡처하고 구분할 수 있습니다. 필터 dns.flags.response 값을 기준으로 요청(0)과 응답(1)을 구분할 수 있습니다.


DNS 요청만 캡처하기

다음 명령어를 사용하여 실시간으로 DNS 요청을 캡처할 수 있습니다.

# tshark -i any -Y "dns.flags.response == 0"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'
   10 1.772920969 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0x0661 A google.com
   12 1.811223605 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0xf9bf AAAA google.com

  • - i any: 모든 네트워크 인터페이스에서 캡처
  • - Y: 디스플레이 필터 지정
  • - dns.flags.response == 0: 요청 패킷만 캡처

위 출력은 로컬 서버(192.168.155.84)가 Google Public DNS(8.8.8.8)로 google.com에 대한 A 레코드와 AAAA 레코드를 요청하는 장면입니다. 각각의 패킷은 요청 시점의 타임스탬프, 출발지와 목적지 IP, 프로토콜(DNS), 요청의 유형과 쿼리 ID를 포함하고 있습니다.


DNS 응답 캡처

다음 명령어를 사용하여 실시간으로 DNS 응답을 캡처할 수 있습니다.

# tshark -i any -Y "dns.flags.response == 1"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'
   42 6.353728416      8.8.8.8 → 192.168.155.84 DNS 88 Standard query response 0xb6df A google.com A 142.250.76.142
   46 6.392668894      8.8.8.8 → 192.168.155.84 DNS 100 Standard query response 0xafa7 AAAA google.com AAAA 2404:6800:400a:804::200e

  • dns.flags.response == 1: DNS 응답 패킷만 필터링합니다.

이 결과는 앞서 요청한 google.com에 대해 Google DNS 서버(8.8.8.8)가 응답한 패킷입니다. A 레코드에 대한 응답으로 142.250.76.142, AAAA 레코드에는 IPv6 주소 2404:6800:400a:804::200e가 반환되고 있습니다. 요청과 응답을 함께 비교하면 전체 쿼리 흐름을 파악할 수 있습니다.


쿼리된 도메인 이름만 출력

다음 명령어를 사용하여 실시간으로 DNS 요청에서 쿼리된 도메인 이름만 출력할 수 있습니다.

# tshark -i any -Y "dns.flags.response == 0" -T fields -e dns.qry.name
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'
google.com
google.com

  • T fields: 출력 형식을 필드 기반으로 지정합니다.
  • e dns.qry.name: 출력할 필드를 도메인 쿼리 이름으로 지정합니다.
  • dns.flags.response == 0: DNS 요청만 필터링합니다.

위 명령은 DNS 요청 중 쿼리된 도메인 이름만 연속해서 출력합니다. 패킷의 전체 내용이 아닌, 요청 대상 도메인만 보고 싶을 때 유용하며, 로깅 또는 도메인 리스트 수집에 활용할 수 있습니다.


DNS 요청을 파일로 저장 (BPF 필터 활용)

tshark에서 -w 옵션을 사용할 때 디스플레이 필터를 적용하면 파일로 저장하는 기능이 작동하지 않습니다. 그렇다고 전체 패킷을 다 캡처하게 되면 용량이 너무 커질 수 있습니다. 이럴 때 BPF(Berkeley Packet Filter) 기능을 사용할 수 있습니다.

BPF란?

Berkeley Packet Filter (BPF)는 네트워크 트래픽을 운영 체제 수준에서 캡처하고 필터링하는 메커니즘입니다. BPF는 사용자가 정의한 필터를 적용하여 필요한 패킷만 선택적으로 캡처함으로써 성능을 향상시킵니다. 주로 네트워크 분석 도구에서 사용되며, 대부분의 Unix 계열 운영 체제와 일부 Windows 시스템에서도 지원됩니다.

tshark 명령어 예시

아래 명령어는 tshark를 사용하여 DNS 요청 패킷을 캡처하고, 파일 크기가 1MB에 도달할 때마다 새로운 파일로 로테이션하도록 설정하는 예시입니다.

# tshark -i any -f "udp port 53 and udp[10] & 0x80 == 0" -b filesize:1024 -b files:5 -w /tmp/test.pcap

파일 이름에 변수를 사용하지 않아도 현재 시간 관련하여 로테이션 파일 이름이 자동으로 명명됩니다. 이 명령어는 다음과 같이 작동합니다.

  • -i any: 모든 네트워크 인터페이스에서 캡처합니다.
  • -f "udp port 53 and udp[10] & 0x80 == 0": UDP 포트 53(DNS) 트래픽 중에서 DNS 요청 패킷만 필터링합니다. (BPF 필터)
  • -b filesize:1024: 파일 크기가 1MB(1024KB)에 도달하면 새로운 파일을 생성합니다.
  • -b files:5: 최대 5개의 파일을 생성하고, 이후에는 가장 오래된 파일을 덮어씁니다.
  • -w /tmp/test.pcap: 캡처한 패킷을 /tmp/test.pcap 파일에 저장합니다.

파일 로테이션 모니터링

패킷이 저장되는 과정을 실시간으로 확인하려면 watch 명령어를 사용할 수 있습니다. 파일이 생성되거나 교체되는 타이밍을 시각적으로 확인할 수 있어 유용합니다.

# watch -n 1 "ls -alh /tmp/"

이 명령어는 /tmp 디렉토리의 파일 목록을 1초 간격으로 업데이트하여 보여줍니다.

예시 출력은 다음과 같습니다.

Every 1.0s: ls -alh /tmp/                                                                                                                  rhel84: Wed Jun  5 10:53:59 2024

total 3.0M
drwxrwxrwt.  4 root root   187 Jun  5 10:53 .
dr-xr-xr-x. 17 root root   224 Jan 17 09:34 ..
-rw-r--r--.  1 root root   374 Jun  5 09:30 dnsquires.txt
drwxr-xr-x.  2 root root     6 Jun  3 13:26 hsperfdata_root
drwx------.  3 hjun hjun    24 Mar 21 22:10 podman-run-1000
-rw-------.  1 root root 1001K Jun  5 10:51 test_00001_20240605105012.pcap
-rw-------.  1 root root 1001K Jun  5 10:53 test_00002_20240605105144.pcap
-rw-------.  1 root root  459K Jun  5 10:53 test_00003_20240605105317.pcap

수집한 패킷 확인

저장한 .pcap 파일을 분석하려면 -r 옵션을 사용하여 파일을 읽을 수 있습니다.

# tshark -r /tmp/test_00003_20240605105317.pcap
    1 0.000000000 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0x5474 A google.com
    2 0.007049707 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0xb86b A google.com
    3 0.018514264 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0x99ac AAAA google.com
  ...

수집된 패킷을 보면 google.com 도메인을 조회한 내역이 보이는데 이는 tshark를 실행하고 테스트를 위해 다른 터미널에서 nslookup google.com 을 반복적으로 실행했기 때문입니다.


텍스트 파일로 저장

.pcap 형식은 바이너리 파일이기 때문에 사람이 직접 보기에는 불편합니다. 이를 일반 텍스트 파일로 변환하면 검색이나 grep 등을 활용한 분석이 수월해집니다.

# tshark -r /tmp/test_00003_20240605105317.pcap > /tmp/test_003.txt

  • -r: 지정된 pcap 파일을 읽음

텍스트로 저장된 결과를 확인하려면 다음 명령을 실행합니다

# cat /tmp/test_003.txt
    1 0.000000000 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0x5474 A google.com
    2 0.007049707 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0xb86b A google.com
    3 0.018514264 192.168.155.84 → 8.8.8.8      DNS 72 Standard query 0x99ac AAAA google.com
 ...
이와 같이 출력된 텍스트 파일은 로그 분석, 특정 도메인 필터링, 추세 분석 등 다양한 용도로 활용할 수 있습니다. 필요 시 awk나 grep 같은 텍스트 필터링 명령어와 조합하면 자동화된 리포팅도 가능해집니다.

gethostlatency를 이용한 DNS 조회 프로세스 확인

tshark와 같은 도구를 사용하여 패킷을 캡처할 때, 어느 프로세스에서 DNS 조회를 수행하는지를 확인하는 것은 쉽지 않습니다. 이러한 경우, DNS 조회를 수행하는 프로세스를 추적하기 위해 gethostlatency라는 도구를 사용할 수 있습니다. 이 도구는 본래 호스트 이름 해석 지연 시간을 측정하기 위한 용도지만, 프로세스 추적 용도로도 활용할 수 있습니다.

1. gethostlatency 명령어 설치

먼저, 레드햇에서 제공하는 bcc-tools 패키지를 설치합니다. 이 패키지에는 gethostlatency 명령어가 포함되어 있습니다.

# dnf install bcc-tools
이제 DNS 요청을 발생시키는 테스트용 Python 프로그램을 실행해 보겠습니다.

2. DNS 조회 테스트용 파이썬 프로그램 실행


파이썬 코드

import socket
import threading
import time

def dns_lookup(domain):
    try:
        result = socket.gethostbyname(domain)
        print(f"{domain} has IP address: {result}")
    except socket.error as err:
        print(f"DNS lookup failed for {domain}: {err}")

def periodic_lookup(domain, interval, count):
    for _ in range(count):
        dns_lookup(domain)
        time.sleep(interval)

def main():
    domain = "google.com"
    interval = 10  # 10 seconds
    count = 3  # Number of lookups

    # Create a thread to run the periodic_lookup function
    lookup_thread = threading.Thread(target=periodic_lookup, args=(domain, interval, count))
    lookup_thread.daemon = True  # Run in background
    lookup_thread.start()

    # Wait for the lookup thread to complete
    lookup_thread.join()

if __name__ == "__main__":
    main()

파이썬 코드는 gethostbyname 함수를 이용하여 DNS 조회를 수행합니다. gethostlatency 명령의 단점은 getaddrinfo와 gethostbyname 두 함수를 호출하는 경우에만 정보를 수집할 수 있다는 점입니다. 따라서 다른 함수로 DNS를 조회하는 경우에는 gethostlatency 명령으로 확인이 불가능할 수 있습니다.


gethostlatency 출력값을 테스트하기 위해 10초마다 google.com의 DNS를 조회하는 파이썬 프로그램을 실행합니다.

# python3 dns.py
google.com has IP address: 142.250.76.142
google.com has IP address: 142.250.76.142
google.com has IP address: 142.250.206.238
google.com has IP address: 142.250.76.142
google.com has IP address: 142.250.76.142


3. gethostlatency 실행 (모니터링)

이제 gethostlatency 명령어를 실행하여 DNS 조회를 수행하는 프로세스를 실시간으로 모니터링합니다.

# /usr/share/bcc/tools/gethostlatency
TIME      PID     COMM                  LATms HOST
13:41:49  598021  python3               34.78 google.com
13:41:59  598021  python3               36.77 google.com
13:42:09  598021  python3               34.34 google.com
13:42:19  598021  python3               35.30 google.com
  • PID: DNS 요청을 발생시킨 프로세스 ID
  • COMM: 실행 중인 커맨드(여기서는 python3)
  • LATms: DNS 요청-응답 사이의 지연 시간
  • HOST: 요청한 도메인 이름
이 결과를 통해 특정 시간에 어떤 프로세스가 어떤 도메인에 대해 DNS 조회를 했는지를 명확하게 파악할 수 있습니다. 시스템의 DNS 트래픽이 의심스러울 경우, 이상 활동을 추적하는 데 매우 효과적입니다.

4. 프로세스 조회

gethostlatency에서 확인한 PID를 바탕으로 해당 프로세스가 실제 어떤 명령을 실행 중인지 추가로 파악할 수 있습니다. ps 명령어를 이용해 실행 중인 프로세스의 세부 정보를 조회합니다.

# ps -ef | grep 598021
root      598021  597129  0 13:41 pts/1    00:00:00 python3 dns.py

마치며

이 글에서는 리눅스 환경에서 tshark를 이용한 DNS 요청 및 응답 트래픽 분석과, gethostlatency를 활용한 DNS 요청 프로세스 추적 방법을 실습을 통해 알아보았습니다. 두 도구를 함께 활용하면 다음과 같은 점에서 유용합니다:

  • 트래픽 기반 도메인 모니터링: 어떤 도메인이 얼마나 자주 요청되는지 분석 가능

  • 의심 트래픽 추적: 비정상적인 도메인 접근 시, 어떤 프로세스가 이를 유발했는지 추적 가능

  • 보안 및 장애 분석: DNS 서버 변경, 응답 지연, 미응답 등의 상황에 빠르게 대응 가능

운영 중인 시스템에서 DNS 트래픽 흐름을 가시화하고, 문제 원인을 프로세스 단위로 식별해야 하는 경우 본 방법은 실질적인 대안이 될 수 있습니다.



참조

https://en.wikipedia.org/wiki/Berkeley_Packet_Filter [BPF Wikipedia]

https://www.sandeepseeram.com/post/bpf-compiler-collection-bcc-for-observability-performance-monitoring-and-tracing [Bcc-tools]

https://www.redhat.com/sysadmin/using-wireshark-tshark1 [tshark]

댓글

이 블로그의 인기 게시물

[Linux] RHEL Local YUM Repository 구성

[Linux Command] sudo command 설명

[Ansible Modules] Fetch module 설명 및 활용