アプリ開発などで、本番用ドメインを使用したまま開発環境へアクセスしたいケースがある。
今回は、bindのRPZという機能を使い特定のレコードの応答を偽装してみる。
RPZについて
JPNICの「RPZとは」より引用。
RPZ (Response Policy Zones)とは、フィッシングサイトやマルウェア配布サイトといった、 特定のノードへの接続防止などを目的とした、DNSによるフィルタリング機能の一つです。
元々はフィルタリング機能であり、近年では児童ポルノ対策のブロッキング方式として採用されているケースもある。
今回はこの機能を利用する。
用意するもの
ざっくり以下の3つを用意。
- DNSサーバ
- named.conf
- rpzゾーンファイル
なお、DNSサーバについては、「検証用にDockerでbindを動かす」をベースに利用するため割愛。
named.conf
named.confの例
logging {
channel "query-log" {
file "/var/log/query.log" versions 6 size 1024M;
severity dynamic;
print-time yes;
print-category yes;
};
category queries { "query-log"; };
category lame-servers { null; };
category resolver { null; };
};
include "/etc/rndc.key";
controls {
inet 127.0.0.1 allow { 127.0.0.1; } keys { "rndc-key"; };
};
acl "access-list" {
127.0.0.1;
172.17.0.0/16;
};
options {
directory "/var/named/";
pid-file "/run/named/";
dump-file "/var/named/named_dump.db";
statistics-file "/var/named/named.stats.log";
zone-statistics yes;
version "";
masterfile-format text;
recursive-clients 10000;
tcp-clients 10000;
allow-recursion { access-list; };
allow-query { access-list; };
allow-query-cache { access-list; };
response-policy { zone "rpz.zone"; };
};
view "internal" {
recursion yes;
zone "." {
type hint;
file "named.root";
};
zone "0.0.127.in-addr.arpa" {
type master;
file "localhost.rev";
};
zone "rpz.zone" {
type master;
file "rpz.zone";
};
zone "sample.com" {
type master;
file "sample.com.zone";
};
};
解説
response-policyオプションの設定
optionsにresponse-policyを定義する。
zoneは、後々定義するzone名と合わせる必要がある。
response-policy { zone "rpz.zone"; };
rpz.zoneの定義
通常zone名はゾーンそのものを指すが、rpzの場合は特に意味はない。
fileにはrpzの設定ファイル名を指定する。
zone "rpz.zone" {
type master;
file "rpz.zone";
};
rpzゾーンファイル
$TTL 900
@ IN SOA localhost. postmaster.localhost. (
2020062101 ; Serial Number
1800 ; Refresh
900 ; Retry
1209600 ; expire
900 ; minimum
)
@ IN NS localhost.
www.sample.com IN A 127.0.0.1
google.com IN A 127.0.0.1
hogefugamoge.yahoo.co.jp IN A 127.0.0.1
重要なところは、下三行のレコード部分。
上書きしたいドメイン名をFQDNで記載し、期待する応答をレコードとして記載する(終端のピリオドはなくてもOK)。
存在する・しないに関わらず記載することができるため、柔軟にDNSの偽装が可能である。
検証
上記の設定ファイルを元にDockerを起動する。
今回は以前起動したDNSのimageがあったので、buildして再作成。
$ sudo docker build . -t bind
$ sudo docker stop bind
$ sudo docker rm bind
$ sudo docker run -it -d -p 53:53 -p 53:53/udp --name bind bind:latest
・google.comを引いてみる
$ dig @localhost google.com +nostat
; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.3 <<>> @localhost google.com +nostat
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62337
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 5 IN A 127.0.0.1
;; AUTHORITY SECTION:
rpz.zone. 900 IN NS localhost.
・存在しないドメイン(hogefugamoge.yahoo.co.jp)を引いてみる
$ dig @localhost hogefugamoge.yahoo.co.jp +nostat
; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.3 <<>> @localhost hogefugamoge.yahoo.co.jp +nostat
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17667
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;hogefugamoge.yahoo.co.jp. IN A
;; ANSWER SECTION:
hogefugamoge.yahoo.co.jp. 5 IN A 127.0.0.1
;; AUTHORITY SECTION:
rpz.zone. 900 IN NS localhost.
うまく機能している!
注意点
RPZはDNS応答を「上書き」する機能であるため、実際の名前解決フローは実施される。
そのためbindが名前解決をできる状態でないと機能しない。
例えば、外部疎通性のない環境では下記のようになる。
$ dig @localhost google.com +nostat
; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.3 <<>> @localhost google.com +nostat
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37778
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com. IN A
google.comが解決できないため、SERVFAILとなる。。。
また、権威を持っているゾーンについても上書きできなかった。
・sample.comゾーンの場合
$ dig @localhost www.sample.com +nostat
; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.3 <<>> @localhost www.sample.com +nostat
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57804
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.sample.com. IN A
;; ANSWER SECTION:
www.sample.com. 900 IN A 127.0.0.1
;; AUTHORITY SECTION:
sample.com. 900 IN NS sample.com.
;; ADDITIONAL SECTION:
sample.com. 900 IN A 127.0.0.1
この辺りはdnsmasqの方が柔軟かもしれない。