Docker上で実行しているNextCloudで”Updates between multiple major versions and downgrades are unsupported. Update failed.”が発生してアップデートに失敗する。

Dockerを使ってNextCloudを運用して居る。久しぶりにNextCloudバージョン画面を見たところ、「保守終了したバージョン」との警告が。あわててアップデートを行ったところ、ずっとメンテナンスモードから戻らなくなってしまった。そう、うっかりとversion 23→version 25にアップデートを掛けてしまったため、処理が正常に終了せずに環境が壊れてしまったのだ。

以下のコマンドを実行してメンテナンスモードを強制的に解除してNextCloudをWERBで開くがUpgradeの確認画面になり、そしてUpgradeは失敗してしまう。

$ sudo docker exec -u www-data <container name> php occ maintenance:mode --off

以下のコマンドを実行してコマンドラインからUpgradeを実施するが、「Updates between multiple major versions and downgrades are unsupported. Update failed.」のエラーになってしまう。

$ sudo docker exec -u www-data <container name> php occ upgrade

リカバリ方法がないかと探していたところ「How to fix an accidental Nextcloud docker image update」にたどり着いたので、こちらの方法でリカバリを試みることにする。

まずhtml/config/config.phpを開き、正確なバージョンを確認する。ここでは「’version’ => ‘23.0.0.10’,」とあるとおり、23.0.0.10が正確なージョンになる。

  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'mysql',
  'version' => '23.0.0.10',
  'overwrite.cli.url' => 'https://example.net',
  'dbname' => 'nextcloud',

次にhtml/version.phpを編集する。既にversion 25のファイルに置き換わってしまっているため、これを書き換えて23→24へのアップグレードを通すために、$OC_Versionをarray(25,0,2,3)からarray(23,0,0,10)に、$OC_VersionStringを’25.0.2’から’23.0.0’に、array (‘24.0’ => true, ‘25.0’ => true,)をarray (‘23.0’ => true, ‘24.0’ => true,)に、下記の通りに書き換えます。

<?php
$OC_Version = array(23,0,0,10);
$OC_VersionString = '23.0.0';
$OC_Edition = '';
$OC_Channel = 'stable';
$OC_VersionCanBeUpgradedFrom = array (
  'nextcloud' =>
  array (
    '23.0' => true,
    '24.0' => true,
  ),
  'owncloud' =>
  array (
    '10.11' => true,
  ),
);
$OC_Build = '2022-12-08T11:32:17+00:00 9c791972de9c2561a9b36c1a60e48c25315ecca5';
$vendor = 'nextcloud';

続いてdocker-compose.ymlを編集します。下記のようにnextcloud:24のようにアップデート先となる旧バージョンのdockerイメージを指定します。

  nextcloud:
    image: nextcloud:24

これで23→24にアップデートする環境が整うので、以下のコマンドを実行してNextCloudを起動し、23から24へのアップデートを実行します。

$ sudo docker-compose up --build -d
$ sudo docker exec -u www-data <container name> php occ upgrade

version 24へのアップデートが正常に終了したら、再びdocker-compose.ymlを編集して元に戻します。その後に再び下記のコマンドを実行すれば、24から25へのバージョンアップを行えます。

$ sudo docker-compose up --build -d
$ sudo docker exec -u www-data <container name> php occ upgrade

Nextcloudに大きなファイルをアップロードするとRequestTimeTooSkewedが発生する

NextcloudでストレージにS3を使用している場合に、約500MBを超える大きなファイルをアップロードすると、以下のようなエラーが発生する場合がある。

An exception occurred while uploading parts to a multipart upload. The following parts had errors:
- Part 1: Error executing "UploadPart" on "https://nextcloud-xxxx.s3.us-west-1.wasabisys.com/xxxx"; AWS HTTP error: Client error: `PUT https://nextcloud-xxxx.s3.us-west-1.wasabisys.com/xxxx` resulted in a `403 Forbidden` response:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>RequestTimeTooSkewed</Code><Message>The difference between the reque (truncated...)
RequestTimeTooSkewed (client): The difference between the request time and the current time is too large. - <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>RequestTimeTooSkewed</Code><Message>The difference between the request time and the current time is too large.</Message><RequestTime>20220101T141414Z</RequestTime><ServerTime>2022-01-01T14:32:28Z</ServerTime><MaxAllowedSkewMilliseconds>900000</MaxAllowedSkewMilliseconds><RequestId>xxxx</RequestId><HostId>xxxx/xxxx</HostId></Error>

この問題を解決するには./html/config/config.phpに以下の行を追加し、500MBよりも適当に小さなサイズで分割してアップロードするように設定する。下記の例では約20MBに設定を変更している。

    array (
      'bucket' => 'nextcloud-bucket',
      'key' => '{key}',
      'secret' => '{secret}',
      'region' => 'us-west-1',
      'hostname' => 's3.us-west-1.wasabisys.com',
      'port' => '443',
      'objectPrefix' => 'urn:oid:',
      'autocreate' => false,
      'use_ssl' => true,
      'use_path_style' => false,
      'legacy_auth' => false,
      'uploadPartSize' => 20971520,
    ),

S3にsinglepartでアップロードできるファイルサイズの上限は5GBとなり、より大きなファイルをアップロードするときにはmultipartでアップロードする必要がある。標準設定のNextcloudでは約500MBを超えるファイルをアップロードするときにはmultipartアップロード を使用する。

S3のmultipartアップロードがもつ仕様上の問題で、通信帯域の不足等によりデータのアップロードに約15分以上かかると、HTTPヘッダに記載されている時刻とAWS側サーバーとの時刻の差がMaxAllowedSkewMillisecondsを超えるために”RequestTimeTooSkewed: The difference between the reque (truncated…)
RequestTimeTooSkewed (client): The difference between the request time and the current time is too large.”のエラーが発生する。

MaxAllowedSkewMillisecondsは900000msに固定されいる。HTTPのリクエストデータを複製することによる第三者の攻撃を防ぐために設けられている値で、ユーザー側でこの値を任意に変更する事は出来ない。この問題を避けるには15分以内にアップロードが終わる程度の適当なサイズに分割してアップロードする必要がある。

ただし小さく分割するとS3にアップロードできる最大ファイルサイズが小さくなる事にも注意しなくてはならない。S3には最大で5TBのファイルを保管できるが、 multipart アップロード時には10,000分割以上に分ける事ができない。仮に上記のように20MBで分割した場合には、200GBを超えるファイルをアップロード出来ない。(Amazon S3 multipart upload limits

NextcloudでRedisによるLock管理を有効にする

NextcloudのWindowsアプリからフォルダの同期を行っている途中、~ Locked errorが度々表示されていました。Nextcloudはデフォルトだとmysqlを使用してロック管理をしていますが、高負荷時に問題がおこる場合があるようです。ファイルのロックにかかる問題を回避するために、Redisを導入してTransactional file lockingを有効にします。

Docker環境でRedisを有効にするにはdocker-conpose.ymlに、redisのイメージを追加し、nextcloudのenvironemntにREDIS_HOST、REDIS_HOST_PORT、REDIS_HOST_PASSWORDを、下記の様に追加します。

version: '3'

volumes:
  nextcloud:
  nextcloud-db:
  nextcloud-redis:

services:
  nextcloud-redis:
    image: redis
    ports:
      - 6379:6379
    volumes:
      - ./redis:/data

  nextcloud-db:
    image: mariadb
      ...

  nextcloud:
    image: nextcloud
      ...
    environment:
      - REDIS_HOST=nextcloud-redis
      - REDIS_HOST_PORT=6379
      - REDIS_HOST_PASSWORD=${redis_password}

その後、以下のコマンドを実行します。

$ sudo docker-compose up -d

docker-compose.ymlの設定だけでは Transactional file locking は有効になっていません。./html/config/config.phpを編集して、「’filelocking.enabled’ => true,」を追記します。

  'filelocking.enabled' => true,
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'redis' =>
  array (
    'host' => 'nextcloud-redis',
    'password' => '',
    'port' => 6379,
  ),

Nextcloudの暗号化を有効にするときの覚書

Nextcloud:Encryption configuration

暗号鍵の保存場所

暗号化を有効にしたとき、暗号鍵は./html/data/files_encryptionおよび ./html/data/ユーザー名/files_encryptionに保存されます。基本的にアップロードしたデータと同じ場所に鍵も保管されるため、プライマリストレージをS3等のストレージに変更しているか、あるいは外部ストレージを使用していない限り暗号化によるセキュリティ向上の恩恵はありません。

またこれらの暗号鍵を安全な場所にバックアップしておく必要があります。

回復パスワードの保存

上記フォルダの暗号鍵はログインパスワードを元にして暗号化されているため、ログインパスワードが分からなくなった場合に、パスワードの初期化を行うと暗号化したファイルを読むことが出来なくなります。

これを避ける為には事前に回復パスワードを作成しておく必要があります。

LDAPなど外部の認証システムとの連携

前項の理由により、LDAPなど外部の認証システムと連携している場合に、ファイブのシステム上でパスワードの変更を行う場合、そのままでは暗号ファイルを読み取ることが出来なくなります。Nextcloud上で新旧のパスワード入力を求められるため、そこで旧パスワードを入力する必要があります。

NextCloudでアップロード時に不明なエラーが発生する。

大きなファイルをアップロードしていると「不明なエラー」が発生します。ログを見ると以下の様なエラーが発生していました。どうも帯域が狭いために、アップロード中にタイムアウトエラーが発生しているようです。

Sabre\DAV\Exception\BadRequest: Expected filesize of 10485760 bytes but read (from Nextcloud client) and wrote (to Nextcloud storage) 5368032 bytes. Could either be a network problem on the sending side or a problem writing to the storage on the server side.

10,485,760 bytesは標準のチャンクサイズです。Nextcloudではアップロードをチャンクサイズのデータに分解して処理しています。10MB分送信する前にタイムアウトが発生してしまっていたようです。

タイムアウトを長くしても良いのですが、設定対象が多岐に渡るので、ここはチャンクサイズを5MBに調整する方向で対処します。occコマンドで設定を変更できますので、以下のコマンドを実行してチャンクサイズを変更します。

$ sudo docker-compose exec --user www-data nextcloud php occ config:app:set files max_chunk_size --value 5242880
Config value max_chunk_size for app files set to 5242880

再度ファイルをアップロードしたところ、エラーになる事無く上手く動いているようです。