当ブログでは、 Raspberry Pi 4B 上に、 Docker でお手軽に Nextcloud を構築しました。そのままでは、外部からアクセスできないので SoftEther VPN も Raspberry Pi 4B にインストールして、外部からのアクセス性を確保しました。
Nextcloud の公式クライアントは仮想ファイルによるクラウド上のファイルへのオンデマンドアクセス、つまり、必要な時にダウンロードしてキャッシュして編集可能にする、昨今の Dropbox や OneDrive のような機能に対応しているので、これで充分便利に使えます。 VPN を接続しなければならないとはいえ、セキュリティの専門家・サーバー管理の専門家でない以上、外部から丸見えのサービスを展開するのはやはり怖いですし、自動で接続するようにしておけばいいだけですし。
残った問題としてはせっかくの Nextcloud なのに、 WebDAV でアクセスできないというのは悲しいです。
WebDAV とは?
HTTP (Webの通信方式)だけでファイルの管理が行えるようにした仕組み。伝統的な Web サイトの更新方法は FTP や scp, rsync など別のプロトコル(通信方式)を利用する必要があったが、 WebDAV を使えばそれらが必要ない(が、あんまり普及していない)。
Nextcloud の場合、Windows でもネットワークドライブとしてマウントすることで、クライアントや Web ブラウザを経由しないでも直接、通常のドライブのようにファイルの読み書きができるのが利点です。
ただ、 HTTP 経由と言いつつ、 Windows でネットワークドライブとして WebDAV をマウントするには SSL 通信が必須になっています。
楽介のようにスクリーンショットを沢山撮るタイプのブログ書き(仕事でもドキュメント作りで一杯撮ります)からすると、環境を変えてもどこからでもスクリーンショットにアクセスしたいですし、そのためにローカルにもクラウドにもデータを保存するというのもちょっともったいない気がします。どこかで一時ファイルが作られるとしても、小さいスクリーンショットくらい、クラウド上にだけ置いてある方が、気分的に整理されていてよいです。
また、写真や動画なども、直接クラウドにアップして、 Web から閲覧して必要なものだけダウンロードできると便利ですね。
ということで、 SSL 通信を出来るようにしてみたいと思います。
SSL に対応させてみよう
もちろん、 Nextcloud が動作している Apache 自体をSSL 化するのも一つの手段なのだと思いますが、 公式の Docker image ではリバースプロキシの使用を推奨すると書かれています。なので、ここでもリバースプロキシを使ってみることにします。
リバースプロキシとは?
リバースプロキシ reverse-proxy とは、 Web サーバーやらアプリケーションサーバーやらの本体の手前に設置される、本体サーバーの代わりに応答するサーバーのことです。受け付けみたいなイメージですね。
目的は色々ありますが、負荷分散やセキュリティ、高速化など多様です。
今回は、本体サーバーである Nextcloud に変わって SSL 通信を行うので、セキュリティの強化となりますね(多分、物理的なハードウェア自体を分けるともっとセキュリティ上、強固になると思います)。
nginx を使う
「リバースプロキシといえば nginx だろ!」みたいな風潮がある、というか他に知らないので今回はdocker と nginx を使ってリバースプロキシを構築、Nextcloud の通信を SSL 化したいと思います。
なお、先に作成した Nextcloud の Docker-compose に含めてしまって、同じコンテナの中で動作させるというのも一つだとは思いますが、将来的に「リバースプロキシとアプリケーションサーバーを分けたい」となったときなどに不便そうなので今回は別に Docker-compose を書いて、コンテナを作って見ます。その方が、他のアプリケーションでも使えて応用が利きますしね。
Docker と Nginx, OpenSSL でオレオレ証明書を発行する
参考
暗号化の話なのに証明書?
単に暗号化通信といっても、ランダムに暗号化を施した情報を流しても解読できません。かといって、誰からアクセスできるか分からないインターネットの仕組み上、事前に暗号化の仕組み(日本で有名な「たぬき」とか)を共有しておくことも現実的ではありません。みんなが同じ暗号化/復号化の仕組みを使って通信をしてしまうと、誰でも他人の通信を覗き見できてしまいますからね。
そこで、 SSL/TLS 通信では共通鍵/秘密鍵による暗号化と共通鍵暗号を組み合わせて個別の暗号の鍵を作っているのですが(詳細は省きます)、ここでも一つ問題が残ります。それは、初めてアクセスするサーバーから渡される暗号の鍵が、果たして本物かどうかが分からない、ということです。
例えば銀行のサーバーにアクセスしたつもりが、犯罪集団の管理するサーバーで口座情報を全て盗まれてしまった……となっては目も当てられません。「フィッシング詐欺なら、URL に気をつけておけば……」という話ではなく、全く正しいサーバーから送られてきた(ように見える)情報が別のところから送信されていることが起こりえます。
それを防ぐ(というか検証する)ためにあるのが「証明書」で、暗号化された通信が、正しくそのサーバーとの通信に用いられることを証明します。また、この証明書は一度発行されれば永久に使えるものではなく、現在、約13ヶ月が事実上の最大期限となっています。
参考サイト様を参考にやってみる
まずは、証明書を作成する Dockerfile を作成します。
compose-docker に使われている Long Syntax が ARM64 apt-get でインストールできる docker-compose のバージョンでは利用できないようなので、cron で定期実行されるようにします。ただ定期実行されるだけでは Nginx が発行する証明書は変わらないのでは?(キャッシュ的な・メモリ的なものから発行されるのでは)という気もしますが、その辺はオレオレ証明書なので気にしないことにします。だめだったら ssh で docker-compose down, docker-compose up をしましょう。
まずは、
sudo apt-get update
sudo apt-get install openssl
コマンドで openSSL をインストールします。また、 nginx の docker-compose と SSL を格納するディレクトリを作成します。
mkdir -p /home/pi/rproxy/ssl
後で rproxy ディレクトリに nginx 用の docker-composeを格納します。
ディレクトリの作成ができたら、
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes -keyout /home/pi/rproxy/ssl/domain.key -out /home/pi/rproxy/ssl/signed.crt -subj "/CN=raspberrypi.local" -addext "subjectAltName=DNS:raspberrypi.local" -addext "extendedKeyUsage=serverAuth"
バックスラッシュ(または円) \ 記号で区切って、改行を途中にいれた方がみやすいですが、後で cron にそのままコピーしたいので、1行で無理矢理書いてしまっています。
keyout, out オプションには出力する証明書や鍵ファイルを指定します。絶対パスで、作成したディレクトリに格納されるようにします。また、CN=, AltName=DNS:… には、普段 Raspberry Pi へのアクセスに利用しているホスト名を指定します。
openssl のオプションは、暗号化の形式と期限などを設定しています。が、ここでは本題ではないので省きます。
openssl を実行し、
最終的に、 writing new private key to …. というメッセージが表示されたら、ここではOKとします。サーバーと組み合わせてみないとここでの作業が本当にただしいかどうか、検証のしようもないので先に進みます。
Nginx をイメージから起動する
参考サイト様では、 nginx も build しているみたいですが、よく分からないので image を利用させてもらいます。
ここからは、以前と同様に docker-compose で楽をさせてもらいます。
mkdir -p /home/pi/rproxy
cd /home/pi/rproxy
mkdir log
mkdir conf
touch docker-compose.yml
nano docker-compose.yml
として、リバースプロキシに使う Docker 用のディレクトリと docker-compose.yml を用意します。
最初は、 SSL は気にせずに nginx が HTTP のリバースプロキシとして起動するようにしてみます。
version: "3"
services:
nginx:
image: nginx
container_name: nginx
ports:
- "8181:80"
volumes:
- /home/pi/rproxy/conf:/etc/nginx/conf.d
- /home/pi/rproxy/log:/var/log/nginx
docker-compose up -d
で Docker コンテナを立ち上げてみます。問題なく起動し、エラーログなどは rproxy/log ディレクトリに作成されますが、 config ファイルは残念ながら作成されないようです。
なので、
docker-compose down
cd conf
touch nginx.conf
nano nginx.conf
として、 一旦 nginx が起動しているコンテナを終了させた後 nginx.conf ファイルを編集します。
server {
listen 80;
server_name raspberrypi.local;
location / {
proxy_pass http://192.168.1.181:8080;
}
}
server_name は変更していたら各自の環境にし、proxy_pass も自身の環境に合わせてください。ただし、 proxy_pass ではraspberrypi.local というホスト名は使えません。固定した IP アドレスを使います。楽介の場合は、以前の記事の通り、 8080 ポートでNextcloud が待ち受けているので上記のようになっています。IP アドレスは、192.168.1.181 でした(もし固定していない方は固定した方がいいでしょう)。ここも、各自の環境に揃えてください。
config ファイルの作成まで終わったら、ブラウザで nginx にアクセスします。ここまでの手順通りにやっていた場合は、 raspberrypi.local:8181 ですね。
正常にアクセスできると、下図のようになります