サイトブロッキングが話題なのでDNSブロッキングを実現するための方法を検証してみた

本日、NTTコミュニケーションズ、NTTドコモ、NTTぷららの3社はサイトブロッキングを実施するとの方針を発表しましたね。

www.ntt.com https://www.asahi.com/articles/ASL4R4Q81L4RULFA01G.htmlwww.asahi.com

技術的にはどのように実現すればよいのか興味があるので検証してみました。

サイトブロッキングを実現するための方法の一つとして、「DNSブロッキング」という手法があるそうです。ISPがユーザに対して提供しているDNSキャッシュサーバでブロックしたいサイトの名前解決をできないようにする手法です。ただこの手法を用いても、ユーザがISPの管理外の 1.1.1.1, 1.0.0.1, 8.8.8.8, 8.8.4.4 のようなpublic DNS serverを使えば回避できてしまうのであまり意味はありません。

DNSブロッキングに対応したDNSサーバを構築する

今回はOSとしてubuntu16.04の上でDNSキャッシュリゾルバであるunboundを構築して設定していきます。

まずはふつうのDNSフルリゾルバを構築する

まずはユーザに提供するようのDNSフルリゾルバを作成します。 aptコマンドを使ってunboundをインストールします。

ubuntu@dns-resolver01:~$ sudo apt install unbound

ubuntu@dns-resolver01:~$ sudo service unbound status
● unbound.service
   Loaded: loaded (/etc/init.d/unbound; bad; vendor preset: enabled)
  Drop-In: /run/systemd/generator/unbound.service.d
           └─50-insserv.conf-$named.conf, 50-unbound-$named.conf
   Active: active (running) since Mon 2018-04-23 06:57:24 UTC; 17min ago
     Docs: man:systemd-sysv-generator(8)
   CGroup: /system.slice/unbound.service
           └─2297 /usr/sbin/unbound

インストールコマンドを1行打つだけでunboundがインストールされて起動されていることがわかりますね。この状態だと外部からのアクセスに応答しないようになっているので、private IP アドレスからの問い合わせには応答するようにしてみましょう。

/etc/unbound/unbound.conf.d/dns-cache.conf というファイルを作成し、以下の内容を記述します。

server:
    interface: 0.0.0.0
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 192.168.0.0/16 allow
    access-control: 127.0.0.1/32 allow

完了したら、以下のコマンドで再起動をします。

ubuntu@dns-resolver01:~$ sudo service unbound restart

これで別のホストからのアクセスにも応答できるはずです。別ホストからDNSサーバとして参照してみた時の結果が以下です。 10.55.48.99 というのは今回構築したサーバのIPアドレスです。

ubuntu@ubuntu01:~$ dig +short google.com @10.55.48.99
216.58.197.174

DNSによるサイトブロッキングを実現する

ここからが本題です。特定のサイトへアクセスできないようにするにはどうすればよいのでしょうか。サイトブロッキングの対象とされている anitube.se にアクセスできないようにしてみましょう。まずは特に設定を入れない状態で anitube.se の名前解決をしてみます。

ubuntu@ubuntu01:~$ dig +short anitube.se
104.20.203.27
104.20.202.27

2つのIPアドレスが返ってきましたね。名前解決ができています。これを解決できないようにしたいということです。

Unboundのpython拡張

Unboundにはpythonスクリプトで拡張をする機能があります。ドキュメントを参考にして実装しました。 https://unbound.net/documentation/pythonmod/examples/example0.html

gist.github.com

def filter_domain(qstate, id):
    domain = qstate.qinfo.qname_str.rstrip('.')
    if domain in block_domains:
        qstate.return_rcode = RCODE_NXDOMAIN
        qstate.ext_state[id] = MODULE_FINISHED
    else:
        qstate.ext_state[id] = MODULE_WAIT_MODULE

filter_domainという箇所でクエリで問い合わせられているドメインがブラックリストに入っていれば名前解決を続行せずに NXDOMAIN を即答するようになっています。

このスクリプトを /etc/unbound/unbound/domain_filter.py に設置します。

Unboundの設定

UnboundのPython拡張をインストールします。

ubuntu@dns-resolver01:~$ sudo apt install python-unbound

/etc/unbound/unbound.conf.d/dns-cache.conf を以下のように書き換えます。

server:
    interface: 0.0.0.0
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 192.168.0.0/16 allow
    access-control: 127.0.0.1/32 allow
    module-config: "python validator iterator"
python:
    python-script: "/etc/unbound/domain_filter.py"

ドメインのフィルタ対象の一覧ファイルを /etc/unbound/block_domains.txt に作成します。1行1ドメインで列挙していきます。

anitube.se

unboundを再起動します。

ubuntu@dns-resolver01:~$ sudo service unbound restart

動作確認

ubuntu@ubuntu01:~$ dig anitube.se @10.55.48.99

; <<>> DiG 9.10.3-P4-Ubuntu <<>> anitube.se @10.55.48.99
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 17521
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;anitube.se.                    IN      A

;; Query time: 0 msec
;; SERVER: 10.55.48.99#53(10.55.48.99)
;; WHEN: Mon Apr 23 10:01:15 UTC 2018
;; MSG SIZE  rcvd: 39

anitube.se を名前解決しようとしてもレコードが返ってこなくなりました。 ログを見るかぎり、結果はキャッシュされているようなので毎回実行されるわけではなさそうです。

追記

BINDだとRPZという機能を使えばDNSブロッキングができるらしい。職場の方に教えてもらいました。