Raspberry PiをHTTP/HTTPSプロキシサーバーにしてみた

プロキシサーバーとは、主に**HTTP/HTTPS通信を"代理"(Proxy)**するサーバーのことを言います。
PCやスマホ、ゲーム機のネットワーク設定で、プロキシを設定することでHTTP/HTTPSの通信ログを取得したり、ドメイン名などを指定して特定のWebサイトやサービスをブロックすることができます。

今回の記事では[Squid](http://www.squid-cache.org/)というソフトを利用し、題目通りRaspberry PiをHTTP/HTTPSプロキシサーバーとして機能させ、[Nintendo Switch](https://www.nintendo.co.jp/hardware/switch/)にプロキシの設定をしてHTTP/HTTPS通信ログをみてみる実験をしてみます。

![illust-proxy.png](https://qiita-image-store.s3.amazonaws.com/0/137045/b2ea7594-408b-a181-8960-685d9618cbca.png)

# 動作環境(確認済み)
- Raspberry Pi 3 Model B
    - Raspbian Stretch Lite 2017-11-29
        - イメージをMicroSDに書き込んだ後、MicroSDのルートディレクトリに`ssh`という名前で空のファイルを作成
        - 家庭用無線LANルーターに有線LAN接続

# 設定
## 初期設定
[あなたのお家に眠るラズパイを救出したい](https://speakerdeck.com/mascii/anatafalseojia-nimian-rurasuhaiwojiu-chu-sitai)のスライドなどを参考に、有線LAN接続を用いてPCからSSH接続できる状態にしておいてください。
`ssh`または`ssh.txt`という名前のファイルをPC上でMicroSDに保存しておく必要があることに気をつけてください。
必要であればテキストエディタとして、Vimなどをインストールしてください。

## Squidのインストール
```
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install squid -y
```

## Squidの設定
サイボウズさんの技術ブログの記事[^1]を参考にSquidの設定をしました:

```squid:/etc/squid/squid.conf
# ローカルネットワークの定義
acl localnet src 10.0.0.0/8     # RFC1918 possible internal network
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
# acl localnet src fc00::/7 # RFC 4193 local private network range
# acl localnet src fe80::/10# RFC 4291 link-local (directly plugged) machines

# SSL接続時に 443 ポート以外の CONNECT を拒否
acl SSL_ports port 443
acl CONNECT method CONNECT
http_access deny CONNECT !SSL_ports

# 接続先として指定されているポート以外を拒否
acl Safe_ports port 80    # http
acl Safe_ports port 21    # ftp
acl Safe_ports port 443   # https
acl Safe_ports port 70    # gopher
acl Safe_ports port 210   # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280   # http-mgmt
acl Safe_ports port 488   # gss-http
acl Safe_ports port 591   # filemaker
acl Safe_ports port 777   # multiling http
http_access deny !Safe_ports

# キャッシュの設定( manager を定義してないので無効な値)
http_access allow localhost manager
http_access deny manager

# ローカルネットワークからのアクセスを許可
http_access allow localnet
   
# 自身からのアクセスを許可
http_access allow localhost

# ここまで一致しなかった場合は拒否
http_access deny all

# Squid が使用するポート
http_port 3128

# core 出力場所の設定
coredump_dir /var/spool/squid

# キャッシュの設定
# refresh_pattern ^ftp:     1440    20%     10080
# refresh_pattern ^gopher:  1440    0%1440
# refresh_pattern -i (/cgi-bin/|\?) 0     0%0
# refresh_pattern .   0 20%     4320

# QueryStringの記録
strip_query_terms off

# Apache風の形式でログを記録
logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
access_log /var/log/squid/access.log combined
```

IPv6のローカルネットワークの定義があるとSquidがうまく動かなかったので、コメントアウトしました。

http通信に関しては、URLの `?` 以降のクエリー文字列も取得できるので、クエリー文字列取得のための設定をしました[^2]

標準では以下のように、`1517980020.712   1150 192.168.10.3 TCP_TUNNEL/200 4531 CONNECT accounts.nintendo.com:443 - HIER_DIRECT/54.175.219.211 -`　のように、Unix時間から始まるログが記録されるので、下記のログのようなApache風のログを出力するための設定をしました[^3]

```
192.168.10.3 - - [09/Feb/2018:16:47:38 +0900] "CONNECT accounts.nintendo.com:443 HTTP/1.1" 200 4531 "-" "libcurl (nnAccount; 789f928b-138e-4b2f-afeb-1aca
e821d897; SDK 4.4.0.0; Add-on 4.4.0.0)" TCP_TUNNEL:HIER_DIRECT
```

設定を反映するには、以下のコマンドでSquidを再起動します:

```
sudo systemctl restart squid
```

## Timezoneの設定
Squidのアクセスログの時刻をJST(UTC+9)で出力させるため、以下のコマンドを実行します:

```
sudo timedatectl set-timezone Asia/Tokyo
```

## logrotateの設定
Squidをaptでインストールすると、 `/etc/logrotate.d/` にSquidのアクセスログに関するログローテーションの定義ファイルが入ります。
この定義ファイルがあると、深夜3時台に１日分のログファイルがリネームされ、過去2日分だけログが保存されるようになります。
少しの間だけプロキシサーバーを立てて試してみるのには不要でしたので、以下の定義ファイルの内容をすべて `#` でコメントアウトしてみました。
(x86マシンなどでプロキシサーバーの長期運用を考えている際には、適切なログローテーションの運用が必要ですので、注意してください)

```/etc/logrotate.d/squid
#
#	Logrotate fragment for squid.
#
/var/log/squid/*.log {
	daily
	compress
	delaycompress
	rotate 2
	missingok
	nocreate
	sharedscripts
	prerotate
		test ! -x /usr/sbin/sarg-reports || /usr/sbin/sarg-reports daily
	endscript
	postrotate
		test ! -e /var/run/squid.pid || test ! -x /usr/sbin/squid || /usr/sbin/squid -k rotate
	endscript
}
```

logrotateについては、サービスの再起動なしで設定が反映されます。

# IPアドレスの固定設定
Nintendo Switchは[Zeroconf](https://ja.wikipedia.org/wiki/Zeroconf)に対応していないようなので、Raspberry PiのIPアドレスは固定しておくと良いです。
IPアドレスを固定するには、dhcpcdで設定するか、ルーター(DHCP)で設定します。

## dhcpcdで設定
`/etc/dhcpcd.conf` に設定を追記してRaspberry Piを再起動することで、IPアドレスを固定できます。
参照: [Raspberry Pi 2 (Raspbian: jessie) でIPアドレスを固定する](https://qiita.com/ykog/items/a6dbba1c09e870f8f702)

## ルーター(DHCP)で設定
ルーターのDHCP機能を利用している方が大半だと思いますが、MACアドレスを指定してDHCPで割り当てられるIPアドレスを固定することができます。

![aterm-dhcp.png](https://qiita-image-store.s3.amazonaws.com/0/137045/e1270e80-5dc1-b1b7-d4d0-b87820274d1f.png)

Raspberry PiのMACアドレス、割り当てられているIPアドレスを調べるには、`ip addr` コマンドを実行します(`eth0`が有線LAN、`wlan0`が無線LANです)

```
pi@raspberrypi:~ $ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.10/24 brd 192.168.10.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2405:6581:9b20:1200:4504:c156:f42e:b053/64 scope global mngtmpaddr noprefixroute dynamic 
       valid_lft 2591961sec preferred_lft 604761sec
    inet6 fe80::12d6:b296:64dd:10e3/64 scope link 
       valid_lft forever preferred_lft forever
3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether b8:27:eb:xx:xx:xx brd ff:ff:ff:ff:ff:ff
```

# アクセスログを見てみる
`less`コマンドを使ってアクセスログをみることができます:

```
sudo less /var/log/squid/access.log
```
アクセスログを開いている際、大文字`G`を入力で、最終行までジャンプできます。

アクセスログをリアルタイムで見たい場合は`tail`コマンドを使います:

```
sudo tail -f /var/log/squid/access.log
```

# Nintendo Switchにプロキシを設定してみる
このスクリーンショットのように、接続先単位でプロキシを設定できます
![switch-proxy.jpg](https://qiita-image-store.s3.amazonaws.com/0/137045/eeac7c58-a8c5-a078-39ab-6bd04f245b11.jpeg)

# Nintendo SwitchのHTTP/HTTPS通信ログを見てみる
## 1日放置してみた
[Nintendo みまもり Switch](https://www.nintendo.co.jp/hardware/switch/parentalcontrols/)を設定したNintendo Switchを、30時間ほど放置してみたところ、145行ほどのログが出力されていました。この記事にログを貼り付けると長くなってしまうのでGistに置きました:
https://gist.github.com/mascii/73334b6994c699fd30c065e59bc7efac

SwitchのOSは、ほぼHTTPSで通信していることがわかりました。
[api-lp1.pctl.srv.nintendo.net](https://api-lp1.pctl.srv.nintendo.net/)はブラウザで開いてみると、みまもり Switchのアプリのダウンロードリンクだけあるページが表示されるので、これはみまもり Switch関連の通信だと推測します。6時間に1回みまもり Switch関連の通信があると思われます。
他にも、ニンテンドーeショップやゲームニュース関連の通信が6時間に1回程度あるものと思われます。

## niconicoアプリ
ニンテンドーeショップから無料でダウンロードできる[niconico](https://site.nicovideo.jp/switch/)アプリで、[ニコニコ動画](http://www.nicovideo.jp/video_top)を観てみたところ、以下のようなHTTPのログが残っていました:

```
192.168.10.3 - - [10/Feb/2018:15:10:53 +0900] "GET http://flapi.nicovideo.jp/api/getflv?v=1182590816&device=nicoswitch&eco=1 HTTP/1.1" 200 1058 "-" "NicoSwitchAgent/2.00 (handheld)" TCP_MISS:HIER_DIRECT
```

クエリー文字列までログに残すようにしていたので、[組曲『ニコニコ動画』](http://www.nicovideo.jp/watch/1182590816)を観たことがバレバレでした。SwitchのOSのログがほぼHTTPSだったので、ちょっと残念。

# まとめ
組み込み機器やゲーム機がHTTP/HTTPSでいつどんな通信しているか、プロキシサーバーを立てて見てみることはやはり面白かったです。
ただ、Raspberry PiではストレージがmicroSD(ログの書き込みなどによる故障の確率が高い)であったり、Gigabit Ethernetに非対応だったりするので、長期運用や速度は望めなさそうです。

小規模オフィスでプロキシによるアクセス制御(SNSやオンラインストレージの使用制限など)や、アクセスログの収集が必要になった際に、Raspberry Piを試験的なプロキシサーバーとしてみるような使い方もできると思います。x86マシンにCentOSとSquidをインストールし、logrotateやfluentdの設定をしっかりやって...などの前段階の調査に良さそうです。

また、DHCP環境だと端末に割り当てられるIPアドレスが頻繁に変わることがあるので、「いつ、**IPアドレス**がどのWebサーバーにアクセスしたか」だけでなく、「いつ、**どの端末**がどのWebサーバーにアクセスしたか」までログに残す必要がある場合は、DHCPのログも必要になると思われます。家庭用ルーターだとDHCPのログをSyslogでログサーバーに転送するといった機能はないので、[Node.js](https://nodejs.org/ja/)を用いてルーターの管理画面にあるDHCPのログをHTTPでスクレイピングするような仕組みを作ると良いと思います。
![aterm-dchp-log.png](https://qiita-image-store.s3.amazonaws.com/0/137045/aa0c96a7-4e03-7b61-d3ba-cee2f0372940.png)

# 参考文献
[^1]: [Squid で安全なインターネットアクセス環境を構築する方法 - Cybozu Inside Out | サイボウズエンジニアのブログ](http://blog.cybozu.io/entry/2017/02/03/080000)
[^2]: [squidのアクセスログにQueryStringを記録する方法 - iRSSの日記](http://d.hatena.ne.jp/iRSS/20110522/1306020100)
[^3]: [Squid2.6 access.logをApahceのaccess_log風にする方法 - TrippyBoyの愉快な日々](https://blog.trippyboy.com/2011/squid2-6/squid2-6-access-log%E3%82%92apahce%E3%81%AEaccess_log%E9%A2%A8%E3%81%AB%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)