Nextcloudでプライベートなオンラインストレージを構築する
環境構築記録です。今回は2019年夏に構築したものの式年遷宮です(もう5年以上も使っているのか……)。……え、Dockerで入れれば一発?まあそう言いなさんなって。
Preamble
Nextcloudはオープンソースのオンラインストレージソフトウェアです。これをサーバにインストールして走らせると、DropboxやBoxなどと似たオンラインストレージとして利用できます。今回はその環境を構築していくというわけです。
インストールするのはLinuxのマシンならなんでもよいですが、今回はRaspberry Piを使います。モデルはRaspberry Pi 4 Model Bのメモリ4GBを用意しました。また、ディストリビューションはRaspberry Piなら普通はRaspberry Pi OSを使うところですが、筆者はほかの環境ではArch Linuxを使っているので、管理コスト低減のためにここでもArch Linux ARMを使うことにしました。Raspberry PiにArch Linux ARMをLVMでインストールするをご覧ください。他のディストリビューションでもパッケージマネージャ周りを読み替えればだいたい動くと思います。
準備するものたち
- Raspberry Pi:言わずと知れたシングルボードコンピュータです。
- microSDカード:それはそう。
- ストレージ:Nextcloud上に保存するデータを物理的に格納する場所です。マシンがRaspberry Piなので、USB Type-Aで接続する外付けHDDを用意しました(もちろんSSDでも可)。
- ドメイン:外部からアクセスしたりHTTPSの証明書を取得するために必要となります(逆に言えば
/etc/hosts
と自己署名証明書で良ければ不要かも)。 - グローバルIPアドレス:外部からアクセスするために必要です。NA(P)Tやファイアウォールがある場合はTCPの80番と443番ポートを開放してRaspberry Piに向けてください(HTTP/3を使うならUDPの443番も)。また、利用するドメインのAレコード(またはAAAAレコード)をこのIPアドレスに向けてください。固定IPアドレスが望ましいですが、動的IPアドレスでもDDNSを使えばなんとかなると思います。
全体構成
全体構成は次のようになります。
- nginx:Webサーバです。HTTPリクエストをここで受けてPHP (php-fpm) に渡す、いわゆるリバースプロキシの構成です。
- certbot (Let's Encrypt):ご存じHTTPS証明書を取得・更新するやつです。
- PHP:NextcloudはPHPアプリケーションです。PHPインタプリタ本体はWebサーバ(TCPポートをバインドしてHTTPプロトコルの通信を直接受ける機能)を持たないので、PHPをApache httpdのモジュールとして動かす構成(昔はこれが圧倒的)や、php-fpmと呼ばれるPHPのモジュールでFastCGIサーバとして動かす構成などを取ります。今回はというと、さすがにApache httpdのモジュールとして動かす気はまったく起きないので、php-fpmでセットアップします。
- MariaDB:データベースです。NextcloudはPostgreSQLもサポートしていますが、MySQL / MariaDBの方が推奨なのだそうです。
- Nextcloud:Nextcloudの本体です。PHPスクリプトです。
/opt/nextcloud
に配置します。 - データフォルダ:Nextcloudのデータが置かれるディレクトリで、
/var/lib/nextcloud/data
とします。筆者は外付けストレージをここにマウントしました。
ユーザの作成
外部からのHTTPアクセスを受けるプログラムはrootで実行するべきではありません。Arch LinuxではApache httpdやnginxなどのHTTPサーバ用に http
ユーザが存在しますが、ここではさらにそれとは別のユーザを作成しておきます(ユーザーとグループ - ArchWiki # システムユーザーを追加する例)。
$ sudo useradd --system -s /usr/bin/nologin -u 1999 -U nextcloud
PHP
ではここから一つずつ作っていきます。まずPHPです。
ビルド
パッケージでインストールすればいいという説もありますが、いろいろとモジュールが必要なので、PHPを自分でビルドします(Arch Linuxのnextcloudパッケージで入れればモジュールを含めてパッケージの依存関係で処理してくれるって?まあまあ……)。マニュアルはPHP: Unix および macOS システムでのソースコードからのインストール - Manualにありますが、基本的には PHP: Downloads からソースコードをtar.gzで落としてきて ./configure
からの make
というよくある流れです。
なお、筆者は旧環境からの移行の関係でPHP 8.1を使いますが、特に事情がなければ最新のPHPを入れればよいです。
$ mkdir ~/php
$ cd ~/php
$ curl -O https://www.php.net/distributions/php-8.1.31.tar.gz
$ tar -xf php-8.1.31.tar.gz
$ cd php-8.1.31
$ ./configure --enable-fpm --with-fpm-user=http --with-fpm-group=http --with-fpm-systemd --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-curl --enable-gd --with-avif --with-jpeg --with-webp --with-freetype --enable-mbstring --with-openssl --with-zip --with-zlib --with-pdo-mysql --with-mysql-sock=/run/mysqld/mysqld.sock --with-bz2 --enable-intl --with-sodium --enable-bcmath --with-gmp --enable-exif --enable-pcntl --enable-sysvsem
ここで、Nextcloudの要求するモジュールがドキュメントのPHP Modules & Configuration — Nextcloud latest Administration Manual latest documentationに載っているので、これに従ってconfigureのオプションに書き並べています(どうでもよいですが、モジュールによってconfigureのオプション名が違って、デフォルトで有効になっているモジュールもあるので、調べるのがクソ面倒です。正攻法はPHP: 拡張モジュールの一覧/分類 - Manualから各モジュールの「インストール手順」を辿ることで、抜け道は ./configure --help
を目grepすることです)。optionalなモジュールは気分で入れているので、必要に応じて取捨選択してください。さらに sysvsem
はドキュメント化されていませんが、Nextcloudインストール後の設定診断で推奨モジュールだと言われます。
fpmのユーザとグループはさっき作った nextcloud
ではなく http
を指定していますが、Nextcloud専用のPHPではないという気持ちでこうしておきます。
さて、上の通りに configure
するといろんなパッケージが足りないと言われるので、適当に pacman -S
します(参考までに今回は libpng libavif freetype2 oniguruma libsodium libzip
でした)。それが済んだらいよいよ make
です。
$ make -j4
$ make TEST_PHP_ARGS=-j4 test
$ sudo make install
$ php -v
私の環境ではmakeには30分ほどかかりました。気長に待ちましょう。
PECL拡張機能のインストール
一部のモジュールはPECL拡張機能なので、別途インストールします。昔は pecl install
コマンドでインストールできたようなのですが、PHP7.4で
PEAR (PECL を含む) のインストールは、デフォルトで無効になりました。configure オプション --with-pear を使うことで明示的に有効にできますが、このオプションは推奨されなくなり、将来的に削除される可能性があります。
ということなのだそうです(PHP: その他の変更 - PHP 7.3.x から PHP 7.4.x への移行)。で、どうするかというと、書かれている通りPHPのビルドオプションでPEARを入れてもいいのですが、非推奨だしあまり気が乗らないので、PHP: phpize で共有 PECL 拡張モジュールをコンパイルする方法 - Manualでいきます。
対象は apcu
と imagick
です。当たり前ですが imagick
はpacmanで imagemagick
パッケージのインストールが必要です。あと、これもドキュメント化されていないものの、Nextcloudインストール後にImageMagickはSVGサポート付きが推奨だと言われるのですが、それには librsvg
が必要です。
インストールしたextensionは php.ini
で読み込む必要がありますが、それは次でやります。
PHPの設定
一通り出来上がった後に書いているので天下りというか先回りなところがありますがご愛嬌。
php.ini
まず php.ini
の場所ですが、上でしれっと --with-config-file-path=/etc
と設定していたので /etc/php.ini
になります。
extension = apcu.so
extension = imagick.so
zend_extension = /usr/local/lib/php/extensions/no-debug-non-zts-20210902/opcache.so
memory_limit = 512M
apc.enable_cli = 1
最初はさっきのextensionの読み込みです。次の opcache
extensionはPHP: インストール手順 - OPcacheに書いてある通りです。それから memory_limit
はNextcloudのドキュメントに書いてあります。 apc.enable_cli
はNextcloudのCLIコマンドを実行するのに必要で、APCuモジュールはデフォルトではCLIだと無効という設定になっている(PHP: 実行時設定 - インストール/設定 - APCu)ため自分で書いてあげる必要があります(という旨はこれもドキュメントに明示的には書かれていなさそう……)。
また、 pdo_mysql.default_socket
についても確認してください (php -i | grep pdo_mysql.default_socket
)。これはPHPがMariaDBにアクセスするときに見に行く場所です。PHPのビルド時(configure実行時)にMariaDBが起動していてソケットが存在していれば、そこをデフォルトにしてビルドされるので、 php.ini
で書く必要はありません。あるいはconfigure optionで指定することも可能です(上のコマンドではしれっと /run/mysqld/mysqld.sock
の指定を含めておきました)。設定方法はなんでもよいし場所もどこでもよいのですが、(据わりのよい場所にするのと)MariaDBの設定と揃えることが必要です。
php-fpm.conf
次に php-fpm.conf
です。場所は php.ini
と同じ /etc/php-fpm.conf
が無難で、 /usr/local/etc/php-fpm.conf.default
からコピーして作成すると良いです。リファレンスはPHP: 設定 - FastCGI Process Manager (FPM)です。次に出てくる php-fpm.d
を読み込むため
include = /etc/php-conf.d/*.conf
を書いておきます。他に必須で設定しなければいけない項目はないので、お好みで。筆者は
pid = /run/php-fpm.pid
と
error_log = /var/log/php-fpm.log
を設定しました。
php-fpm.d
そしてプールの設定ファイルが必要で、こちらの方が重要です。こちらも /usr/local/etc/php-fpm.d/www.conf.default
からコピーして作成します。場所はさっき設定した /etc/php-fpm.d
で、筆者は nextcloud.conf
という名前にしました。さて内容ですが、まず
user = nextcloud
group = nextcloud
です。見ての通りphp-fpmでスクリプトを実行する際のユーザです。次に
listen = /run/php-fpm/nextcloud.sock
です。nginxとPHPの間はTCPかUNIXドメインソケットで通信するようになっていますが、ここではソケットファイルの場所を設定してUNIXドメインソケットを使うよう指示しています。nginx側の設定でも同じソケットファイルを指定してあげれば繋がるという次第です。続いて
listen.owner = http
listen.group = http
listen.mode = 0660
です。こちらのユーザとグループは、nginxが通信できるようにnginxのワーカプロセスのものにしておきます。
それから pm.
で始まるプロセスマネージャ関連の設定について、Server tuning — Nextcloud latest Administration Manual latest documentation # Tune PHP-FPMでよさげな設定値を決めてあげるといいようです。
どんどんいきます。
access.log = /var/log/nextcloud/access.log
場所はお好みで(必要に応じて mkdir
しておくこと)。
chdir = /opt/nextcloud
ワーキングディレクトリの設定です。後でNextcloudのスクリプトを置く予定の場所を入れておきます。
env[PATH] = /usr/local/sbin:/usr/local/bin:/usr/bin
PATHは適当に(ここではArch Linuxの /etc/profile
を眺めてそれっぽい値を入れました)。PHPをインストールした /usr/local
系のディレクトリは含めておいた方がいいでしょう、知らんけど。
php_admin_value[error_log] = /var/log/nextcloud/error.log
php_admin_value[memory_limit] = 512M
php_value[session.save_path] = /var/lib/nextcloud/session
エラーログの場所はお好みですがアクセスログと足並みを揃えておくといいでしょう。 memory_limit
は php.ini
と同じです。 session.save_path
はなんだっけ、php-fpmのワーカプロセスがセッションのデータを読み書きするので nextcloud
ユーザの権限がついている場所である必要があって、なのでデフォルトから変更した、ということだったと思います(指定したディレクトリは必要に応じて mkdir
しておくこと)。
php-fpmのsystemdサービス
PHPをビルドしたディレクトリの下の sapi/fpm/php-fpm.service
がサービス定義ファイルです。 /usr/local/lib/systemd/system
にインストールしてくれればいいんですが、何故かそうなってません。まあ、どちらにせよコピーして /etc/systemd/system
に置きます。内容は PIDFile
の場所を php-fpm.conf
の設定と合わせることと、 ExecStart
のオプション --fpm-config
の指定を php-fpm.conf
の場所にすることくらいです。
PIDFile=/run/php-fpm.pid
ExecStart=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /etc/php-fpm.conf
ExecStartPre=/bin/mkdir -p /run/php-fpm
nginxとcertbot
最近はこの手のリバースプロキシもいろいろ選択肢が出てきており、HTTPSを終端してHTTPでバックエンドに投げるくらいの目的であればnginx以外のソリューションも真面目に視野に入ってきます。が、今回はphp-fpmを動かそうとしているのでFastCGIが使えないといけなくて選択肢が減るのと、Nextcloudのドキュメントでnginxのサンプルconfigurationが提供されているので、とりあえずnginxでいきます(他の候補としてはCaddyが有力なんですかね……)。インストールは sudo pacman -S nginx
です。
nginxの設定
それでnginxの設定ファイルですが、NGINX configuration — Nextcloud latest Administration Manual latest documentationから取ってきて適当に改造します。
パスの調整
まず、筆者は https://nextcloud.example/nextcloud
のようなパスを切ってそこからNextcloudにアクセスしたいです。ところがここで提供されているconfigurationでは、そういう場合Nextcloudの本体を /var/www/nextcloud
とかに置くことが前提になっています。今回は後述のように本体を /opt/nextcloud
に置こうと思うので、そこを調整します。
元の設定ファイルで関係する部分を抜粋すると以下のようになります。
server {
root /var/www;
location ^~ /nextcloud {
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_pass php-handler;
}
location /nextcloud {
try_files $uri $uri/ /nextcloud/index.php$request_uri;
}
}
}
最後の location
ディレクティブで(静的ファイル以外への)あらゆるリクエストを index.php
に内部リダイレクトして、それを今度は手前の location
ディレクティブで拾ってFastCGIに渡してやるという寸法です。それはいいのですが server
ディレクティブ直下で root /var/www;
としているのが気に食わないです。ルートパスの配下をどうするのか指図されたくありません。そこで外側の location
ディレクティブの下に alias
ディレクティブを書く形に変えてやります。
server {
location ^~ /nextcloud {
alias /opt/nextcloud;
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^/nextcloud(/.+?\.php)(/.*)?$;
set $path_info $fastcgi_path_info;
try_files /nextcloud$fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME /nextcloud$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_pass nextcloud;
}
location /nextcloud {
try_files $uri $uri/ /nextcloud/nextcloud/index.php$request_uri;
}
}
}
ここで、 root
ディレクティブはURLパスのルートを root
ディレクティブで指定したOS上の実パスに対応付けます。すなわち root /var/www;
とすると、 /index.html
へのリクエストは /var/www/index.html
に、 /nextcloud/login
へのリクエストは /var/www/nextcloud/login
に対応付けられ、それは location
ディレクティブ下に記述しても同じです。一方 alias
ディレクティブは location
コンテキストで記述する必要があり、その location
のURLパスを alias
ディレクティブで指定したOS上の実パスに対応付けます。具体的には、今回でいうと location ^~ /nextcloud
ディレクティブのコンテキストで alias /opt/nextcloud;
と書いたので、URLパス /nextcloud
が /opt/nextcloud
に対応するという意味になり、 /nextcloud/login
へのリクエストは /opt/nextcloud/login
に対応付けられます。 root /opt;
なんておっかなくて書けませんので、こういう形で設定をすることになります。なお、このあたりのドキュメントはModule ngx_http_core_moduleにありますが、記述が薄味すぎて正直これ単体で物事が解決したことがありません……。
さて、この変更に合わせて弄らなければいけない点が2つあります。まず1つは単純ではあるのですがふざけた話で、最後の部分で index.php
に内部リダイレクトするために try_files
を使っていますが、nginxには try_files
と alias
を組み合わせたときの挙動にバグがあり、本来は /nextcloud/index.php
と指定すればいいはずのところ、 alias
での対応付け元となるlocationパス /nextcloud
の部分が消失して /index.php
というURLパスに内部リダイレクトされてしまいます (#97 (try_files and alias problems) – nginx)。意味が分かりません。このバグが最初に報告されたのが2012年だというのでなおさら理解に苦しみます。workaroundは単純に消失してしまうことを見越して /nextcloud/nextcloud/index.php
と親パスの部分を二重にした形で書いておくことです。
もう1つはFastCGIでPHPにリクエストを渡す部分です。
CGIとFastCGIあれこれ
歴史を語れば、FastCGIの源流にはCGIという技術があります (RFC 3875)。こいつは20年くらいまえに隆盛を極めたブツで、Apache httpdのようなWebサーバが裏にいるスクリプト(CGIスクリプト)にリクエストを渡し、そのスクリプトからレスポンスを返すことで動的なWebページを作成できる、というものでした。今だと動的なWebページとか当たり前すぎて何言ってんだという感じですね。CGIの時代にはリクエストが来たときにその都度スクリプトのプロセスが起動される(1リクエスト1プロセス)という仕組みになっていて、プロセスが起動されるときに環境変数でリクエストのメタ情報(パスやクエリ文字列の情報)が渡されるようになっていました。たとえば、 /cgi-bin/forum.cgi/topic/example?comment=42
というリクエストが来ると、 /cgi-bin/forum.cgi
が実行され、 PATH_INFO
環境変数に /topic/example
が、 QUERY_STRING
環境変数に comment=42
が入っている、という具合です。
1リクエスト1プロセスはさすがに効率が悪いので、それを改良したのがFastCGIです。FastCGIではバックエンドのプログラムは常時起動していて、TCPやUNIXドメインソケットなどでWebサーバからリクエストを受け取ります。一方でCGIの延長線上にある技術なので、CGIにおいて環境変数で受け渡していたものに相当するデータをやり取りすることができます。
今回重要な役割を果たすのは SCRIPT_FILENAME
と SCRIPT_NAME
の2つの変数です。FastCGIでは1個のプロセスが1個のリクエストに対応しないので、プロセスの実行ファイル(たとえばPHPのバイナリ)とは別に各リクエストに対応するスクリプトファイル(たとえば .php
ファイル)の情報をリクエストごとに引き渡す必要があり、それに SCRIPT_FILENAME
変数が使われます。具体的には、PHPの場合はPHPファイルのOS上の実パス(たとえば /opt/nextcloud/index.php
)を入れます。これを正しく指定しないとPHP処理系が実行すべきPHPファイルを特定できないのでまったく動きません。もう1つの SCRIPT_NAME
変数は SCRIPT_FILENAME
に対応するURL上のパス(たとえば /nextcloud/index.php
)になります。これはCGI時代からあるもので、スクリプト上でサイト内リンクを生成する場合に SCRIPT_NAME
で渡されるURLパスの情報を使って正しい階層のサイト内リンクを作る、といった使い方をされます。
イマドキのWebアプリのバックエンドではこういうCGIの変数はほぼ忘れ去られており、特にJavaScriptで実装されたバックエンドでは一切登場しません。一方でこの系譜を引き継いでいるケースもあり、たとえばRailsをはじめとしたRuby製Webアプリケーションサーバのリンガ・フランカとなっているRack SpecificationはCGI変数が基礎になっています。PHPもその例に漏れず、PHPの世界では $_SERVER
連想配列にCGI変数に相当する情報が格納された状態でスクリプトが起動されるというのが大前提になっています(PHP: $_SERVER - 定義済み変数)。これはPHPがCGIやFastCGIで実行されている場合に限らず、Apache httpdのモジュールとしてPHPが実行されている場合も同じです。そんなわけで、nginxからNextcloudにリクエストを渡す際にはCGI変数のことを気にかけてあげないといけないのです。
さて、ここでnginxの fastcgi_split_path_info
ディレクティブについて説明しないといけません。かつてのCGIの流儀では、上の例にもあるように /cgi-bin/forum.cgi/topic/example
といった形式のURLパスでリクエストをしていました。WebサーバはここからCGIスクリプトファイルのパスに一致する部分を切り出し、余った部分を PATH_INFO
環境変数に格納する、という処理をしていました。nginxでこの流儀に沿った処理を実現するために用意されているのが fastcgi_split_path_info
です。このディレクティブには2つのキャプチャグループ(括弧)を持つ正規表現を引数に渡します。nginxがリクエストを処理する際には、リクエストのURLパスに対してこの正規表現をマッチさせ、1個目のキャプチャグループの結果をnginxの $fastcgi_script_name
変数に、2個目のキャプチャグループの結果を $fastcgi_path_info
変数に格納します。つまり先ほどの例では /cgi-bin/forum.cgi
の部分が1個目のキャプチャグループにマッチし、 /topic/example
の部分が2個目のキャプチャグループにマッチするような正規表現を fastcgi_split_path_info
ディレクティブに書いてやればいいことになります。 fastcgi_split_path_info
はnginxの変数に値を入れるだけですが、 fastcgi_param
ディレクティブを使ってこれらの変数を実際にFastCGIに渡してあげれば完成です。実はnginxの設定ディレクトリには /etc/nginx/fastcgi_params
というファイルが配置されており、このファイルを include
することで fastcgi_param
ディレクティブをいい感じに呼んでくれます。
もとのNextcloud公式の設定ファイルでは root /var/www;
でしたから、この /var/www
にURL上のパス /nextcloud/index.php
を連結してやればOS上の実パス /var/www/nextcloud/index.php
が求まります。なので fastcgi_split_path_info
で /nextcloud/index.php
の部分を $fastcgi_script_name
として切り出してやって SCRIPT_NAME
に使い、さらに /var/www
を前に連結したものを SCRIPT_FILENAME
に指定してあげればOKでした。 /var/www
は root
ディレクティブで指定した値ということでnginxの $document_root
変数に入っていますから、 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
で実現できます。
今回Nextcloudを /opt/nextcloud
に配置したので、URLパスが /nextcloud/index.php
のリクエストに対しては、 /opt/nextcloud
の下の /index.php
を呼び出さなければなりません。これが SCRIPT_FILENAME
になります。一方 SCRIPT_NAME
はURLパスの話なので /nextcloud/index.php
を指定してあげる必要があります。そこで fastcgi_split_path_info
では $fastcgi_script_name
として一旦 /nextcloud
の部分を除いた /index.php
がキャプチャされるようにしてやり、これを /opt/nextcloud
( alias
ディレクティブで指定したので $document_root
にはこれが入っている)と連結して fastcgi_param SCRIPT_FILENAME $document_root$fastcgit_script_name;
とします(ここだけ見ると結果としての見た目はたまたま変わらずです)。そしてこのままでは SCRIPT_NAME
が壊れてしまうので、 fastcgi_param SCRIPT_NAME /nextcloud$fastcgi_script_name;
と明示的に指定してあげることでこちらも正しい値が入るようにします。これで設定が完成です。
……と、nginxを正しく動かすには非常に長い説明がいるのですが、私もCGI変数まわりの話を忘却していたので、先の try_files
と alias
のバグの件も含めてたいへん試行錯誤しました。余談ですが、上の設定ファイルで $fastcgi_path_info
を一度 $path_info
変数に待避しているのは、 try_files
ディレクティブを挟むと $fastcgi_path_info
の値が消失するという“仕様” (#321 (try_files & $fastcgi_path_info) – nginx)があるためです。前々からApache httpdの設定ファイルは書きたくないと思っていましたが、今回nginxの設定ファイルも同じくらい書きたくない気持ちになりました。
nginxの設定におけるその他のポイント
nginxの設定に関して他のポイントを2個ほど書いておきます。
upstream nextcloud {
server unix:/run/php-fpm/nextcloud.sock
}
ここではphp-fpmの設定で指定したUNIXドメインソケットを指定します。upstream名は任意ですがここで命名したものを fastcgi_pass
ディレクティブで使います。
server {
listen 80;
listen [::]:80;
server_name nextcloud.example;
location / {
return 301 https://$host$request_uri;
}
location ^~ /.well-known/acme-challenge/ {
allow all;
root /var/lib/certbot/nextcloud;
default_type "text/plain";
}
}
/.well-known/acme-challenge
へのリクエストをcertbotが拾えるようにします。Certbot - ArchWiki # Webrootとかも見てください。
あとはお好みで。
certbot
インストールは sudo pacman -S certbot
です。あとは
$ sudo certbot certonly --webroot -w /var/lib/certbot/nextcloud -d nextcloud.example
で。もちろん -w
で渡すディレクトリはnginxで設定したものと合わせます。それから $ sudo systemctl enable certbot-renew.timer
しておけば自動更新もしてくれるのですが、そのままだと自動更新が走った後にnginxをリロードしないと反映されなくて哀しいです。 $ sudo systemctl edit certbot-renew.service
で
[Service]
ExecStart=
ExecStart=/usr/bin/certbot renew -q --post-hook "systemctl reload nginx"
とか書いてあげればいいはずです。
MariaDB
インストールしてユーザ周りなど基本的なセットアップをするだけです。ぶっちゃけデータベースのユーザ管理は苦手分野です。
インストールは sudo pacman -S mariadb
でOKです。まず /etc/my.cnf.d/server.cnf
の [server]
オプショングループに
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
を書き、 /etc/my.cnf.d/client.cnf
の [client]
オプショングループに
default-character-set = utf8mb4
を書きます。ちなみにMariaDB 11.8以上では utf8
文字セットが utf8mb4
の別名になるそうです (Unicode - MariaDB Knowledge Base)。時代を感じます。実はArch Linuxではかなり前から character-set-server = utf8mb4
と collation-server = utf8mb4_unicode_ci
がビルド時に設定済みのようですが。
それから /etc/my.cnf
の [client-server]
オプショングループに
socket = /run/mysqld/mysqld.sock
を書きます(これもArch Linuxではビルド時に設定済みなんですが)。
他の設定として、Nextcloudのドキュメントでは transaction-isolation = READ-COMMITTED
と binlog-format = ROW
を書けということになっています(書くとしたら [server]
に)。グローバルにDBの挙動を変えるのでちょっと躊躇いますが……
設定ファイルの整備が済んだら、MariaDB - ArchWikiとDatabase configuration — Nextcloud latest Administration Manual latest documentationを眺めつつコマンドを叩くだけです。
$ sudo mariadb-install-db --user=mysql --basedir=/usr --da
tadir=/var/lib/mysql
$ sudo systemctl enable mariadb
$ sudo systemctl start mariadb
$ sudo mariadb-secure-installation
$ sudo mariadb
MariaDB [(none)]> CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY '...';
MariaDB [(none)]> CREATE DATABASE nextcloud;
MariaDB [(none)]> GRANT ALL PRIVILEGES on nextcloud.* to 'nextcloud'@'localhost';
MariaDB [(none)]> quit;
テーブル定義とかはNextcloudの初期設定中に勝手にやってくれます。
Nextcloud
本体の配置
Nextcloud本体はtarballを解凍してchownするだけです (Installing from command line — Nextcloud latest Administration Manual latest documentation)。場所ですが、Filesystem Hierarchy Standard (FHS) に則るとこういうソフトウェアは /opt
に置くのが正解です(決して /usr/local
配下ではない。PHPがwwwドキュメントの延長線上だという解釈を取れば /var
配下と思えないことはないが、現代的なPHPアプリケーションではちょっとstraightforwardな解釈じゃない気がする)。
Nextcloudの最新版はDownload and install Nextcloudからダウンロードできます。古いバージョンはNextcloud server changelogにあります。
$ curl -O https://download.nextcloud.com/server/releases/nextcloud-31.0.0.tar.bz2
$ sudo tar -xf nextcloud-31.0.0.tar.bz2 -C /opt
$ sudo chown -R nextcloud:http /opt/nextcloud
ユーザは nextcloud
ユーザですが、グループは http
グループにしておきます。nginxのワーカプロセスが静的ファイルを読み取る必要があるためです。このあたりの所有者やパーミッションの設定はアップデート時に壊れがち(壊しがち?)なので適当にだましだまし直して使っています。
データフォルダ
Nextcloudのデータフォルダ(ユーザがアップロードするファイルが置かれるフォルダ)はNextcloudのPHPスクリプト本体が置かれている場所とは別の場所にすることができます。今回は /var/lib/nextcloud/data
とし、このディレクトリに外付けストレージをマウントすることでユーザのファイルを外付けストレージ上に保存するようにします。データフォルダの場所はNextcloudの config.php
で指定するようになっており、次に出てくる初期設定ウィザードに含まれているので、あらかじめマウントを済ませておきます。もちろん所有者とグループは nextcloud:nextcloud
にして、パーミッションも0750か0700あたりにしておくといいでしょう。
初期設定とチューニング
この状態で既にHTTP(S)アクセスできるようになっているはずです。Nextcloudの設定を何もしていない状態でHTTPアクセスすると初期設定ウィザードが開いてデータフォルダの場所やデータベースの認証情報を聞かれるので、そのままブラウザから入力していけば使い始められます( config.php
やデータベースに書き込まれる)。もしくは occ maintenance:install
コマンドを叩いてもいいです(上にも貼ったcommand lineインストールのドキュメントに書いてあります)し、手で config.php
を編集してもいいです。
管理者ユーザを作成して管理者設定画面を開くと、セキュリティやパフォーマンス関連の診断結果が表示されます(賢い)。不備のある部分があれば適当に修正していきましょう。一例として筆者はPHPの opcache.interned_strings_buffer
の設定値を大きくしろと言われたので php.ini
に書き足しました。
振り返り
それぞれのパーツがどこで繋がっていたか振り返っておきます。
- nginxとcertbot:certbotの認証に使われる
/.well-known/acme-challenge/
へのHTTPアクセスをnginxの設定で然るべきディレクトリに向ける。 - nginxとphp-fpm:それぞれの設定で同じUNIXドメインソケットを指定する。
- nginxとNextcloud:nginxからphp-fpmに渡すFastCGI変数でNextcloudのPHPファイルが指定されるよう設定する。
- php-fpmとMariaDB:それぞれの設定で同じUNIXドメインソケットを指定する。
- NextcloudとMariaDB:Nextcloudの設定にMariaDBのユーザ名・パスワードを書く。
- Nextcloudとストレージ:Nextcloudの設定でデータフォルダを指定し、そこにストレージをマウントする。
結構複雑ですね。頭の中に相関図でも描きながら読んでください。
Samba
おまけのセクションです。Nextcloudと本質的な関係はほとんどありません。筆者はクライアントにあたるWindowsマシンでNextcloudの同期を設定するにあたって、所要時間やストレージの関係から一部のフォルダを除外しておこうと考えました。しかし除外したフォルダにアクセスしたいこともあります。それにはNextcloudのデータが入っているフォルダをSMBでLAN内に公開すればよく、Sambaを入れて少し設定を書けば実現できます。ぶっちゃけSMBってしょっちゅう脆弱性が見つかっている気がしてならないですが、SMBバージョン3系はそこまでではないはず……?
注:SMBをインターネットに公開しないよう注意してください。最低限でもNA(P)Tやファイアウォールの配下で利用することをおすすめします。
むかし書いたSambaの設定ファイルがあるのですが、何を参考にして作ったのかまったく思い出せません。といってもデフォルトが割とよくできているので、基本的に共有を1つ作って(= smb.conf
にセクションを1つ作って) path
パラメータでWindows側に公開したいディレクトリを指定してあげるだけでそんなに問題はない気がします。Nextcloudの仕様でユーザのファイルはデータディレクトリの下の <username>/files
以下に格納されているので、筆者は /var/lib/nextcloud/share
にシンボリックリンクを貼りました。あと細かい部分はsmb.conf(5)の極めて長大なmanページを眺めて調整すればいいはずです。今回は valid user = nextcloud
と writeable = yes
あたりは書いておかないとまずいでしょうか。本当はLinuxユーザをSamba用に用意したいところですが、Nextcloudのデータディレクトリに違うユーザが所有者のファイルができても困るのでそういうわけにはいかなさそうです。まとめると最低限は以下で、後はお好みです。
[Share]
path = /var/lib/nextcloud/share
valid users = nextcloud
writeable = yes
で、 nextcloud
ユーザに対応するSamba用の認証情報を手で作成する必要があります。
$ sudo pdbedit -a -u nextcloud
そしてサービスを上げましょう。
$ sudo systemctl enable --now smb
これであとはWindowsのエクスプローラーから \\192.0.2.2\Share
のようにアクセスすればSMBで見れると思います。サーバ自身のファイアウォールを有効化している場合はTCPの139と445を開ければいいはずです。筆者の手元ではsystemd-resolvedでmDNSとかLLMNRが有効になっていたので、よくわかりませんがこの時点で名前でもアクセスできました(mDNSはUDPの5353番、LLMNRはTCPとUDPの5355番ポートが必要かな)。あと、ルータがDHCPのオプション12でクライアントから受け取ったホスト名を覚えていてDNSで聞かれたときに答えてくれるという動きもしているようです。
あとはネットワーク探索を有効にすればWindowsエクスプローラーの「ネットワーク」のところに出てきます。これは nmb
を上げればいい……というのは古くなりつつある話で、イマドキのWindowsはNetBIOSじゃなくてWSDというのを使ってローカルネットワーク上のマシンを探すらしいです。Linuxでは wsdd
を入れればWSDを喋れて、これはインストールしてサービスを上げるだけです(必要なポートは3702/udpと5357/tcp)。
今回あらためてSambaのドキュメントをつまみ読みしましたが、それにしてもSambaって本気で使うとActive Directoryドメインコントローラまで作れちゃうんですね。なんというか執念を感じます。