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

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

NextCloudのアクセスログ管理

Nextcloudには標準で監査ログ機能があります。インターネットから接続出来る状態で運用するなら、監理者の責任としてログぐらいは残して起きたいところです。Docker上で動作させているNextCloudのログ設定をおこないます。

監査ログ出力のための設定

「Auditing / Logging」プラグインが有効になっていることを確認して下さい。標準で有効になっているはずです。

以下のコマンドを実行してログの設定をログレベル:1、タイムズーんを日本に変更します。現時点(バージョン23.0.0)では–rotate-sizeの設定は機能しません。明示的にNextcloudのLog Rotateを無効にしておき、別にlogrotateを設定する事にします。

$ sudo docker-compose exec --user www-data nextcloud php occ log:manage --level=info --timezone Asia/Tokyo
Enabled logging backend: file
Log level: Info (1)
Log timezone: Asia/Tokyo

$ sudo docker-compose exec --user www-data nextcloud php occ log:file --rotate-size=0
Log backend file: enabled
Log file: /var/www/html/data/nextcloud.log
Rotate at: disabled

これで./html/data/audit.logに監査ログが出力されるようになるはずです。

Log Rotateの設定

/etc/logrotate.d/nextcloudを以下の通り作成します。パス名の/var/docker-nextcloudは自信の環境に置き換えて下さい。

/var/docker-nextcloud/html/data/nextcloud.log {
  monthly
  rotate 12
  missingok
  notifempty
  compress
  delaycompress
  postrotate
    touch /var/docker-nextcloud/html/data/nextcloud.log
    chown www-data:www-data /var/docker-nextcloud/html/data/nextcloud.log
    chmod 640 /var/docker-nextcloud/html/data/nextcloud.log
  endscript
}

/etc/logrotate.d/nextcloudauditを以下の通り作成します。

/var/docker-nextcloud/html/data/audit.log {
  monthly
  rotate 12
  missingok
  notifempty
  compress
  delaycompress
  postrotate
    touch /var/docker-nextcloud/html/data/audit.log
    chown www-data:www-data /var/docker-nextcloud/html/data/audit.log
    chmod 640 /var/docker-nextcloud/html/data/audit.log
  endscript
}

以下の通りlogrotateデーモンを再起動します。

$ sudo service logrotate restart

以上で毎月ログが圧縮されて別ファイルとなり、12ヶ月でローテーションされます。