当ブログでは、 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 ですね。
正常にアクセスできると、下図のようになります。
これは Nextcloud のセキュリティ機能の一環で、正しいドメイン経由でないとアクセスを拒否するというものです。かといって、 raspberrypi.local というホスト名は(通常は)Dockerコンテナ内からは使えません(nginx のconfig をいじって試してみましょう)。
そもそも nginx を Docker で動かさないというのも一つの手段ですが、ここでは Nextcloud の設定をいじって問題を回避します。
cd /home/pi/nextcloud/nextcloud/config
などとして nextcloud のconfig ディレクトリに移動します。
まずは、 画面の表示通り、 cofig.sample.php を参考に trusted_domains の設定を確認してみます。
sudo less config.sample.php
less コマンドで、スペースキーを数回叩いて画面をスクロールすると、問題の trusted_domains の例が見つかります(終了は q キー)。
'trusted_domains' =>
[
'demo.example.org',
'otherdomain.example.org',
'10.111.112.113',
'[2001:db8::1]'
],
PHP ファイルなので少し見慣れない書式ですが、角括弧[]の中にシングルクォーテーションでドメインまたは IP アドレスを記入すればいいようです。
そうと分かれば、同じディレクトリにある config.php ファイルを編集します。間違えると nextcloud が起動しなくなってしまうので、慎重に行いましょう!(cp コマンドでバックアップをとっておくのはいい考えです)
sudo nano config.php
エディタでコンフィグファイルを開いて探すと、下図のような記述が見つかります。
早速、サンプルと違って泣きたくなりますが、要は配列に入っていればいいので、下記のように修正します。
'trusted_domains' =>
array (
0 => 'raspberrypi.local:8080',
1 => '192.168.1.181:8080',
),
trusted_domains という名前の配列に、信用できるドメインが順に入力されていれば書式は多少違って大丈夫です。
記述が終わったら、 Nextcloud の docker-compose.yml のあるディレクトリに移動し、コンテナを再起動します。
cd /home/pi/nextcloud
docker-compose down
docker-compose up -d
無事再起動したら、Nginx 経由でアクセスしてみます。
このとき、
http://raspberrypi.local:8181/
にアクセスすると、
http://raspberrypi.local/apps/dashboard/
にリダイレクトされてしまい、上手くいきません……。一旦、
http://raspberrypi.local:8181/apps/dashboard/
にアクセスしてみましょう。そうすると、アクセスできるはずです。また、「ファイル」の中に入り、右下の設定にある WebDAV の項目を確認します。このとき、
上図のように固定 IP アドレス + 元々設定していたポート番号(現在アクセスしていたものではなく)が URL に表示されていれば Nginx 経由でのアクセスはとりあえず成功です。
SSL を設定する
では、このリバースプロキシを SSL 経由で動作させます。nginx のディレクトリに戻り(cd /home/pi/rproxy)、nginx.conf を編集します。
server {
listen 443 ssl;
server_name ${NGINX_SERVER_NAME};
ssl_certificate /etc/ssl/certs/nginx/signed.crt;
ssl_certificate_key /etc/ssl/certs/nginx/domain.key;
location / {
proxy_pass http://192.168.1.181:8080;
}
}
nginx が待ち受けるポートを SSL 標準の 443 に変更し、 ssl を追記します。また、 SSL 証明書とキーファイルの場所を指定します。
続いて、 nginx (とopenSSL)を起動する docker-compose.yml を編集します。nginx が起動したままであれば、忘れずにdocker-compose down で終了しましょう。
version: "3"
services:
nginx:
image: nginx
container_name: nginx
restart: always
ports:
- "8181:443"
environment:
NGINX_SERVER_NAME: raspberrypi.local
volumes:
- /home/pi/rproxy/conf:/etc/nginx/conf.d/
- /home/pi/rproxy/log:/var/log/nginx
- /home/pi/rproxy/ssl:/etc/ssl/certs/nginx
ports を、8181:443 として SSL を待ち受けるようにしています。443:443 としないのは、SoftEther VPN との重複を避けてのことです。
Nginx が Docker コンテナで起動したら、 https://raspberrypi.local/apps/dashboard にアクセスしてみます。すると、
上図のような警告が表示されます(Chrome の場合)。これは、SSL 通信は行おうとしているものの、証明書が信頼できる認証局で作成されたものではないため、通信内容が危険であるという意味です。
普段であれば、即座にブラウザバック推奨ですが、自分で作成してインストールした証明書なのでアクセスを継続したいですよね? ということで、「詳細設定」をクリックします。
ここでは、自分のサーバーであることが(ほぼ)分かっているので、詳細設定をクリック後に出てくる「raspberrypi.local にアクセスする」リンクをクリックします。これで無事に、曲がりなりにも SSL 経由で Nextcloud でアクセスできます。
Windows に証明書をインストールしよう
さて、ようやく WebDAV アクセスが解禁……というとそうでもありません。この状態で WebDAV アクセスしても、同じ証明書を利用しているのでエラーが出ます。
しかも、上記は別の WebDAVクライアント(cyberduck)によるもので、Windows 標準のエクスプローラーによるマウントでは、このようなエラーもなく接続失敗となってしまいます。
仕方ないので、証明書を手動でインストールします。
先ほど、 SSL 接続のテストに使ったブラウザのタブに戻り(ここでは Chrome を使います)
「保護されてないない通信」をクリックして、「証明書が無効です」の右隣にある、ポップアップっぽいアイコンをクリックします(上図参照)。
証明書ビューワーが起動するので、「詳細」タブに切り替え、一番下にある「選択した証明書をエクスポート (X)…」ボタンをクリックします。
ファイルの保存ダイアログが表示されるので、どこか適当なところに保存します。
保存したファイル(rasperrypi.download.crt)をダブルクリックすると、以下のように証明書が開きます。
有効期間や発行先などもここで確認できます。ここでは、 Windows にこの証明書を信用してもらうために、「証明書のインストール」ボタンをクリックします。
証明書のインポートウィザードが起動するので、「保存場所」を選んで「次へ」ボタンで進めます。保存場所は、通常のアプリケーションと同様で、個人で使っている場合はあまり意識しないでも大丈夫です。共用 PC で、他の人にもアクセスさせたい場合は「ローカルコンピューター」。逆に個人専用であれば「現在のユーザー」にするといいでしょう。
続いて証明書ストアを選択する画面となりますが、証明書の種類は問題ないはずなので「自動的に証明書ストアを選択する」を選び、次へをクリックします。
確認画面が表示されるので、「完了」で終了します。
エクスプローラーから、 Nextcloud の WebDAV をマウントする
やっとここまでたどりつけました! まずは、エクスプローラーから「PC」を探して右クリックします。
メニューの中から「ネットワーク ドライブの割り当て」を探してクリックします。 Windows 11 以降で見つからない場合は、「その他のオプションを表示」をクリックして全メニューを表示して、その中から探してクリックします。
- フォルダーは、「https://ドメイン:ポート番号」まではこの記事で設定した SSL の設定、/以降は、 Nextcloud のファイルアプリ右下にある「設定」の中のWebDAV のURLとなります。通常、https://ドメイン:ポート番号/remote.php/dav/files/ユーザー名/ となります。
- 通常は再接続したいので、「サインイン時に再接続する」にチェックを入れます。
- Windows のログインユーザーと、当然異なるので「別の資格情報を使用して接続する」にチェックを入れます(必須)。
- 「完了」ボタンをクリックします。
ログインダイアログが表示されるので、 Nextcloud のユーザー名、パスワードを入力し、「資格情報を記憶する」にチェックを入れて OK ボタンをクリックして接続します。
無事マウントされると、上図のように公式クライアントとは異なった形式で Nextcloud が表示されます。こちらは、ネットワークに直接保存されるので、ネットワークから切断された場合は一切アクセスができなくなります。
確認事項
Windows の エクスプローラーからマウント出来ない場合、
- URL が SSL のものではない
- SSL 証明書がインストールされていない
- ログイン情報が間違っている
といった原因が考えられます。また、そもそも WebDAV にリバースプロキシが動いているかの確認は、 Cyberduck などのhttpでもWebDAV アクセスができるクライアントを使用するとチェックしやすいです。
終わりに
Windows のレジストリをいじった方が楽ですね。
このやり方の問題点としては、 SSL を使わなくても Nextcloud にアクセスできるので、そもそもセキュリティ上のメリットはほとんどないです(それでも、外部公開の場合には暗号化されるというメリットはありますが)。そして、証明書を手動でインストールしないといけないので、自宅内や VPN 経由に限定するのであれば、 Windows のレジストリを改変して HTTP 通信でもネットワークドライブをマウントできるようにした方が簡便です。社内ネットワークで多数の PC をセットアップしなければならない場合は、なおさらでしょう。
Let’s Encrypt などを利用して「きちんとした」証明書を発行すれば、上記の問題はほぼ解決します……が、今度はホスト名の問題が発生します。常に外部からのアクセスであれば公的な DNS に任せておけるのでいいですが、内部からのアクセスとなると、 hosts ファイルなどで対応が必要になります。そして、外部 / 内部からのアクセス両方となると、 hosts ファイルのスイッチや ローカル DNS などの工夫が必要となり、面倒です。
ただ、平文の通信でのネットワークドライブのマウントの許可は、 Windows のセキュリティを低下させることは間違いないので、面倒を承知で(後は勉強のために)ローカル認証局で SSL を発行してみました。
ともあれこれで、 Nextcloud の利点の一つの 「WebDAV でネットワークドライブが使える」が達成できました。バッチ処理や RPA で大量の一時ファイルを書き込まないと行けないときや、複数台に RPA で使うスクリーンショットを展開するときにパスを統一できるなど、意外に便利に使用できます。
個人~十数人程度までで利用するファイルサーバーとしては、なかなかいいものが仕上がったのではないかなと思います。
楽介でした。