Nextcloud

Überblick

Es spielt eine Rolle, welchem Anbieter man seine Daten (Dateien aller Art, Kalender-Einträge etc.) anvertraut: Unternehmen mit Sitz in den USA müssen laut US CLOUD Act nach Aufforderung der Behörden jederzeit Daten von ihren Servern liefern, auch wenn sie ausserhalb der USA stehen. Die National Security Letters verbieten ihnen unter Strafandrohung, ihre Kunden darüber zu informieren. Das kollidiert mit den Schweizer- und EU-Datenschutzregeln und/oder nationalem Recht. Der Einsatz solcher Lösungen kann daher beispielsweise in Deutschland zu massiven DSGVO-Strafen führen.

Nextcloud ist Open Source und bietet Cloud-Dienste und Datensynchronisation auf eigenen Servern, die zu 100% unter eigener Kontrolle stehen - also ganz im Gegensatz zu AWS, Azure und Office 365, Dropbox oder GCP. Nextcloud ging Mitte 2016 aus Owncloud hervor. Der Unternehmensgründer Frank Karlitschek selbst gab den Fork bekannt.

Kern-Feature ist die verteilte Zusammenarbeit an Dateien, die per Web-GUI, WebDAV oder dem Nextcloud-Client verwaltet und synchronisiert werden können.

Apps erweitern das System in beliebige Richtungen:

  • Kalender und Adressbuch (Standard)

  • Videokonferenz

  • Social-Media-Funktionen

  • Kartenintegration

  • alternative Benutzerdatenbanken (LDAP, OAuth)

  • etc.

Versionstabelle:

Nextcloud Version

Nextcloud Hub

max. PHP-Version

Nextcloud 27.1

Hub 6

Nextcloud 27

Hub 5

PHP 8.2

Nextcloud 26

Hub 4

PHP 8.2

Nextcloud 25

Hub 3

PHP 8.1

Nextcloud 24

Hub 24

PHP 8.1

Nextcloud 23

Hub 23 / Hub II

PHP 8.0

Nextcloud 22

Hub 22

PHP 8.0

Nextcloud 21

Hub 21

PHP 8.0

Nextcloud 20

Hub 20

PHP 7.4

Nextcloud 19

Hub 19

PHP 7.4

Nextcloud 18

als Hub 18 vermarktet

PHP 7.4

Nextcloud 17

PHP 7.3

Nextcloud 16

PHP 7.3

Nextcloud 15

PHP 7.3

Nextcloud 14

PHP 7.2

Nextcloud 13

PHP 7.2

Installation

Siehe https://docs.nextcloud.com/server/latest/admin_manual/installation/example_centos.html. Download von https://download.nextcloud.com/server/releases/ (neueste Datei: https://download.nextcloud.com/server/releases/latest.tar.bz2).

Tipp

Wer eine Shared Instance betreibt, auf der sich die Benutzer untereinander nicht kennen, sollte die App „User status“ abschalten - sie ermöglicht im Nextcloud-Dashboard die Anzeige der „Recent statuses“, eine Liste der letzten Anmeldevorgänge der Benutzer (inkl. E-Mail-Adresse).

Update/Upgrade durchführen

per OCC (bevorzugt)

Siehe nextcloud-update.

per Web-Interface
setsebool httpd_unified on
setsebool httpd_can_network_connect on

Jetzt das Update über das Webinterface durchführen. Anschliessend:

setsebool -P httpd_unified off
# if we don't need any network connections
setsebool -P httpd_can_network_connect off

Forbidden Chars

Liste ungültiger Zeichen in Verzeichnis- und Dateinamen (sogenannte „forbidden chars“):

\ / : ? * " < > |
plus: files/folders with leading and trailing spaces are also considered invalid

Siehe dazu auch unseren Blog-Beitrag RewriteRule vs ProxyPass - für Nextcloud entscheidend.

Eine Liste aller Dateien, die ungültige Zeichen im Verzeichnis- oder Dateinamen enthalten, kann aus der Datenbank extrahiert werden:

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%/%' INTO OUTFILE '/tmp/forbidden-chars-slash.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%\\%' INTO OUTFILE '/tmp/forbidden-chars-backslash.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%:%' INTO OUTFILE '/tmp/forbidden-chars-colon.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%?%' INTO OUTFILE '/tmp/forbidden-chars-question-mark.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%*%' INTO OUTFILE '/tmp/forbidden-chars-star.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%"%' INTO OUTFILE '/tmp/forbidden-chars-quote.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%<%' INTO OUTFILE '/tmp/forbidden-chars-less-than.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%>%' INTO OUTFILE '/tmp/forbidden-chars-greater-than.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

SELECT
    oc_storages.id, path
FROM
    oc_filecache
        LEFT JOIN
    oc_storages ON oc_filecache.storage = oc_storages.numeric_id
WHERE
    path LIKE 'files/%' AND name LIKE '%|%' INTO OUTFILE '/tmp/forbidden-chars-pipe.csv' FIELDS TERMINATED BY ', ' ENCLOSED BY '"';

Nextcloud Loglevel

  • 0: DEBUG: All activity; the most detailed logging.

  • 1: INFO: Activity such as user logins and file activities, plus warnings, errors, and fatal errors.

  • 2: WARN: Operations succeed, but with warnings of potential problems, plus errors and fatal errors.

  • 3: ERROR: An operation fails, but other services and operations continue, plus fatal errors.

  • 4: FATAL: The server stops.

/var/www/html/nextcloud/config/config.php
<?php
$CONFIG = array (
  'loglevel' => 3,
);

OCC

OCC Repair

Was macht ein sudo -u apache /var/www/html/nextcloud/occ maintenance:repair genau?

- Repair MySQL collation
    - All tables already have the correct collation -> nothing to do
- Repair mime types
- Clean tags and favorites
    - 0 tags of deleted users have been removed.
    - 0 tags for delete files have been removed.
    - 0 tag entries for deleted tags have been removed.
    - 2 tags with no entries have been removed.
- Repair invalid shares
- Move .step file of updater to backup location
- Add move avatar background job
    - Repair step already executed
- Add preview cleanup background jobs
- Fix potential broken mount points
    - No mounts updated
- Repair language codes
- Install new core bundle components
- Add log rotate job
- Clear frontend caches
    - Image cache cleared
    - SCSS cache cleared
    - JS cache cleared
- Clear every generated avatar on major updates
- Add preview background cleanup job
- Queue a one-time job to cleanup old backups of the updater
- Cleanup invalid photocache files for carddav
- Add background job to cleanup login flow v2 tokens
- Remove potentially over exposing share links
    - No need to remove link shares.
- Clear access cache of projects
- Reset generated avatar flag
- Keep legacy encryption enabled
- Check encryption key format
- Remove old dashboard app config data
- Add job to cleanup the bruteforce entries
- Queue a one-time job to check for user uploaded certificates
- Repair DAV shares
- Add background job to set the lookup server share state for users
- Update hook for Appointments app
    - appointments UpdateHook finished
- Deduplicate shared bookmark folders
    - Removed 0 duplicate shares
- Remove superfluous shared bookmark folders
    - Removed 0 superfluous shares
- Remove orphaned bookmark shares
    - Removed 0 orphaned shares
    - Removed 0 orphaned public links
- Remove orphaned bookmark tree items
    - Removed 0 orphaned bookmarks entries
    - Removed 0 orphaned folders entries
    - Reinserted 0 orphaned children entries
    - Removed 0 orphaned bookmark folders
    - Reinserted 0 orphaned bookmarks
- Update bookmark group shares
    - Removed 0 users and added 0 users to 0 groups
    - Removed 0 shares
- Update name of the stored view
- Fix component of birthday calendars
    - 42 birthday calendars updated.
- Regenerating birthday calendars to use new icons and fix old birthday events without year
    - Repair step already executed
- Fix broken values of calendar objects
   0 [->--------------------------]
- Registering building of calendar search index as background job
    - Repair step already executed
- Register building of social profile search index as background job
    - Repair step already executed
- Registering background jobs to update cache for webcal calendars
    - Added 0 background jobs to update webcal calendars
- Registering building of calendar reminder index as background job
    - Repair step already executed
- Clean up orphan event and contact data
    - 0 events without a calendar have been cleaned up
    - 0 properties without an events have been cleaned up
    - 0 changes without a calendar have been cleaned up
    - 0 cached events without a calendar subscription have been cleaned up
    - 0 changes without a calendar subscription have been cleaned up
    - 0 contacts without an addressbook have been cleaned up
    - 0 properties without a contact have been cleaned up
    - 0 changes without an addressbook have been cleaned up
- Remove activity entries of private events
    - Removed 0 activity entries
- Clean up old calendar subscriptions from deleted users that were not cleaned-up
7/7 [============================] 100%
    - 0 calendar subscriptions without an user have been cleaned up
- Write default encryption module configuration to the database
- Copy default images to the app data directory
    - Icon external.svg already exists
    - Icon external-dark.svg already exists
    - Icon settings.svg already exists
- Fix the share type of guest shares when migrating from ownCloud
- Copy the share password into the dedicated column
- Set existing shares as accepted
- Restore default tags that are missing
    - Looking up default tags
    - 17 accounts to check found
17/17 [============================] 100%
- Add a generated message-id to all Mail messages that have none
    - Looking up messages without a message-id
- Purify and migrate collected mail addresses
12/12 [============================] 100%
- Insert background jobs for all accounts
17/17 [============================] 100%
- Make Mail itinerary extractor executable
- Create or update provisioned Mail accounts
    - 0 accounts provisioned
- Repair Broken Threads for all mail accounts
    - Repaired threads, 0 messages updated
- Clean up meta table
- Update OAuth token expiration times
- Enabling server side encryption for passman
    - Enabling Service Side Encryption for passman
- Polls - Create indices and foreign key constraints
    - Polls - Indices created.
    - Polls - Foreign key contraints created.
- Create help command
- Invalidate access cache for projects conversation provider
    - Invalidation not required
- Cache the user display names
- Switches from default updater server to the customer one if a valid subscription is available
    - Repair step already executed
- Add background job to check for backup codes
- Populating added database structures for workflows

OCC Cheat Sheet

v24

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --no-warnings     Skip global warnings, show command output only
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  check                                  check dependencies of the server environment
  help                                   Display help for a command
  list                                   List commands
  status                                 show some status information
  upgrade                                run upgrade routines after installation of a new release. The release has to be installed before.
 activity
  activity:send-mails                    Sends the activity notification mails
 app
  app:check-code                         check code to be compliant
  app:disable                            disable an app
  app:enable                             enable an app
  app:getpath                            Get an absolute path to the app directory
  app:install                            install an app
  app:list                               List all available apps
  app:remove                             remove an app
  app:update                             update an app or all apps
 background
  background:ajax                        Use ajax to run background jobs
  background:cron                        Use cron to run background jobs
  background:webcron                     Use webcron to run background jobs
 background-job
  background-job:execute                 Execute a single background job manually
 broadcast
  broadcast:test                         test the SSE broadcaster
 circles
  circles:check                          Checking your configuration
  circles:maintenance                    Clean stuff, keeps the app running
  circles:manage:config                  edit config/type of a Circle
  circles:manage:create                  create a new circle
  circles:manage:destroy                 destroy a circle by its ID
  circles:manage:details                 get details about a circle by its ID
  circles:manage:edit                    edit displayName or description of a Circle
  circles:manage:join                    emulate a user joining a Circle
  circles:manage:leave                   simulate a user joining a Circle
  circles:manage:list                    listing current circles
  circles:manage:setting                 edit setting for a Circle
  circles:members:add                    Add a member to a Circle
  circles:members:details                get details about a member by its ID
  circles:members:level                  Change the level of a member from a Circle
  circles:members:list                   listing Members from a Circle
  circles:members:remove                 remove a member from a circle
  circles:members:search                 Change the level of a member from a Circle
  circles:memberships                    index and display memberships for local and federated users
  circles:remote                         remote features
  circles:shares:files                   listing shares files
  circles:sync                           Sync Circles and Members
  circles:test                           testing some features
 config
  config:app:delete                      Delete an app config value
  config:app:get                         Get an app config value
  config:app:set                         Set an app config value
  config:import                          Import a list of configs
  config:list                            List all configs
  config:system:delete                   Delete a system config value
  config:system:get                      Get a system config value
  config:system:set                      Set a system config value
 dav
  dav:create-addressbook                 Create a dav addressbook
  dav:create-calendar                    Create a dav calendar
  dav:delete-calendar                    Delete a dav calendar
  dav:list-calendars                     List all calendars of a user
  dav:move-calendar                      Move a calendar from an user to another
  dav:remove-invalid-shares              Remove invalid dav shares
  dav:retention:clean-up
  dav:send-event-reminders               Sends event reminders
  dav:sync-birthday-calendar             Synchronizes the birthday calendar
  dav:sync-system-addressbook            Synchronizes users to the system addressbook
 db
  db:add-missing-columns                 Add missing optional columns to the database tables
  db:add-missing-indices                 Add missing indices to the database tables
  db:add-missing-primary-keys            Add missing primary keys to the database tables
  db:convert-filecache-bigint            Convert the ID columns of the filecache to BigInt
  db:convert-mysql-charset               Convert charset of MySQL/MariaDB to use utf8mb4
  db:convert-type                        Convert the Nextcloud database to the newly configured one
 encryption
  encryption:change-key-storage-root     Change key storage root
  encryption:decrypt-all                 Disable server-side encryption and decrypt all files
  encryption:disable                     Disable encryption
  encryption:enable                      Enable encryption
  encryption:encrypt-all                 Encrypt all files for all users
  encryption:list-modules                List all available encryption modules
  encryption:migrate-key-storage-format  Migrate the format of the keystorage to a newer format
  encryption:set-default-module          Set the encryption default module
  encryption:show-key-storage-root       Show current key storage root
  encryption:status                      Lists the current status of encryption
 federation
  federation:sync-addressbooks           Synchronizes addressbooks of all federated clouds
 files
  files:cleanup                          cleanup filecache
  files:recommendations:recommend
  files:repair-tree                      Try and repair malformed filesystem tree structures
  files:scan                             rescan filesystem
  files:scan-app-data                    rescan the AppData folder
  files:transfer-ownership               All files and folders are moved to another user - outgoing shares and incoming user file shares (optionally) are moved as well.
 group
  group:add                              Add a group
  group:adduser                          add a user to a group
  group:delete                           Remove a group
  group:info                             Show information about a group
  group:list                             list configured groups
  group:removeuser                       remove a user from a group
 integrity
  integrity:check-app                    Check integrity of an app using a signature.
  integrity:check-core                   Check integrity of core code using a signature.
  integrity:sign-app                     Signs an app using a private key.
  integrity:sign-core                    Sign core using a private key.
 l10n
  l10n:createjs                          Create javascript translation files for a given app
 log
  log:file                               manipulate logging backend
  log:manage                             manage logging configuration
  log:tail                               Tail the nextcloud logfile
  log:watch                              Watch the nextcloud logfile
 maintenance
  maintenance:data-fingerprint           update the systems data-fingerprint after a backup is restored
  maintenance:mimetype:update-db         Update database mimetypes and update filecache
  maintenance:mimetype:update-js         Update mimetypelist.js
  maintenance:mode                       set maintenance mode
  maintenance:repair                     repair this installation
  maintenance:theme:update               Apply custom theme changes
  maintenance:update:htaccess            Updates the .htaccess file
 notification
  notification:generate                  Generate a notification for the given user
  notification:test-push                 Generate a notification for the given user
 preview
  preview:repair                         distributes the existing previews into subfolders
  preview:reset-rendered-texts           Deletes all generated avatars and previews of text and md files
 security
  security:bruteforce:reset              resets bruteforce attemps for given IP address
  security:certificates                  list trusted certificates
  security:certificates:import           import trusted certificate in PEM format
  security:certificates:remove           remove trusted certificate
 serverinfo
  serverinfo:update-storage-statistics   Triggers an update of the counts related to storages used in serverinfo
 sharing
  sharing:cleanup-remote-storages        Cleanup shared storage entries that have no matching entry in the shares_external table
  sharing:expiration-notification        Notify share initiators when a share will expire the next day.
 tag
  tag:add                                Add new tag
  tag:delete                             delete a tag
  tag:edit                               edit tag attributes
  tag:list                               list tags
 text
  text:reset                             Reset a text document
 theming
  theming:config                         Set theming app config values
 trashbin
  trashbin:cleanup                       Remove deleted files
  trashbin:expire                        Expires the users trashbin
  trashbin:restore                       Restore all deleted files
  trashbin:size                          Configure the target trashbin size
 twofactorauth
  twofactorauth:cleanup                  Clean up the two-factor user-provider association of an uninstalled/removed provider
  twofactorauth:disable                  Disable two-factor authentication for a user
  twofactorauth:enable                   Enable two-factor authentication for a user
  twofactorauth:enforce                  Enabled/disable enforced two-factor authentication
  twofactorauth:state                    Get the two-factor authentication (2FA) state of a user
 update
  update:check                           Check for server and app updates
 user
  user:add                               adds a user
  user:add-app-password                  Add app password for the named user
  user:delete                            deletes the specified user
  user:disable                           disables the specified user
  user:enable                            enables the specified user
  user:info                              show user info
  user:lastseen                          shows when the user was logged in last time
  user:list                              list configured users
  user:report                            shows how many users have access
  user:resetpassword                     Resets the password of the named user
  user:setting                           Read and modify user settings
 versions
  versions:cleanup                       Delete versions
  versions:expire                        Expires the users file versions
 workflows
  workflows:list                         Lists configured workflows

Storage: Object Storage Backend

Nextcloud von Beginn an mit Object Storage Backend aufsetzen

Der Trick: Vor der Ausführung des occ-Kommandos zur Installation die Konfigurationsdatei für den Objectstore erzeugen und direkt zur config.php dazulegen. Der Installer führt die beiden Dateien zusammen, womit der Object-Store zum primären Storage wird. Im Beispiel mit S3:

/var/www/html/nextcloud/config/objectstore.config.php
$CONFIG = array (
    'objectstore' => [
        'class' => 'OC\\Files\\ObjectStore\\S3',
        'arguments' => [
            'autocreate' => true,
            'bucket' => 'bucket-name',
            'hostname' => 's3.pub1.infomaniak.cloud',
            'key' => '7ca40b8a-2572-4ff7-9a9a-57051bf33dc7',
            'region' => 'us-east-1',
            'secret' => '51c9d8b3-25f7-45dc-812f-2146e38d2167',
            'use_ssl' => true,
            'use_path_style' => true,
        ],
    ],
);

Dann:

sudo -u apache php occ maintenance:install
    --admin-pass 'linuxfabrik'
    --admin-user 'nextcloud-admin'
    --data-dir '/data'
    --database 'mysql'
    --database-host 'localhost'
    --database-name 'nextcloud'
    --database-user 'nextcloud'
    --database-pass 'linuxfabrik'

Migration Local Storage zu Object Storage

Die Anleitung zeigt den Umzug hin zu S3 oder alternativ Swift.

Bemerkung

Vorhandene Zeitangaben beziehen sich auf ein System mit knapp 90 Benutzern, etwas mehr als 2.1 Millionen Dateien und einer Gesamtgrösse von rund 4 TB.

So wird ein Block-basierter Nextcloud-Storage auf einen Object Store migriert. Mit welchen Tools die Inhalte vom Local Storage auf einen Object Storage kopiert werden, spielt keine Rolle. Die Anleitung behandelt zwei Tools, aws und rclone. Das Ergebnis ist bei beiden das gleiche.

Hier wird von folgenden Voraussetzungen ausgegangen:

  • Die Datenbank heisst nextcloud.

  • Der lokale Nextcloud-Storage liegt auf /var/www/html/nextcloud/data/.

  • Der Object-Storage läuft im Beispiel bei Infomaniak im Zürcher Datacenter.

  • S3-Bucket/Swift-Container namens bucket-name ist angelegt.

S3/Swift-Storage beim Provider einrichten und Credentials notieren:

Vorbereitende Massnahmen:

# 30 minutes
chown -R apache:apache /var/www/html/nextcloud/data/
# 30 minutes
sudo -u apache /var/www/html/nextcloud/occ files:scan --all

Maintenance-Mode aktivieren (Benutzer für die Zeit der Migration aus Konsistenzgründen aussperren):

sudo -u apache php /var/www/html/nextcloud/occ maintenance:mode --on
systemctl restart php-fpm

Datenbank abfragen, um eine Liste der Dateien aller Benutzer inkl. Metadaten zu erhalten. Die beiden resultierenden SQL-Dumps belegen auf dem beschriebenen System zusammengenommen 500 MB.

mysql_user='root'

# get the user files
mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/user_file_list
    select concat('urn:oid:', fileid, ' ', '/var/www/html/nextcloud/data/', substring(id from 7), '/', path)
    from oc_filecache join oc_storages on storage = numeric_id
    where id like 'home::%'
    order by id;
EOF

# get the meta files
mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/meta_file_list
    select concat('urn:oid:', fileid, ' ', substring(id from 8), path)
    from oc_filecache join oc_storages on storage = numeric_id
    where id like 'local::%'
    order by id;
EOF

Symbolischen Link für Block-Storage-Datei auf zukünftige Datei im Object Store-Bucket anlegen:

mkdir -p /var/www/html/nextcloud/data/objectstore_files
cd /var/www/html/nextcloud/data/objectstore_files

# count the files that have to be processed
# 3 minutes
find /var/www/html/nextcloud/data/ -type f | wc -l

# 50 minutes
while read -r target source ; do
    if [ -f "$source" ] ; then
        if ! ln -s "$source" "$target"; then
            echo "Error: Failed to link $source to $target!"
        fi
    else
        echo "Error: Could not find $source!"
    fi
done < /tmp/user_file_list | tee /tmp/user_file.log

# 30 minutes
while read -r target source ; do
    if [ -f "$source" ] ; then
        if ! ln -s "$source" "$target"; then
            echo "Error: Failed to link $source to $target!"
        fi
    else
        echo "Error: Could not find $source!"
    fi
done < /tmp/meta_file_list | tee /tmp/meta_file.log
grep '^Error: ' /tmp/*.log

Am Ende ergibt dies ein Verzeichnis mit (im Beispiel etwas über 2.3 Millionen) urn:oid:<nnnnn>-Dateien.

Kopiervorgang mit aws (gegen einen S3-fähigen Object Storage)

Das Python-Programm AWS CLI per Download installieren und konfigurieren (die Version in den OS-Repos ist hoffnungslos veraltet):

cd /tmp
dnf -y install unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
./aws/install --bin-dir /usr/local/bin

mkdir /root/.aws
cat > /root/.aws/config << EOF
[default]
region = us-east-1
s3 =
    max_concurrent_requests = 100
EOF
cat > /root/.aws/credentials  << EOF
[default]
aws_access_key_id = 7ca40b8a-2572-4ff7-9a9a-57051bf33dc7
aws_secret_access_key = 51c9d8b3-25f7-45dc-812f-2146e38d2167
EOF

Dateien der Nextcloud-Benutzer hin zu S3 syncen:

cd /var/www/html/nextcloud/data/objectstore_files

# ~ 75 MiB/sec = 4.4 GiB/min = 263 GiB/h = 6.1 TiB/d
# first sync takes 14h 40min; a second sync a day later takes 1h 08min
aws s3 sync . s3://bucket-name --endpoint-url https://s3.pub1.infomaniak.cloud --color auto
Kopiervorgang mit rclone (am Beispiel eines Swift-fähigen Object Storage)
# from epel-repo
dnf -y install rclone

cd /var/www/html/nextcloud/data/objectstore_files

export RCLONE_CONFIG_MYPROJECT_TYPE=swift
export RCLONE_CONFIG_MYPROJECT_ENV_AUTH=true
source path/to/openstack.rc

# ca. 25 MB/sec
rclone sync --copy-links --progress . myproject:mybucket

Danach:

cd
rm -rf /var/www/html/nextcloud/data/objectstore_files

Ging es bisher nur um den Sync, wird es ab hier ernst und die bestehende Nextcloud-Instanz tatsächlich geändert. Die einzelnen Schritte sollten vor der Ausführung unbedingt genau geprüft werden. Deshalb: Datenbank-Backup bereithalten.

S3-Backend

Datenbank aktualisieren und die Storage-Einträge auf den S3 zeigen lassen (im Beispiel auf den Bucket „bucket-name“):

cat > /tmp/update-database.sql << 'EOF'
update oc_storages
set id = concat('object::user:', substring(id from 7))
where id like 'home::%';

update oc_storages
set id = 'object::store:amazon::bucket-name'
where id like 'local::%';

update oc_mounts
set mount_provider_class = 'OC\\Files\\Mount\\ObjectHomeMountProvider'
where mount_provider_class = 'OC\\Files\\Mount\\LocalHomeMountProvider';
EOF
# a few milliseconds
mysql --user root --password --database nextcloud < /tmp/update-database.sql

Nextcloud-Konfiguration im Storage-Bereich auf Object Storage umkonfigurieren:

cd /var/www/html/nextcloud/
sudo -u apache php /var/www/html/nextcloud/occ maintenance:mode --off
systemctl restart php-fpm
sudo -u apache php occ config:system:set objectstore class --value=OC\\Files\\ObjectStore\\S3
sudo -u apache php occ config:system:set objectstore arguments autocreate --type=boolean --value=true
sudo -u apache php occ config:system:set objectstore arguments bucket --value=bucket-name
sudo -u apache php occ config:system:set objectstore arguments hostname --value=s3.pub1.infomaniak.cloud
sudo -u apache php occ config:system:set objectstore arguments key --value=7ca40b8a-2572-4ff7-9a9a-57051bf33dc7
sudo -u apache php occ config:system:set objectstore arguments port --type=integer --value=443
sudo -u apache php occ config:system:set objectstore arguments region --value=us-east-1
sudo -u apache php occ config:system:set objectstore arguments secret --value=51c9d8b3-25f7-45dc-812f-2146e38d2167
sudo -u apache php occ config:system:set objectstore arguments use_path_style --type=boolean --value=true
sudo -u apache php occ config:system:set objectstore arguments use_ssl --type=boolean --value=true

Inspiration war https://github.com/nextcloud/server/issues/25781.

Swift-Backend

Datenbank aktualisieren und die Storage-Einträge auf den Swift zeigen lassen (im Beispiel auf den Bucket „bucket-name“):

cat > /tmp/update-database.sql << 'EOF'
update oc_storages
set id = concat('object::user:', substring(id from 7))
where id like 'home::%';

update oc_storages
set id = 'object::store:bucket-name'
where id like 'local::%';

update oc_mounts
set mount_provider_class = 'OC\\Files\\Mount\\ObjectHomeMountProvider'
where mount_provider_class = 'OC\\Files\\Mount\\LocalHomeMountProvider';
EOF
# a few milliseconds
mysql --user root --password --database nextcloud < /tmp/update-database.sql

Nextcloud-Konfiguration im Storage-Bereich auf Object Storage umkonfigurieren:

cd /var/www/html/nextcloud/
sudo -u apache php /var/www/html/nextcloud/occ maintenance:mode --off
systemctl restart php-fpm
sudo -u apache php occ config:system:set objectstore class --value=OC\\Files\\ObjectStore\\Swift
sudo -u apache php occ config:system:set objectstore arguments autocreate --type=boolean --value=true
sudo -u apache php occ config:system:set objectstore arguments bucket --value=bucket-name
sudo -u apache php occ config:system:set objectstore arguments region --value=dc3-a
sudo -u apache php occ config:system:set objectstore arguments scope project domain name --value=Default
sudo -u apache php occ config:system:set objectstore arguments scope project name --value=cabfeec2-ee88-4fba-8197-5bcae1ac96eb
sudo -u apache php occ config:system:set objectstore arguments serviceName --value=swift
sudo -u apache php occ config:system:set objectstore arguments url --value=https://api.pub1.infomaniak.cloud/identity/v3
sudo -u apache php occ config:system:set objectstore arguments user domain name --value=Default
sudo -u apache php occ config:system:set objectstore arguments user name --value=f187e2b0-7d6c-419a-b5b0-9a5743677005
sudo -u apache php occ config:system:set objectstore arguments user password --value=linuxfabrik

Siehe auch https://docs.infomaniak.cloud/documentation/04.object-storage/third-party-integration/01.Nextcloud/

rm -f path/to/openstack.rc

Zum Schluss:

systemctl restart php-fpm
sudo -u apache /var/www/html/nextcloud/occ maintenance:repair

Fertig. Testen.

Achtung

Vorschau der Bilder funktioniert nicht? Bilder lassen sich nicht laden? Verschiedene Fehler im nextcloud.log zum Thema „Encryption“ bis hin zu „Seeking“?

Schauen, ob die Datenbank-Tabelle oc_mounts wirklich aktualisiert und „LocalHomeMountProvider“ auf „ObjectHomeMountProvider“ umgestellt wurde.

Migration Object Storage zu Local Storage

Die Anleitung zeigt den Umzug von Object Storage wie S3 oder Swift hin zu Local Block Storage.

Hier wird von folgenden Voraussetzungen ausgegangen:

  • Die Datenbank heisst nextcloud.

  • Die Nextcloud-Files liegen im Object Store in einem S3-Bucket/Swift-Container namens bucket-name.

  • Der lokale Nextcloud-Storage liegt unter $nextcloud_datadir.

Vorbereitende Massnahmen:

nextcloud_datadir='/data'
chown -R apache:apache "$nextcloud_datadir"
sudo -u apache /var/www/html/nextcloud/occ files:scan --all

Maintenance-Mode aktivieren (Benutzer für die Zeit der Migration aus Konsistenzgründen aussperren):

sudo -u apache php /var/www/html/nextcloud/occ maintenance:mode --on
systemctl restart php-fpm

systemctl disable --now nextcloud-jobs.timer nextcloud-scan-files.timer

Datenbank abfragen, um eine Liste der Dateien aller Benutzer inkl. Metadaten zu erhalten.

mysql_user='root'

# get the user files
mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/user_file_mkdir_list
    select concat(substring(oc_storages.id from 14), '/', path)
    from oc_filecache
    left join oc_storages on oc_filecache.storage = oc_storages.numeric_id
    left join oc_mimetypes on oc_filecache.mimetype = oc_mimetypes.id
    where oc_storages.id like 'object::user:%'
    and oc_mimetypes.mimetype = 'httpd/unix-directory'
    order by oc_storages.id;
EOF

mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/user_file_mv_list
    select concat('urn:oid:', oc_filecache.fileid, ' ', substring(oc_storages.id from 14), '/', path)
    from oc_filecache
    left join oc_storages on oc_filecache.storage = oc_storages.numeric_id
    left join oc_mimetypes on oc_filecache.mimetype = oc_mimetypes.id
    where oc_storages.id like 'object::user:%'
    and oc_mimetypes.mimetype != 'httpd/unix-directory'
    order by oc_storages.id;
EOF

# get the meta files
mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/meta_file_mkdir_list
    select path
    from oc_filecache
    left join oc_storages on oc_filecache.storage = oc_storages.numeric_id
    left join oc_mimetypes on oc_filecache.mimetype = oc_mimetypes.id
    where oc_storages.id like 'object::store:%'
    and oc_mimetypes.mimetype = 'httpd/unix-directory'
    order by oc_storages.id;
EOF

mysql --user "$mysql_user" --password --batch --disable-column-names --database nextcloud << EOF > /tmp/meta_file_mv_list
    select concat('urn:oid:', fileid, ' ', path)
    from oc_filecache
    left join oc_storages on oc_filecache.storage = oc_storages.numeric_id
    left join oc_mimetypes on oc_filecache.mimetype = oc_mimetypes.id
    where oc_storages.id like 'object::store:%'
    and oc_mimetypes.mimetype != 'httpd/unix-directory'
    order by oc_storages.id;
EOF

Nun müssen alle Files aus dem Object Store in einen temporären Ordner heruntergeladen werden:

# from epel-repo
dnf -y install rclone

nextcloud_datadir='/data'
mkdir -p "$nextcloud_datadir/objectstore_files"
cd "$nextcloud_datadir/objectstore_files"

export RCLONE_CONFIG_MYPROJECT_TYPE=swift
export RCLONE_CONFIG_MYPROJECT_ENV_AUTH=true
source path/to/openstack.rc

rclone sync --progress myproject:mybucket .

# for example:
# Transferred:      129.321 GiB / 129.321 GiB, 100%, 120.399 KiB/s, ETA 0s
# Checks:            101520 / 101520, 100%
# Transferred:       101537 / 101537, 100%
# Elapsed time:   2h30m56.7s

Am Ende ergibt dies ein Verzeichnis mit urn:oid:<nnnnn>-Dateien. Diese müssen nun an den richtigen Ort bewegt werden:

nextcloud_datadir='/data'
cd "$nextcloud_datadir/objectstore_files"

while read -r folder ; do
    folder_path="$nextcloud_datadir/$folder"
    if ! mkdir -p "$folder_path"; then
        echo "Error: Failed to create $folder_path!"
    fi
done < /tmp/user_file_mkdir_list | tee /tmp/user_file_mkdir.log

while read -r source target ; do
    if [ -z "$target" ]; then
        echo "Error: target for $source is empty!"
        continue
    fi
    target_path="$nextcloud_datadir/$target"
    if ! \mv "$source" "$target_path"; then
        echo "Error: Failed to move $source to $target_path!"
    fi
done < /tmp/user_file_mv_list | tee /tmp/user_file_mv.log

while read -r folder ; do
    folder_path="$nextcloud_datadir/$folder"
    if ! mkdir -p "$folder_path"; then
        echo "Error: Failed to create $folder_path!"
    fi
done < /tmp/meta_file_mkdir_list | tee /tmp/meta_file_mkdir.log

while read -r source target ; do
    if [ -z "$target" ]; then
        echo "Error: target for $source is empty!"
        continue
    fi
    target_path="$nextcloud_datadir/$target"
    if ! \mv "$source" "$target_path"; then
        echo "Error: Failed to move $source to $target_path!"
    fi
done < /tmp/meta_file_mv_list | tee /tmp/meta_file_mv.log

grep '^Error: ' /tmp/*.log

# should be empty now
ll /data/objectstore_files/
rmdir /data/objectstore_files/

chown -R apache:apache "$nextcloud_datadir"
restorecon -Fvr "$nextcloud_datadir"

Datenbank aktualisieren und die Storage-Einträge auf den Local Storage zeigen lassen:

old_objectstore_bucket_name='mybucket'
nextcloud_datadir='/data'

cat > /tmp/update-database.sql << EOF
update oc_storages
set id = concat('home::', substring(oc_storages.id from 14))
where id like 'object::user:%';

update oc_storages
set id = 'local::$nextcloud_datadir/'
where id = 'object::store:$old_objectstore_bucket_name';

update oc_mounts
set mount_provider_class = 'OC\\\\Files\\\\Mount\\\\LocalHomeMountProvider'
where mount_provider_class = 'OC\\\\Files\\\\Mount\\\\ObjectHomeMountProvider';
EOF

# takes a few milliseconds
mysql_user='root'
mysql --user="$mysql_user" --password --database=nextcloud < /tmp/update-database.sql

# double check oc_storages.
# there should only be one entry starting with `local::`.
# if that is not the case, manually fix the database before starting the nextcloud services
mysql --user="$mysql_user" --password --database=nextcloud --execute="SELECT * FROM nextcloud.oc_storages WHERE id like 'local::%';"

Nun muss man die alte Object Storage Konfiguration entfernen und den Maintenance-Mode deaktivieren:

$EDITOR /var/www/html/nextcloud/config/config.php

cd /var/www/html/nextcloud/
sudo -u apache php /var/www/html/nextcloud/occ maintenance:mode --off
systemctl restart php-fpm

systemctl enable --now nextcloud-jobs.timer nextcloud-scan-files.timer

Fertig. Testen.

Storage: External SMB/CIFS Storage

Ein optimales Setup braucht sowohl smbclient und libsmbclient-php. smbclient wird von occ external_storage:notify benötigt, libsmbclient-php wird von Nextcloud direkt verwendet. Unter CentOS:

yum install cifs-utils php-smbclient -y

Pro External Storage muss ein occ external_storage:notify gestartet werden. Dazu verwenden wir die nc-external-files-notify Skripte.

nc-external-files-notify.timer und nc-external-files-notify.service sollten falls möglich in das Monitoring aufgenommen werden.

nc-external-files-notify.timer
activestate: active
loadstate: loaded
substate: running, waiting
unitfilestate: enabled
nc-external-files-notify.service
activestate: active, inactive
loadstate: loaded
substate: running, dead
unitfilestate: disabled

Storage: Upload grosser Dateien

Im Beispiel wird der Upload von 10 GB grossen Dateien gegen einen langsamen Storage konfiguriert. Wichtig ist, dass die Webserver-Timeouts genügend tolerant gewählt werden, um das Assembling der Datei-Chunks auf Serverseite zu ermöglichen - ansonsten erhält man Fehler der Art Error when assembling chunks. status code 504. PHP mit memory_limit unter 1 GB ergibt regelmässig OOM-Fehler. Die post_max_size spielt dagegen keine grosse Rolle - ausser dass sich in unseren Messungen die Upload-Zeit durch langsameres Assembling verlängert, je grösser die post_max_size gewählt wird.

Apache App-Server:

Timeout 600                     # Default: 60

Apache Reverse-Proxy:

ProxyTimeout 600                # same value as Timeout on Nextcloud App-Server

PHP:

max_execution_time = 3600       ; Default: 30
memory_limit = 1024M            ; Default: 128M
post_max_size = 16M             ; Default: 8M
upload_max_filesize = 10000M    ; Default: 2M

Und darauf achten, dass /tmp genügend Platz für den (parallelen) Upload solch grosser Dateien bietet.

LDAP

App LDAP user and group backend verwenden.

Tipp

Issue seit mind. 2017: wird ein LDAP-Zugang konfiguriert, werden wie erwartet die passenden Benutzer aus dem LDAP in der Nextcloud-Benutzerliste angezeigt. Später im AD/LDAP hinzugefügte Benutzer tauchen dann aber dort nicht mehr auf, obwohl sie bekannt sind und ein Login problemlos funktioniert. Möchte man jetzt die Eigenschaften der unauffindbaren/nicht gelisteten LDAP-Benutzer in Nextcloud anpassen, ruft man die Benutzerliste auf und nutzt die Nextcloud-Suche. Dabei erscheinen die bisher unsichtbaren Benutzer, und man kann wie gewohnt deren Eigenschaften wie beispielsweise deren Gruppenzugehörigkeit anpassen.

Wer sich nach wie vor mit einem lokalen Account anmelden möchte, verwendet die URL https://cloud.example.com/index.php/login?direct=1

LDAP gegen FreeIPA konfigurieren

Server:

  • User DN: uid=freeipa-reader,cn=sysaccounts,cn=etc,dc=myorg,dc=tld

  • Password: xxx

  • Base DN: dc=myorg,dc=tld

Users - nur Benutzer aus der FreeIPA-Gruppe „nextcloud“ zulassen:

  • LDAP-Query: (&(objectclass=inetorgperson)(memberof=cn=nextcloud,cn=groups,cn=accounts,dc=myorg,dc=tld))

  • Advanced > Directory Settings > User Display Name Field: uid

Login Attributes:

  • LDAP / AD Username: yes

  • LDAP / AD Email Address: yes

Test einer LDAP-Query mit ldapsearch:

dnf -y install openldap-clients
# the single quotes are important
ldapsearch -v \
    -H ldap://freeipa \
    -D 'uid=freeipa-reader,cn=sysaccounts,cn=etc,dc=myorg,dc=tld' \
    -W -b \
    'DC=myorg,DC=tld' \
    '(&(objectClass=inetorgperson)(memberOf=cn=nextcloud,cn=groups,cn=accounts,dc=myorg,dc=tld))'

Umgang mit OCC und LDAP auf der Kommandozeile:

ldap:check-user          checks whether a user exists on LDAP.
ldap:create-empty-config creates an empty LDAP configuration
ldap:delete-config       deletes an existing LDAP configuration
ldap:search              executes a user or group search
ldap:set-config          modifies an LDAP configuration
ldap:show-config         shows the LDAP configuration
ldap:show-remnants       shows which users are not available on LDAP anymore, but have remnants in Nextcloud.
ldap:test-config         tests an LDAP configuration
sudo -u apache /var/www/html/nextcloud/occ ldap:check-user "Markus Frei"
# user exists? then:
sudo -u apache /var/www/html/nextcloud/occ ldap:search "Markus Frei"

Um „remnant“-Benutzer zu löschen, regelmässig sudo -u apache php /var/www/html/nextcloud/occ user:delete <nextcloud name> (erste Spalte aus show-remnants) verwenden. Achtung: Man muss vor dem Löschen die Dateien einem anderen Benutzer zuweisen, sonst werden die Dateien des LDAP-Benutzers gelöscht.

Keycloak (oAuth, SAML)

Nextcloud-Seite

Es gibt drei Möglichkeiten - in unseren Tests hat Stand 2021-03 keine davon funktioniert:

  1. Nextcloud und Keycloak per SAML über die App SSO & SAML authentication verbinden (schon wegen SAML und dem Zertifikatshandling nicht zu empfehlen)

  2. Nextcloud und Keycloak per OpenID-Connect über die App Social Login verbinden. Unterstützt Roles, aber keine benutzerspezifischen Attribute.

  3. Nextcloud und Keycloak per OpenID-Connect über die App OpenID Connect Login verbinden. Unterstützt eigene User-Attribute wie „Quota“, aber keine Roles. Ist zudem nur über die config.php administrierbar.

Für die Nextcloud-App Social Login:

Damit sich Nextcloud über Keycloak authentifizieren kann, wird im Keycloak-Realm ein Client (hier „nextcloud“) und in Nextcloud die App „Social Login“ benötigt, womit OpenID-Connect/OAuth verwendet wird. Wer SAML bevorzugt, muss die App „SSO & SAML authentication“ installieren.

Konfiguration der „Social Login“-App:

  • [x] Prevent creating an account if the email address exists in another account

  • [x] Update user profile every login

  • [x] Restrict login for users without mapped groups

  • > Save

„Custom OpenID Connect“-Button klicken.

„Add group mapping“ anklicken.

  • users / users

Save.

Benutzer wählen > Role Mappings > Client Roles: nextcloud > Rolle zuweisen.

Siehe auch

Für die Nextcloud-App OpenID Connect Login

Siehe

Keycloak-Seite

Die Keycloak-Konfiguration hängt von der verwendeten Nextcloud-App ab.

Für die Nextcloud-App Social Login:

Neuer Client „nextcloud“ mit „openid-connect“ und Root URL „https://nextcloud.example.com“.

  • Clients > „nextcloud“ > Settings

    • Client ID: nextcloud

    • Client Protocol: openid-connect

    • Access Type: confidential

  • Clients > „nextcloud“ > Roles > Add Role

    • Role name: users

  • Clients > „nextcloud“ > Mappers > Add Builtin

    • „client roles“ auswählen > Add selected

  • Clients > „nextcloud“ > Mappers > „client roles“

    • Client ID: nextcloud

    • Token Claim Name: roles

    • Add to userinfo: ON

  • Clients > „nextcloud“ > Scope

    • Full Scope Allowed: OFF (This will ensure that only the client roles are stored in the token)

Sollen keine kryptische Benutzernamen in der Form „04f21d18-6176-11eb-a904-525400b49b22“ erzeugt werden, muss das sub-Property der userinfo per Mapper überschrieben werden:

  • Clients > „nextcloud“ > Mappers > Create

    • Name: sub

    • Mapper Type: User Prooperty

    • Property: username

    • Token Claim Name: sub

Siehe auch https://janikvonrotz.ch/2020/10/20/openid-connect-with-nextcloud-and-keycloak/.

Encryption in Nextcloud

Nextcloud bietet mehrere Verschlüsselungsebenen.

Transportverschlüsselung (TLS)

Schützt vor Lauschangriffen. Schützt nicht vor kompromittierten Geräten oder Servern.

Server Side Encryption (SSE) - Daten beim Speichern verschlüsseln

Optional. Jede Datei wird vor dem Speichern mit einem eindeutigen Schlüssel verschlüsselt - entweder serverweit (aus Funktions- und Leistungsgründen der Standard), oder einem Schlüssel pro Benutzer. SSE verschlüsselt nur den Inhalt der Dateien, nicht aber deren Namen oder Ordnerstruktur.

Server Side Encryption wurde ursprünglich eingeführt, um External Storage wie Dropbox und dergleichen sicher anbinden zu können: Nextcloud sorgt dann dafür, dass alle Daten ausserhalb des eigenen Standorts verschlüsselt gespeichert werden.

Der serverweite Schlüssel (technisch ein Passwort zum Entschlüsseln der Dateien) wird mit Hilfe des Secrets in der config.php verschlüsselt im Nextcloud-Datenverzeichnis abgelegt (und lässt sich im Desaster-Fall wiederherstellen).

Benutzerschlüssel liegen in den Datenverzeichnissen der einzelnen Benutzer und werden mit deren Benutzerkennwort verschlüsselt. Wird dieses Feature aktiviert, ist der Einsatz eines Online-Office (Collabora, OnlyOffice) nicht mehr möglich.

SSE schützt Dateien, solange die Dateiablage nicht auf dem gleichen System wie Nextcloud selbst liegt - eine auf ein lokales Datenverzeichnis angwendete SSE bietet wenn überhaupt nur geringen Schutz, schliesslich wird hier der Schlüssel zusammen mit den Daten gespeichert.

Stand 2021-04 „lügt“ die Web-Oberfläche noch: ist „Serverseitige Verschlüsselung“ aktiviert, meldet das eigene Benutzerprofil unter „Datenschutz“ ein „Deine Dateien sind mittels der serverseitigen Verschlüsselung verschlüsselt“ - egal ob „Benutzerverzeichnis verschlüsseln“ aktiviert wurde.

Fazit:

  • SSE macht Sinn, wenn die App External Storage angeboten wird, oder das Datenverzeichnis nicht auf dem gleichen System wie die Nextcloud-Installation selbst liegt.

  • Die Anwendung von SSE auf ein lokales Datenverzeichnis bringt keinen Sicherheitsvorteil (trügerische Sicherheit, die durch Meldungen unter „Datenschutz“ noch verstärkt werden).

  • Sollen Dateiinhalte vor dem Serverbetreiber verborgen werden, muss End-to-End-Encryption (E2E) zum Einsatz kommen.

End-to-End-Encryption (E2E, E2EE - Client-basierte Verschlüsselung)

Der ultimative Schutz der Benutzerdaten wird durch die Ende-zu-Ende-Verschlüsselung gewährleistet. Nur hier hat der Server niemals Zugriff auf Schlüssel oder unverschlüsselte Dateien. Der Sync-Client verschlüsselt die Daten vor dem Senden innerhalb des E2E-fähigen Ordners (jedoch nicht in darunterliegenden Ordnern).

Das geht nicht ohne Funktionsverlust: die Nextcloud-Weboberfläche kann die verschlüsselten Dateien nicht einsehen, der Sync-Client ist damit Pflicht. Eine öffentliche Freigabe oder Freigabe an Gruppen ist nicht möglich, die Suchmaschine kennt deren Inhalte nicht, und Collaboration per Online-Office funktioniert nicht.

E2E lässt sich für einen oder mehrere Ordner aktivieren. Der Inhalt jedes dieser Ordner wird vollständig vom Server ausgeblendet, einschließlich Dateinamen und Verzeichnisstruktur. Um die Daten mit anderen Geräten zu synchronisieren, müssen Benutzer eine vom ersten Gerät erstellte Mnemonic-Passphrase nutzen. Sobald dies erledigt ist, werden durchgängig verschlüsselte Ordner nahtlos zwischen Geräten synchronisiert. Nextcloud kann beispielsweise auch so konfiguriert werden, dass alle von der Finanzabteilung erstellten Dateien automatisch verschlüsselt werden.

Tipp

Die „Mnemonic-Passphrase“ ist ein aus 12 zufälligen, englischen Wörtern bestehendes Passwort. Die Mnemonic-Passphrase wird zur Verschlüsselung der Dateien verwendet - daher diese UNBEDINGT notieren und in einem Passwort-Manager speichern. Sollte man mehrere Nextcloud-Clients nutzen, wird man nach dieser Passphrase gefragt. Man kann sich die Passphrase im Client unter „Account > Show E2E mnemonic“ nochmals anzeigen lassen. Es wird ein Client >= v2.5.0 benötigt.

Kein Admin dieser Welt kann helfen, falls man die Passphrase vergessen oder nicht notiert hat.

So erhält man die Mnemonic-Passphrase:

  1. Nextcloud-Client in der neuesten Version installieren oder updaten (>= 2.5.0).

  2. Nextcloud-Client (erneut) mit dem Server verbinden. Es erscheint ein Popup mit der „Mnemonic Passphrase“. Passphrase notieren.

So verschlüsselt man einen Ordner:

  1. Einen neuen, leeren Ordner im lokalen Nextcloud-Verzeichnis anlegen.

  2. Nextcloud-Client öffnen, rechte Maustaste auf den Ordner und „Encrypt“ auswählen.

Das war’s. Dateien, die in diesen Ordnern landen, werden nur noch verschlüsselt auf einen Nextcloud-Server hochgeladen - uneinsehbar für den Rest der Welt. Es können beliebig viele Ordner verschlüsselt werden.

Das ist mit verschlüsselten Ordnern nicht mehr möglich:

  • Zugriff per Web-Interface

  • Freigabe an eine Gruppe

  • Freigabe auf Dateiebene

  • Serverseitige Suche oder Vorschau

  • Serverseitiger Papierkorb und Versionierung

E2E ist seit 2020-08 (NC19+) produktiv einsetzbar, davor galt E2E lange Jahre als „experimental“.

Die End-to-End-Verschlüsselung in Nextcloud schützt Benutzerdaten vor jedem Angriffsszenario, selbst im Falle einer unentdeckten Sicherheitslücke oder vor (nicht vertrauenswürdigen) Serverbetreibern.

Die Daten auf den Benutzergeräten selbst werden nicht geschützt, und der Diebstahl eines unverschlüsselten, entsperrten Benutzergeräts würde es einem Angreifer ermöglichen, auf private Schlüssel zuzugreifen.

Fazit:

  • E2E hat einen sehr spezifischen Anwendungsfall, die Einschränkungen sind nur bei sensiblen Daten sinnvoll.

  • Verliert ein Benutzer seine Mnemonic-Passphrase, sind auch die damit verschlüsselten Daten unweigerlich verloren.

Der gleichzeitige Betrieb von SSE mit lokal verschlüsseltem Benutzerverzeichnis und E2E ist zwar möglich, wird aber nicht unterstützt - beides zusammen wird zu technischen Problemen führen. Und egal welche der beiden Methoden zum Einsatz kommt: verschlüsselt werden nur Dateien. Kalender, ToDo-Listen und andere Daten werden nicht verschlüsselt, da es beispielsweise im Bereich CalDAV dafür an Clients fehlt, die dies unterstützen.

Hier finden sich noch einige Details: https://nextcloud.com/blog/encryption-in-nextcloud/.

Collabora und ODF vs OnlyOffice und OOXML

ODF vs OOXML

ODF ist das leichgewichtigere, offenere und von LibreOffice präferierte Format für Dokumente aller Art. Die Spezifikation für ODF 1.2 umfasst ca. 1’000 Seiten (ISO 26300). ODF-Spreadsheets werden beispielsweise als .ods gespeichert, Textdokumente als .odt. Einige Microsoft-Office-Funktionen lassen sich mit ODF nicht umsetzen, weshalb es durch die Redmonder nicht unterstützt wird.

Wenn die Microsoft-eigenen, mit Binärcode gespickten, XML-basierten Office-Formate .docx & Co. nicht zum Einsatz kommen, ist Office Open XML (OOXML) das von Microsoft Office präferierte Dokumentenformat, welches 2008 als ISO-Norm 29500 verabschiedet wurde und einen Umfang von ca. 6’000 Seiten aufweist. Es ist daher nicht vollständig implementiert; der Umfang ist hauptsächlich der MS-Office Rückwärtskompatibilität geschuldet.

Collabora vs OnlyOffice

Collabora ist quasi eine auf dem Server ausgeführte LibreOffice-Installation, für die der Browser mehr oder weniger nur das GUI anzeigt. Collabora orientiert sich bei Look & Feel also an LibreOffice und verarbeitet die Dokumente auf dem Server.

OnlyOffice hat den gegenteiligen Ansatz - es orientiert sich vom Aussehen her an MS-Office und verlagert das Document Processing auf den Client. Deren Document-Server stellt den Speicherplatz zur Verfügung und ist hier mehr für die kollaborative Kommunikation zuständig, der Grossteil der Applikation läuft dagegen im Browser.

In Collabora lässt sich bei der Erstellung einer Datei das Dateiformat frei wählen (ODF vs. OOXML), ODF wird aber wie bei LibreOffice klar präferiert. OnlyOffice bevorzugt dagegen Dokumente im OOXML-Format. Siehe https://www.onlyoffice.com/blog/2018/09/onlyffice-makes-it-easier-to-work-with-odf-in-integrated-solutions/.

OnlyOffice beherrscht das Freezing von Zeilen und Spalten, was für Collabora erst ab v6 angekündigt ist.

In einer Nextcloud-Instanz lassen sich beide Office-Suiten parallel nutzen. In diesem Fall werden Dokumente On-Click sofort in Collabora geöffnet; im Web-GUI ist OnlyOffice per Kontext-Menu (3 Punkte) verfügbar. In der Smartphone-App von Nextcloud ist das nicht der Fall, das Dokument kann nur mit OnlyOffice geöffnet werden.

Kommt in Nextcloud ausschliesslich Collabora zum Einsatz, wird es als Standard-Editor für sämtliche durch Collabora unterstützen Dateiformate benutzt. Bei OnlyOffice lässt sich in den Einstellung anpassen, welche Formate damit geöffnet werden sollen; per Kontext-Menu lassen sich damit trotzdem abgewählte Formate öffnen.

Ab Nextcloud Hub v18 wird zum ersten Mal OnlyOffice während der Nextcloud-Installation on demand installiert und integriert. OnlyOffice findet sich nicht im Nextcloud tarball/zip. Collabora wird (wie bis dato auch) per separater App angeboten oder dediziert und losgelöst von Nextcloud installiert. Ein klares Bekenntnis der Nextcloud-Macher zu OnlyOffice also. Für Verwirrung sorgte dann die Pressemitteilung von Collabora auf https://www.collaboraoffice.com/press-releases/collabora-online-as-default-in-nextcloud-hub/, dass ab Nextcloud Hub v19 Collabora das neue Default Web-Office-Paket ist.

Wohl auch durch die neu gewonnene Popularität hat OnlyOffice im Nextcloud-Umfeld recht schnell mit einigen „Known Issues“ auf sich aufmerksam gemacht, die einen negativen Einfluss auf die Funktion haben, siehe zum Beispiel https://github.com/nextcloud/documentserver_community/issues/100 und https://api.onlyoffice.com/editors/nextcloud#issue.

Die Features in der Übersicht:

Produkt

Preis

Edit auf Mobile

max. gleichz. geöffnete Dokumente

max. gleichz. Verbindungen

max. Benutzer

Collabora CODE

kostenlos

ja

10

20

k.A.

Collabora for SMBs

17€/User/Jahr

ja

k.A.

k.A.

99

Collabora for Enterprises

k.A.

ja

k.A.

k.A.

k.A.

OnlyOffice CE

kostenlos

view only

k.A.

20

bis 20

OnlyOffice Integration Edition Start

35€ pro Server-Lifetime, 1 Jahr Support & Updates

ja

k.A.

50

bis 50

OnlyOffice Integration Edition Standard

1870€ pro Server-Lifetime, 1 Jahr Support & Updates

ja

k.A.

100

bis 100

OnlyOffice Integration Edition Standard+

3740€ pro Server-Lifetime, 1 Jahr Support & Updates

ja

k.A.

200

bis 400

Siehe auch:

DB: Informationen zu Benutzern auslesen

Liste der E-Mail-Adressen aller Benutzer erhalten:

select json_extract(data, '$.email.value')
from oc_accounts;

Alle deaktivierten Benutzer auflisten (gilt nicht für LDAP):

select userid
from oc_preferences as p
left join oc_users as u on p.userid = u.uid
where p.appid = "core"
and p.configkey = "enabled"
and p.configvalue = "false"

E-Mail-Adressen aller aktivierten Benutzer auflisten (gilt nicht für LDAP):

select json_extract(data, '$.email.value')
from oc_accounts
where uid not in (
    select userid
    from oc_preferences as p
    left join oc_users as u on p.userid = u.uid
    where p.appid = "core"
    and p.configkey = "enabled"
    and p.configvalue = "false"
)

Wieviel Storage hat der Benutzer beansprucht, inkl. versionierten Dateien und Trash?

select s.id, size
from `oc_filecache` as f
inner join `oc_storages` as s on f.storage = s.numeric_id
where s.id = 'object::user:admin'
and f.parent = -1

Wieviel Storage hat der Benutzer ohne versionierte Dateien und ohne Trash beansprucht?

select s.id, size
from `oc_filecache` as f
inner join `oc_storages` as s on f.storage = s.numeric_id
where s.id = 'object::user:admin'
and path = 'files'

DB: Informationen zum Server auslesen

# databaseVersion
SELECT VERSION() AS version;

# databasePerfData
SHOW variables;
SHOW global status;

# databaseSize
SHOW databases;
SHOW TABLE STATUS FROM `databaseName`;
USE `databaseName`;

# getNumberOfActiveUsers in the last 5 minutes
SELECT count(*) as num_entries FROM oc_authtoken WHERE last_activity > UNIX_TIMESTAMP() - 300;

# countUserEntries
SELECT COUNT(*) as num_entries FROM oc_preferences WHERE configkey= "lastLogin";

# countHomeStorages
SELECT COUNT(*) as num_entries FROM oc_storages WHERE id like 'home::%';

# countLocalStorages
SELECT COUNT(*) as num_entries FROM oc_storages WHERE id like 'local::%';

# countOtherStorages
SELECT COUNT(*) as num_entries FROM oc_storages WHERE id not like 'home::%' and id not like 'local::%';

# getShareStatistics
SELECT COUNT(*) as num_entries, permissions, share_type FROM oc_share WHERE 1 GROUP BY permissions, share_type;

Nextcloud API

Das API verlangt, dass User-Credentials Base64-encoded werden. In python beispielsweise so:

credentials = base64.b64encode('api-user:api-password')
header = {
    'Authorization' : "Basic %s" % credentials,
    'OCS-APIRequest': 'true',
}

Grundlegende Statistiken ermitteln:

curl \
    -H 'Authorization: Basic bmV4dGNsbp3d0NIVQ==' \
    -H 'OCS-APIRequest: true' \
    http://localhost/nextcloud/ocs/v2.php/apps/serverinfo/api/v1/info?format=json

Nextcloud Security Scan

Der externe Dienst https://scan.nextcloud.com sucht nach folgenden Dateien, um die Daten zur Nextcloud-Instanz abzurufen:

  • /nextcloud/status.php

  • /oc-shib/status.php

  • /oc/status.php

  • /owncloud/status.php

  • /status.php

Auf der Kommandozeile geht das ganze so:

# in "data", set what to scan
curl --silent --request POST \
    --header 'Content-type: application/x-www-form-urlencoded' \
    --header 'X-CSRF: true' \
    --data 'url: cloud3.linuxfabrik.ch' \
    https://scan.nextcloud.com/api/queue | jq

# search for the returned Nextcloud UUID
# e.g. 558eccce-8aba-4717-850b-28d47cb84a16

curl --silent https://scan.nextcloud.com/api/result/558eccce-8aba-4717-850b-28d47cb84a16 | jq

Nextcloud-Client

Was fragt der Nextcloud-Client (User-Agent „mirall“, nutzt HTTP/1.1) in welcher Reihenfolge mit welcher Methode ab?

# first contact - if this fails, the client won't work at all
GET /status.php

# after that and round about every 30 seconds at least this:
PROPFIND /remote.php/dav/files/username/
GET /ocs/v1.php/cloud/user?format=json
GET /remote.php/dav/avatars/username/128.png

Troubleshooting

Your web server is not properly set up to resolve „/.well-known/nodeinfo“ / Your web server is not properly set up to resolve „/.well-known/webfinger“.

404 muss durch Nextcloud gehandhabt werden, nicht durch den Webserver. Falls Nextcloud hinter einem Apache Reverse Proxy läuft, die Direktive ProxyErrorOverride Off verwenden.

Um einen Nextcloud Background-Job manuell zu starten, kann man in der oc_jobs-DB-Tabelle last_run und last_checked auf 0 setzen.

Built on 2024-04-18