예전엔 DNS 관련하여 디버깅을 하거나 도메인에 연결된 IP를 가져와야 할 일이 있다면 dig 명령어를 사용했는데, 언제부턴가 drill 명령어를 주로 사용하는 추세가 되었다. 마치 netstat 명령어가 없어지고 ss를 대신 사용하게 된 것처럼 이것도 세대교체가 되는 것 같다.
어쨌든 요즘은 drill 명령어가 대부분의 배포판에 기본적으로 포함되어 있으니 이 명령어를 사용하여 DNS를 분석하고 디버깅하는 방법을 알아보자.
먼저 그냥 drill을 실행해보자:
$ drill ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 10730 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; . IN NS ;; ANSWER SECTION: . 511790 IN NS a.root-servers.net. . 511790 IN NS b.root-servers.net. . 511790 IN NS c.root-servers.net. . 511790 IN NS d.root-servers.net. . 511790 IN NS e.root-servers.net. . 511790 IN NS f.root-servers.net. . 511790 IN NS g.root-servers.net. . 511790 IN NS h.root-servers.net. . 511790 IN NS i.root-servers.net. . 511790 IN NS j.root-servers.net. . 511790 IN NS k.root-servers.net. . 511790 IN NS l.root-servers.net. . 511790 IN NS m.root-servers.net. ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 6 msec ;; SERVER: 1.1.1.1 ;; WHEN: Sun Jul 25 22:52:44 2021 ;; MSG SIZE rcvd: 228
dig와 그리 다르지 않다. 참고로 아래가 dig 실행 시 나오는 결과다:
$ dig ; <<>> DiG 9.16.6-Ubuntu <<>> ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53941 ;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 65494 ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 6944 IN NS m.root-servers.net. . 6944 IN NS l.root-servers.net. . 6944 IN NS k.root-servers.net. . 6944 IN NS j.root-servers.net. . 6944 IN NS i.root-servers.net. . 6944 IN NS h.root-servers.net. . 6944 IN NS g.root-servers.net. . 6944 IN NS f.root-servers.net. . 6944 IN NS e.root-servers.net. . 6944 IN NS d.root-servers.net. . 6944 IN NS c.root-servers.net. . 6944 IN NS b.root-servers.net. . 6944 IN NS a.root-servers.net. ;; Query time: 0 msec ;; SERVER: 127.0.0.53#53(127.0.0.53) ;; WHEN: Sun Jul 25 13:54:07 UTC 2021 ;; MSG SIZE rcvd: 239
사용법도 거의 다르지 않다. 차이점이라면 쿼리 옵션을 설정할 때 dig는 +[no]dnssec 형태로 쓰지만 drill은 -D처럼 보편적인 옵션 스타일을 사용한다. 그래도 이미 dig를 사용할 줄 아는 사람은 drill에도 쉽게 적응할 수 있을 것이다.
drill 명령어는 아래와 같은 형태로 사용한다:
drill [ OPTIONS ] name [ @server ] [ type ] [ class ]
- name
도메인이나 IP 등을 의미한다. 뒤에서 설명하겠지만 IP의 경우는 -x 옵션으로 리버스 도메인을 찾을 때 사용된다.
- @server
쿼리를 특정 DNS 서버로 보내고 싶을 때 사용한다. 만약 이것이 생략된다면 /etc/resolv.conf에 설정된 네임서버가 사용된다.
- type
A, AAAA, TXT, PTR 등 쿼리 할 레코드 타입을 설정한다. 생략되었다면 기본적으로 A 타입이 사용된다.
- class
DNS의 클래스를 설정하는 부분으로 IN, CH, HS가 올 수 있고 기본적으로 IN이 사용된다. CH와 HS는 인터넷에서 거의 사용되지 않기 때문에 평소에 이것을 설정할 일은 거의 없을 것이다.
주로 사용되는 옵션은 아래와 같다.
- -T
루트 네임서버부터 name에 설정된 도메인까지 탐색 과정을 모두 출력한다.
- -x
name에 설정된 IP의 리버스 도메인을 쿼리한다. 1.2.3.4의 리버스 도메인을 찾기 위해서는 4.3.2.1.in-addr.arpa PTR로 쿼리해야 하지만 이것을 알아서 해준다.
- -D
DNSSEC에 관련된 타입도 추가로 출력한다. 보통 -T 옵션과 함께 사용하는 경우가 많다.
- -S
DNSSEC 신뢰 사슬이 어떻게 구성되어 있는지 표시한다.
이제 위의 옵션을 실제로 사용해보자. 먼저 google.com의 IPv4 주소를 출력하는 방법은 아래와 같다:
$ drill google.com ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 22087 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; google.com. IN A ;; ANSWER SECTION: google.com. 269 IN A 172.217.27.78 ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 35 msec ;; SERVER: 8.8.8.8 ;; WHEN: Sun Jul 25 23:54:32 2021 ;; MSG SIZE rcvd: 44
타입은 A가 기본이므로 그냥 도메인만 입력하면 나온다. 만약 IPv6 주소를 얻고싶다면 AAAA를 추가하면 된다:
$ drill google.com AAAA ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 32654 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; google.com. IN AAAA ;; ANSWER SECTION: google.com. 79 IN AAAA 2404:6800:4004:809::200e ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 6 msec ;; SERVER: 1.1.1.1 ;; WHEN: Sun Jul 25 23:56:10 2021 ;; MSG SIZE rcvd: 56
-T 옵션을 사용하여 루트 네임서버로부터 blog.stackframe.dev까지의 탐색 과정을 출력하는 방법:
$ drill -T blog.stackframe.dev . 518400 IN NS a.root-servers.net. . 518400 IN NS b.root-servers.net. . 518400 IN NS c.root-servers.net. . 518400 IN NS d.root-servers.net. . 518400 IN NS e.root-servers.net. . 518400 IN NS f.root-servers.net. . 518400 IN NS g.root-servers.net. . 518400 IN NS h.root-servers.net. . 518400 IN NS i.root-servers.net. . 518400 IN NS j.root-servers.net. . 518400 IN NS k.root-servers.net. . 518400 IN NS l.root-servers.net. . 518400 IN NS m.root-servers.net. dev. 172800 IN NS ns-tld1.charlestonroadregistry.com. dev. 172800 IN NS ns-tld2.charlestonroadregistry.com. dev. 172800 IN NS ns-tld3.charlestonroadregistry.com. dev. 172800 IN NS ns-tld4.charlestonroadregistry.com. dev. 172800 IN NS ns-tld5.charlestonroadregistry.com. stackframe.dev. 10800 IN NS ns2.stackframe.dev. stackframe.dev. 10800 IN NS ns1.stackframe.dev. blog.stackframe.dev. 3600 IN A 220.88.252.109
DNS 설정을 했는데 연결이 되지 않는 경우에 디버깅을 위해 이 옵션을 많이 사용하게 될 것이다.
이번에는 위에서 얻은 google.com의 IPv4 주소인 172.217.27.78의 리버스 도메인을 가져와보자:
$ drill -x 172.217.27.78 ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 46641 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; 78.27.217.172.in-addr.arpa. IN PTR ;; ANSWER SECTION: 78.27.217.172.in-addr.arpa. 20700 IN PTR nrt12s15-in-f14.1e100.net. 78.27.217.172.in-addr.arpa. 20700 IN PTR nrt12s15-in-f78.1e100.net. ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 34 msec ;; SERVER: 8.8.8.8 ;; WHEN: Mon Jul 26 00:05:35 2021 ;; MSG SIZE rcvd: 113
KT DNS 서버인 168.126.63.1에 blog.stackframe.dev 쿼리하기:
$ drill blog.stackframe.dev @168.126.63.1 ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 30290 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 ;; QUESTION SECTION: ;; blog.stackframe.dev. IN A ;; ANSWER SECTION: blog.stackframe.dev. 3600 IN A 220.88.252.109 ;; AUTHORITY SECTION: stackframe.dev. 10800 IN NS ns2.stackframe.dev. stackframe.dev. 10800 IN NS ns1.stackframe.dev. ;; ADDITIONAL SECTION: ns1.stackframe.dev. 3600 IN A 220.88.252.109 ns2.stackframe.dev. 3600 IN A 220.88.252.109 ;; Query time: 75 msec ;; SERVER: 168.126.63.1 ;; WHEN: Mon Jul 26 00:10:45 2021 ;; MSG SIZE rcvd: 121
-S 옵션으로 DNSSEC 체인 확인하기:
$ drill -S blog.stackframe.dev ;; Number of trusted keys: 2 ;; Chasing: blog.stackframe.dev. A DNSSEC Trust tree: blog.stackframe.dev. (A) |---stackframe.dev. (DNSKEY keytag: 7538 alg: 13 flags: 257) |---stackframe.dev. (DS keytag: 7538 digest type: 1) | |---dev. (DNSKEY keytag: 21408 alg: 8 flags: 256) | |---dev. (DNSKEY keytag: 60074 alg: 8 flags: 257) | |---dev. (DS keytag: 60074 digest type: 2) | |---. (DNSKEY keytag: 26838 alg: 8 flags: 256) | |---. (DNSKEY keytag: 20326 alg: 8 flags: 257) |---stackframe.dev. (DS keytag: 7538 digest type: 2) | |---dev. (DNSKEY keytag: 21408 alg: 8 flags: 256) | |---dev. (DNSKEY keytag: 60074 alg: 8 flags: 257) | |---dev. (DS keytag: 60074 digest type: 2) | |---. (DNSKEY keytag: 26838 alg: 8 flags: 256) | |---. (DNSKEY keytag: 20326 alg: 8 flags: 257) |---stackframe.dev. (DS keytag: 7538 digest type: 4) |---dev. (DNSKEY keytag: 21408 alg: 8 flags: 256) |---dev. (DNSKEY keytag: 60074 alg: 8 flags: 257) |---dev. (DS keytag: 60074 digest type: 2) |---. (DNSKEY keytag: 26838 alg: 8 flags: 256) |---. (DNSKEY keytag: 20326 alg: 8 flags: 257) ;; Chase successful