ラズパイにSquidでキャッシュサーバーを立てる

現在の自宅ではマンションの無料インターネット回線を使っていて、公称100Mbpsである。実測で90Mbps程度は出るようだが、決して早い部類ではない。工事して個別に光回線を引き込めばいいのかもしれないが、初期費用もかかるし月額料金もかかる。その前に現状でできることはやっておきたい。

回線が細いと何が辛いかというと、同じ(巨大な)gitリポジトリーをcloneしたり、でかいバイナリー(GHCとか)を何回も落としてくるのがしんどい。こういうダウンロードするものを何とかして手元にキャッシュできないか。

家にはラズパイやNASが転がっている。これらは常時稼働しているので(最近はNASは止めているが)、これらにキャッシュサーバーを立てるのが良いだろう。

QNAPのNASの一部機種はProxy Serverに対応しているようだが、残念ながらうちにあるやつは非対応だ。それに、どっちみちHTTPSには(オフィシャルには)対応していない。

ということで、ラズパイにプロキシーサーバーを立てることにする。

今使っているラズパイはRaspberry Pi 4の8GBモデル、ストレージはUSB接続のSSD(256GBくらい)だ。OSはUbuntu 22.04を入れている。今回の記事でラズパイらしさは(最後に書く件以外は)ないので、Ubuntuなら同様にできるだろう。

プロキシーサーバーの実装として定番なのがSquidと呼ばれるソフトウェアのようだ。前述したQNAPのやつも中ではこれを使っているっぽい。

参考にしたページをいくつか挙げておく。

インストール

SquidはUbuntuにAPTで入る。squid というパッケージ名のものもあるが、それだとHTTPS対応するのに必要なconfigureオプションが指定されていないようだった。ここは squid-openssl パッケージを入れる。

$ sudo apt install squid-openssl

ここではバージョン5.7が入った。最新ではない。

オレオレ証明書の用意

HTTPS通信を傍受するために、オレオレ証明書を用意する。

$ openssl req -new -newkey rsa:2048 -sha256 -days 365 -noenc -x509 -extensions v3_ca -keyout key.pem -out req.pem
$ cat key.pem req.pem > myCA.pem

証明書を作るときにCountry Nameとか色々聞かれるが、適当に入力しておく。

ここでは key.pemreq.pem を連結して myCA.pem を作っているが、openssl コマンドに -keyout myCA.pem -out myCA.pem と指定して最初から連結されたものを作ることもできる。

作った証明書は /etc/squid/ssl_cert に移しておく。

$ sudo mkdir -p /etc/squid/ssl_cert
$ sudo mv myCA.pem /etc/squid/ssl_cert
$ sudo chown -R proxy:proxy /etc/squid/ssl_cert
$ sudo chmod 700 /etc/squid/ssl_cert

Squidの設定

Squidの設定ファイルは /etc/squid/squid.conf にある。コメントが豊富に書かれている。初期状態のファイルは別途保存しておく。

$ cd /etc/squid
$ sudo cp squid.conf squid.conf.original
$ sudo emacs squid.conf

設定内容は、例えばこんな感じだ:

maximum_object_size 10 GB # キャッシュする最大オブジェクトサイズ
cache_dir aufs /var/spool/squid 50000 16 256 # ディスクキャッシュは50GB確保する

# 許可するクライアントの設定
acl myclients src 192.168.5.0/24 # 環境に合わせて
acl myclients src fe80::/10
http_access allow myclients

# SSL Bumpの設定
http_port 3128 ssl-bump cert=/etc/squid/ssl_cert/myCA.pem generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
sslcrtd_program /usr/lib/squid/security_file_certgen -s /var/lib/ssl_db -M 4MB
acl step1 at_step SslBump1
ssl_bump peek step1
ssl_bump bump all

# キャッシュ対象の設定
# ここに書いたドメインだけをキャッシュ対象にしたい
acl cache_targets dstdomain .github.com
acl cache_targets dstdomain .githubusercontent.com
acl cache_targets dstdomain downloads.haskell.org
acl cache_targets dstdomain gitlab.haskell.org
cache allow cache_targets
cache deny all

記述の順番が大事だとどこかで見たので、うまくいかなかったら順番を変えてみると良いかもしれない。

設定を変更したらサービスを再起動する。

$ sudo systemctl restart squid.service

使う

早速使ってみる。デフォルトでは3128ポートで動作している。

$ curl -x http://localhost:3128 http://downloads.haskell.org

無事にアクセスできただろうか?Squidのアクセスログは /var/log/squid/access.log で確認できる。

今度はHTTPSを試してみる。

$ curl -x http://localhost:3128 https://downloads.haskell.org

正常に動作していれば、curl: (60) SSL certificate problem: self-signed certificate in certificate chain というエラーが出るはずだ。Man-in-the-middleならぬSquid-in-the-middle状態である。

正常にダウンロードするには、先ほど作った証明書を指定すれば良い。

$ curl -x http://localhost:3128 --cacert req.pem https://downloads.haskell.org

適切にファイヤーウォールの設定とかをすれば、他のマシンからもアクセスできるはずだ。その際、利用者となるマシンに証明書をコピーしてそれを指定する必要がある。

Gitから利用するには、http.proxyhttp.sslCAInfo を指定する。

$ git clone https://github.com/minoki/LunarML.git --config http.proxy=http://localhost:3128 --config http.sslCAInfo=req.pem

という感じだ。

いちいちオプションを指定したくない場合は、環境変数 http_proxy だか https_proxy だかを指定する。証明書の指定は、curlに対しては CURL_CA_BUNDLE という環境変数を使えるようだ。.curlrc のような設定ファイルを書くのも良いだろう。

思ったほど速くない

というわけで、GHCのbindistのダウンロードとかを試しているが、キャッシュにヒットした状態でも思ったほど速くない。何ならプロキシーを介さない方が瞬間的には速度が出ていたりする。

これはラズパイのCPUがへぼいのが原因なのか?ダウンロード中のCPU使用率は100%に張り付いている。

なので、もっと強力なマシンでもSquidをセットアップして比較してみる必要があるのではないかと思っている。

本当はDockerやWSL2などでプロキシーを使用するための設定も試したかったが、今回はここまで。