現在の自宅ではマンションの無料インターネット回線を使っていて、公称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のやつも中ではこれを使っているっぽい。
参考にしたページをいくつか挙げておく。
- Intercept HTTPS CONNECT messages with SSL-Bump | Squid Web Cache wiki
- How I Saved Tons of GBs with HTTPs Caching | by Rasika Perera | Medium
- HTTPS対応のプロキシサーバー構築 #HTTPS – Qiita
インストール
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.pem
と req.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.proxy
と http.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などでプロキシーを使用するための設定も試したかったが、今回はここまで。