楽々セキュアな Nextcloud 環境を構築する【podman】

今回はコンテナで、Nextcloud の SSL(Let’s Encript) 環境を作ってみましたので、手順をご紹介します。仮想化技術のおかげでどんどん楽にサーバーが立てられるようになっています。20年以上自宅サーバーを構築/管理していますが、VM だとスナップショットがあれば問題が起きる前に戻せますし、ちょっとお試しで・・・なんてことも気楽にできます。OSのインストールから何度もやり直していた、物理ハードにセットアップしていたころが馬鹿らしいです。さらにコンテナだと、ほとんど考えることなく、ワンラインで気楽にサーバーの起動停止ができるので、よりコンテンツに時間を割くことができるようになっています。インフラエンジニアとしては肩身が狭くなっていきます。しっかり勉強していきましょう。

バーチャルマシン(VM)とコンテナの違いはここでは話しませんが、今回はコンテナ技術(podman)を使って Nextcloud のサーバー構築したお話です。

参考:

https://docs.podman.io/en/v4.4/markdown/podman-pod-create.1.html
https://docs.podman.io/en/latest/markdown/podman-run.1.html
https://hub.docker.com/_/nextcloud
https://hub.docker.com/_/mysql
https://hub.docker.com/_/redis
https://docs.bunkerweb.io/1.4/

スポンサーリンク

今回紹介する Nextcloud の構成

今回紹介するのは、Nextcloud と redis、mariadb の3つのコンテナを1つの pod として扱います。この pod はバックエンドになります。フロントエンドには、 Bunkerweb のコンテナを利用しました。ここでは、Bunkerweb については、細かくは触れませんがセキュアなフロントエンドとして利用することができますし、Let’s Encript の操作を自動化してくれ、さらに複数のバックエンド(ウェブサイト)に名前(DNS名)で振り分けることも可能な(簡単にマルチサイト化できる)ため採用しました。

Bunkerweb は現時点で、1.5系 が出ていますが、複数のコンテナを上げる必要があるようで、少し理解に時間がかかりそうだったので、1.4系を利用しています。

環境

Rocky Linux release 9.3 (Blue Onyx)
podman version 4.6.1

コマンド操作は全て、root アカウントで実施しています。

下準備

コンテナを終了させても、Nextcloud はデータを保持したいため、永続化ボリュームが必要です。永続化ボリュームなどを配置するために、ディレクトリを作成します。chown は後の pod を作成させる際に、ホストとコンテナ間でuid/gidのマッピングを行っているため、これに合わせて設定します。

mkdir -p /var/local/containers/nextcloud/db/
mkdir -p /var/local/containers/nextcloud/app/{data,conf,html}
chown 100999.100999 /var/local/containers/nextcloud/db
chown -R 100033.100033 /var/local/containers/nextcloud/app

また、ログを残すためにログのディレクトリも作成しておきます。

mkdir -p /var/log/containers/

バックエンドの Bunkerweb のための永続化ボリュームの準備し、ファイアウォールを開けておきます。

mkdir -p /var/local/containers/bunkerweb/data
chown 100101.100101 /var/local/containers/bunkerweb/data
firewall-cmd --add-port=80/tcp --permanent
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload

バックエンド(Nextcloud)を立ち上げる

バックエンドとなる pod を準備します。この pod は、ホストの 2080 ポートへアクセスされると、pod(コンテナ)の 80 ポートへアクセスさせます。また、uid や gid は、コンテナの 0 をホストの 100000 にマップします。

podman pod create --hostname ncpod --name nextcloud -p 2080:80 --uidmap 0:100000:5000 --gidmap 0:100000:5000

pod の中に、mariadb のコンテナを作成します。–log-driver を指定しているのは、–log-opt path に指定したファイルにログを吐かせるためです。デフォルトでは思ったところにログを吐いてくれませんでした。latest が安定バージョンのようです(現時点で 11.2.2 でした)。-v で ローカルホストの /var/local/containers/nextcloud/db に コンテナの /var/lib/mysql をマウントさせます。これでコンテナをつぶしても DB が残ります。「:Z」 は、SELinux のためのオプション(プライベート非共有ラベル)です。

podman run -d --tz=Asia/Tokyo --pod=nextcloud --name=nc-db \
--log-driver k8s-file --log-opt path=/var/log/containers/nc-db.log \
-e MYSQL_ROOT_PASSWORD="password" -e MYSQL_DATABASE="nextcloud" -e MYSQL_USER="nextcloud" -e MYSQL_PASSWORD="password" \
-v /var/local/containers/nextcloud/db:/var/lib/mysql:Z \
docker.io/library/mariadb:latest

pod の中に、redis のコンテナを作成します。latest で問題ないでしょう・・。(現時点で 7.2.4 でした)。

podman run -d --tz=Asia/Tokyo --pod=nextcloud --name=nc-cache \
--log-driver k8s-file --log-opt path=/var/log/containers/nc-cache.log \
docker.io/library/redis:latest

pod の中に、nextcloud のコンテナを作成します。stable を指定します。(現時点で 27.1.6 でした)。-v で /var/local/containers/nextcloud/app/ の各ディレクトリをマウントしています。data には保存されるデータが入ります。config には、config.php が入っているので出しました。html は、cron.php をホストから触るために出しています。「:z」は、SELinux のためのオプション(共有コンテンツラベル)です。

podman run -d --tz=Asia/Tokyo --pod=nextcloud --name=nc-app \
--log-driver k8s-file --log-opt path=/var/log/containers/nc-app.log \
-e MYSQL_DATABASE="nextcloud" -e MYSQL_USER="nextcloud" -e MYSQL_PASSWORD="password" \
-e MYSQL_HOST="127.0.0.1" -e REDIS_HOST="127.0.0.1" \
-v /var/local/containers/nextcloud/app/html:/var/www/html:z \
-v /var/local/containers/nextcloud/app/data:/var/www/html/data:z \
-v /var/local/containers/nextcloud/app/conf:/var/www/html/config:z \
docker.io/library/nextcloud:stable
Nextcloud で latest を指定した場合、想定通りに動きませんでした。コンテナの知識不足なのかと、かなり悩みましたが、docker hub の説明によると stable を指定するのが正しいようです。

これだけで、Nextcloud が立ち上がっているはずです。いったん http://ホストのIP:2080 にアクセスして確認してみましょう。Nextcloudが立ち上がっていると思います。また、管理者ユーザーのセットアップを完了させておきましょう。config.php が変更されます。もし、アクセスできない場合は、/var/log/containers/nc-app.log にログが出ていると思いますので、それを確認しましょう。

今作った Nextcloud pod を systemd で自動起動するために以下のコマンドを実行します。
一旦、停止して、

podman pod stop nextcloud

systemd の設定ファイルを生成し、systemd で起動しなおします。

cd /etc/systemd/system/
podman generate systemd --files --name nextcloud
systemctl daemon-reload
systemctl enable pod-nextcloud.service
systemctl start pod-nextcloud.service

フロントエンド(Bunkerweb)を立ち上げる

Let’s Encript(SSL) の自動化と、セキュリティを強化するために、フロントエンドに  Bunkerweb を立ち上げます。社内や自宅内だけで使う場合は、先ほど立ち上げたバックエンドだけでも十分に機能すると思います。

通常は、NATルーターの中にホストがあると思いますので、以下の作業をする前に ルーターに 80 と 443 ポートをホストにマッピング(フォワード)してあげてください。

/var/local/containers/bunkerweb/podman.env ファイルを作成し以下を記載

SERVER_NAME=server1.example.com
USE_REVERSE_PROXY=yes
REVERSE_PROXY_URL=/
REVERSE_PROXY_HOST=http://ホストのIP:2080
ALLOWED_METHODS=GET|POST|HEAD|PROPFIND|DELETE|PUT|MKCOL|MOVE|COPY|PROPPATCH|REPORT|OPTIONS
BAD_BEHAVIOR_STATUS_CODES=400 401 403 405 444
INTERCEPTED_ERROR_CODES=400 401 403 405 500 501 502 503 504
USE_DNSBL=no
USE_BUNKERNET=no
USE_MODSECURITY=no
USE_MODSECURITY_CRS=no
LIMIT_REQ_RATE=40r/s
SERVE_FILES=no
DISABLE_DEFAULT_SERVER=yes
AUTO_LETS_ENCRYPT=yes
USE_LETS_ENCRYPT_STAGING=yes       (1)
USE_GZIP=yes
X_FRAME_OPTIONS=SAMEORIGIN
MAX_CLIENT_SIZE=10G
(注1) USE_LETS_ENCRYPT_STAGING は、完成したら no にします。初回構築で何度も、動作確認を繰り返すことになる場合は、USE_LETS_ENCRYPT_STAGING を yes で使いましょう。
さらっと書いていますが、Bunkerwebのキモとなる設定です。動くようになるまで結構悩みました。Bunkerweb 本家の説明をよく確認してください。自宅サーバーで必要なレベルには調整しているつもりですが、特に LIMIT_REQ_RATE は環境に合わせて設定が必要だと思います。

/etc/systemd/system/container-bunkerweb.service を作成し、以下を記載

[Unit]
Description=Podman container-bunkerweb.service

[Service]
Restart=on-failure
ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid \
-d -p 80:8080 -p 443:8443 \
-v /var/local/containers/bunkerweb/data:/data:z \
--uidmap 0:100000:5000 --gidmap 0:100000:5000 \
--tz=Asia/Tokyo \
--env-file /var/local/containers/bunkerweb/podman.env \
--log-driver k8s-file \
--log-opt path=/var/log/containers/bunkerweb.log \
--name=bunkerweb docker.io/bunkerity/bunkerweb:1.4.8
ExecStop=/usr/bin/podman stop -t 10 bunkerweb
ExecStopPost=/usr/bin/sh -c "/usr/bin/podman rm bunkerweb"
KillMode=none
Type=forking
PIDFile=%t/%n-pid

[Install]
WantedBy=default.target

以下のコマンドで Bunkerweb が自動起動するように設定します。

systemctl daemon-reload
systemctl start container-bunkerweb
systemctl enable container-bunkerweb

Nextcloud の config 設定

/var/local/containers/nextcloud/app/conf/config.php ファイルを開いて、フロントエンド(proxyサーバー)の設定を入れます。

・・・
  'trusted_domains' =>
  array (
  0 => 'ホストのIP:2080',
  1 => 'server1.example.com',
  ),
・・・
  'trusted_proxies' =>
  array (
  0 => 'ホストのIP',
  ),
・・・
  'overwrite.cli.url' => 'https://server1.example.com',
  'overwriteprotocol' => 'https',
  'overwritehost' => 'server1.example.com',
  'default_phone_region' => 'JP',
  'default_language' => 'ja',
  'default_locale' => 'ja',
・・・

以下のコマンドで、バックエンドに設定を再読み込みします。

systemctl restart pod-nextcloud.service

これで、https://server1.example.com にアクセスできると思います。アクセスできない場合は、/var/log/containers/bunkerweb.log にログが出ていると思いますので、それを確認しましょう。

ルーターに 80 と 443 ポートをフォワードせずに、作業を行うと Let’s Encript の設定でエラーとなり、Bunkerweb がうまく立ち上がりません。

微調整

管理者アカウントでログインして、管理者設定画面を開きます。

Webサーバーで “/.well-known/caldav” が解決されるように正しく設定されていません。詳細については、ドキュメントをご覧ください。
Webサーバーで “/.well-known/carddav” が解決されるように正しく設定されていません。詳細については、ドキュメントをご覧ください。

このエラーを消すためには、Bunkerweb の方にリダイレクトの設定を追加します。

/var/local/containers/bunkerweb/data/configs/server-http/redirect.conf を開き以下の設定を追加します。nginx の config ファイルなので少し見慣れませんでしたが、このようにするとリダイレクトできるようです。

location /.well-known/carddav {
    return 301 $scheme://$host/remote.php/dav;
}

location /.well-known/caldav {
    return 301 $scheme://$host/remote.php/dav;
}
メールサーバーの設定が未設定または未確認です。基本設定で設定を行ってください。その後、フォームの下にある「メールを送信」ボタンで設定を確認してください。

このエラーを消すのは、そのままです。「基本設定」からメールサーバーの設定を行ってください。

メールサーバーの設定で、メールのテストと検証設定をで「メール送信」を成功させるには、admin のメールアドレスを登録しておく必要があります。実はちょっとだけハマりました。

cron ジョブの実行

cron ジョブが一番悩みました。基本設定でバックグラウンドジョブを「Cron(推奨)」にして放置していると、そのうちエラーが出ます。

コンテナでは cron を実行させるためには工夫がいるようなので、cron.php をホスト側から呼ぶようにしました。 /etc/cron.d/pod-nextcloud ファイルを作成し以下を記述します。

*/5 * * * * root podman exec -u www-data nc-app /usr/local/bin/php -f /var/www/html/cron.php

cron が実行されるようにアクセス権を設定します。

chmod 400 /etc/cron.d/pod-nextcloud