tshark를 이용해서 서버에서 DNS 조회를 하고 있는 모든 패킷을 추적하고 어느 프로세스가 사용하고 있는지 분석해 보도록 하겠습니다.
최근 사용하던 DNS 서버의 서비스가 중단된다는 소식을 받았습니다. 따라서 서버에서 사용하는 도메인 정보를 정확하게 파악하고 필요한 조치를 취해야 합니다. 하지만 현재 애플리케이션이 어느 도메인 주소를 DNS에서 가져오는지 확인할 수 없는 상황이므로, 효율적인 도메인 확인 방법을 찾는 것이 필요합니다. 이 글에서는 DNS 트래픽을 캡처하여 서버에서 사용하는 도메인 정보를 파악하는 방법을 설명합니다. tshark를 사용하면 서버에서 요청하는 도메인 주소를 쉽게 선별하여 확인할 수 있습니다.
환경
Redhat Enterprise Linux 8
Redhat Enterprise Linux 9
설치
먼저 tshark
를 설치합니다.
“wireshark-cli” 패키지를 설치하면 tshark
가 포함되어 있습니다.
# dnf install wireshark-cli
tshark를 활용한 실시간 호스트 이름 조회
tshark
는 네트워크 트래픽을 캡처하고 분석하는 강력한 도구입니다. 이 글에서는 tshark
를 사용하여 실시간으로 DNS 요청과 응답을 캡처하고 분석하는 방법을 설명합니다.
DNS 요청과 응답 구분
tshark
를 사용하여 패킷을 캡처할 때, dns.flags.response
필드를 사용하여 DNS 요청과 응답을 구분할 수 있습니다.
dns.flags.response == 0
: DNS 요청dns.flags.response == 1
: DNS 응답
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
위의 결과에서 DNS 요청 패킷이 로컬 서버(192.168.155.84)에서 구글 DNS 서버(8.8.8.8)로 전송되는 것을 확인할 수 있습니다.
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 서버(8.8.8.8)가 로컬 서버(192.168.155.84)에 응답을 보내는 것을 확인할 수 있습니다.
쿼리된 도메인 이름만 출력
다음 명령어를 사용하여 실시간으로 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
위 명령어는 DNS 요청 패킷에서 쿼리 된 도메인 이름만 출력합니다. -T fields -e dns.qry.name
옵션을 사용하여 쿼리 된 도메인 이름 필드만 표시하도록 설정했습니다.
tshark를 이용한 DNS 요청 캡처 및 파일로 저장
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 -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
수집한 패킷 확인
수집한 패킷을 확인하려면 다음 명령어를 사용합니다.
# 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
을 반복적으로 실행했기 때문입니다.
텍스트 파일로 저장
캡처한 데이터를 일반 텍스트 파일로 저장하려면 다음 명령어를 사용합니다:
# tshark -r /tmp/test_00003_20240605105317.pcap > /tmp/test_003.txt
그리고 텍스트 파일을 확인합니다:
# 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
...
gethostlatency를 이용한 DNS 조회 프로세스 확인
tshark
와 같은 도구를 사용하여 패킷을 캡처할 때, 어느 프로세스에서 DNS 조회를 수행하는지를 확인하는 것은 쉽지 않습니다. 이러한 경우, DNS 조회를 수행하는 프로세스를 추적하기 위해 gethostlatency
라는 도구를 사용할 수 있습니다. 이 도구는 본래 호스트 이름 해석 지연 시간을 측정하기 위한 용도지만, 프로세스 추적 용도로도 활용할 수 있습니다.
1. gethostlatency 명령어 설치
먼저, 레드햇에서 제공하는 bcc-tools
패키지를 설치합니다. 이 패키지에는 gethostlatency
명령어가 포함되어 있습니다.
# dnf install bcc-tools
2. DNS 조회 테스트용 파이썬 프로그램 실행
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
파이썬 코드는 gethostbyname
함수를 이용하여 DNS 조회를 수행합니다. gethostlatency
명령의 단점은 getaddrinfo
와 gethostbyname
두 함수를 호출하는 경우에만 정보를 수집할 수 있다는 점입니다. 따라서 다른 함수로 DNS를 조회하는 경우에는 gethostlatency
명령으로 확인이 불가능할 수 있습니다.
파이썬 코드
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()
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
4. 프로세스 조회
마지막으로, ps
명령어를 사용하여 특정 PID를 가진 프로세스를 확인합니다.
# ps -ef | grep 598021
root 598021 597129 0 13:41 pts/1 00:00:00 python3 dns.py
참조
https://en.wikipedia.org/wiki/Berkeley_Packet_Filter [BPF Wikipedia]
https://www.redhat.com/sysadmin/using-wireshark-tshark1 [tshark]