Conversation
nextcloud-ldap-show-remnants.timer pointed to nextcloud-app-update.service, so the monthly report never ran and the apps were updated once more instead.
…_datadir mkdir, restorecon and the httpd_sys_rw_content_t fcontext were hardcoded to /data, so a different nextcloud__datadir got neither.
…irs in nextcloud-update nextcloud-app-update already ran db:add-missing-primary-keys, the server update did not. maintenance:repair --include-expensive applies the mimetype migrations that `occ setupchecks` reports after an upgrade, since Nextcloud leaves them to the administrator.
--database-pass and --admin-pass were readable in the process list. Without
them occ maintenance:install asks for the database password first and the
admin password second, one line of stdin each when there is no TTY (verified
with Nextcloud 35.0.0 on Rocky 10, PHP 8.3 and 8.5). The stdin is a block
scalar: '{{ a ~ "\n" ~ b }}' in single quotes reaches the command as a
literal backslash-n with ansible-core 2.16.
The script carries the credentials of the Icinga API user and was deployed with 0755. Only root can run it anyway, since it restarts services and switches to the web server user.
The role set mail_smtpstreamoptions ssl allow_self_signed=true, verify_peer=false and verify_peer_name=false on every host. The entries are now state: absent, so existing hosts get them removed; the empty 'ssl' array left behind is a no-op, since the Mailer merges the options into its own (verified against Nextcloud 35).
…xtcloud__app_configs The force and state subkeys of nextcloud__apps were listed under nextcloud__app_configs, and nextcloud__apps claimed present/absent with a default of present, while the task defaults to enabled and the module knows four states. Also drop the trailing spaces from the keys of the reverse proxy example.
…ud forbids anyway '\n', '\r' and '\u0000' in single quotes reached Nextcloud as literal backslash sequences, not as control characters. Nextcloud always forbids the backslash and the characters 0 to 31 (lib/private/Files/FilenameValidator.php, OCP\Constants::FILENAME_INVALID_CHARS), so the entries never had an effect. '|' moves up to index 6 and indexes 7 to 9 are removed from existing hosts.
The filter is the one from the Nextcloud hardening guide. Verified with
fail2ban-regex 1.1.0 on Rocky 10 against the log of Nextcloud 35.0.0: failed
logins via web, WebDAV and OCS, disabled accounts, failed two-factor
challenges, IPv4 and IPv6. The trusted domain error is logged at info level
and only matches with loglevel 1 or lower. The regular expressions contain
'{%', so the template renders them raw.
fail2ban_t may read files of the logfile attribute, but not httpd_sys_rw_content_t in the data directory: the jail found no log file until fail2ban_t was made permissive (fail2ban 1.1.0 with fail2ban-selinux, selinux-policy 42.1.18 on Rocky 10). /var/log/nextcloud is labeled httpd_log_t, where httpd_t may create and append, and the log file is created by the role, since Nextcloud silently falls back to the data directory if it cannot create it itself. Rotation runs in nextcloud-jobs.service, outside httpd_t.
…he trusted proxies fail2ban belongs on hosts that clients reach directly, so it stays off by default (setup_nextcloud__skip_fail2ban). When enabled, the playbook runs it after nextcloud, since fail2ban does not start while the log of a jail is missing, and injects the nextcloud jail. The addresses from the trusted_proxies entries of nextcloud__sysconfig go to the new fail2ban__jail_default_ignoreip__dependent_var, so no jail bans the proxy.
…alled Nextcloud The requirements per major version come from the server source at the release tags v30.0.0 to v35.0.0: the PHP range from lib/versioncheck.php, outside of which Nextcloud refuses to run, and MIN_MARIADB from apps/settings/lib/SetupChecks/SupportedDatabase.php. The major version is read from version.php of the unpacked code, since nextcloud__version may just say 'latest'. Nextcloud sets no minimum for the Redis or Valkey server, only phpredis >= 4.0.0.
Installing the policy build dependencies of the selinux role can update the SELinux policy, and a daemon started under the older policy keeps its domain until it is restarted. On a Rocky 10.0 host (selinux-policy 40.13.26), the first start of Valkey labeled /usr/bin/valkey-server bin_t and ran it in unconfined_service_t; the later update to 42.1.18 relabeled the binary to redis_exec_t, but the process stayed unconfined, and httpd_t may not connect to its socket there. setup_basic already runs selinux right after policycoreutils.
…cket nextcloud__redis_unixsocket and nextcloud__redis_group default to Valkey on RHEL 10 and Redis elsewhere, as setup_nextcloud installs them. The web server user joins the group; SELinux allows httpd_t to connect to redis_t and to write redis_var_run_t sockets without a boolean (selinux-policy 42.1.18 on Rocky 10). 'redis port' is removed instead of set to 0: RedisFactory picks 6379 for a host name and the socket for a path when the port is missing, so removing the port first and changing the host second keeps every step valid. Setting the host to the socket path first left port 6379 next to it, after which every occ call failed at bootstrap, including the one meant to fix the port (reproduced on Rocky 10 with Nextcloud 35.0.0).
…ange Both PHP-FPM restarts ran on every run, and occ background:cron had no changed_when. restorecon now runs without --force, which reset the SELinux user of the files the installer and Nextcloud created since the last run; the targeted policy ignores the SELinux user. Apps installed during the run get a restorecon of their own, since the notify_push binary needs bin_t and the notify_push block that relabels it is skipped with nextcloud__skip_notify_push.
Nextcloud writes config.php at the start of maintenance:install, so a failed attempt made `creates: config.php` skip the installer on every later run. The role asks `occ status` instead: it reports installed=false for an empty config.php (the notice goes to stderr), and the installer may overwrite the config as long as config/CAN_INSTALL from the tarball exists, which only a successful installation removes (lib/private/Config.php, verified with Nextcloud 35.0.0).
… list nextcloud-update passed the credentials to curl with --user. They now reach curl as a header file read from a pipe that the printf builtin fills (--header @file since curl 7.55, verified with curl 7.61.1 on Rocky 8).
CONTRIBUTING: software versions must always be mandatory variables. The variable only picks the tarball of a new installation; updates of an installed Nextcloud run through nextcloud-update. The Molecule scenario pins 'latest-35'.
The role handed the database administrator to occ maintenance:install, which then created 'oc_<admin>'@'%' (lib/private/Setup/MySQL.php). setup_nextcloud now creates the database and nextcloud__database_login@localhost through mariadb_server, with the charset, collation and privileges the installer would use, and the installer gets that user. Without the privilege to read mysql.user, createSpecificUser() falls back to the provided credentials, so config.php holds nextcloud__database_login and no 'oc_' user is created (verified with Nextcloud 35.0.0 and MariaDB 11.4 on Rocky 10). nextcloud__mariadb_login is gone; the version check queries MariaDB as the new user as well.
The playbook installs Collabora on the Nextcloud host but leaves the reverse proxy and the richdocuments settings to the inventory. The walkthrough follows the setup in production: a hostname of its own for Collabora on the proxy, forwarded to port 9980, and the proxy in wopi_allowlist.
The role keeps all Nextcloud caches (local, distributed, locking) in Redis or Valkey, so APCu was installed but never used. Hosts that already have it keep it: an inventory that sets memcache.local to APCu would break if the role removed the package. Verified on Rocky 10: without php-pecl-apcu the role reports no change and the Molecule verify passes.
The module list now names every module the role used to install with the reason it stays or goes. present: what Nextcloud 35 requires or recommends (OC_Util::checkServer(), SetupChecks/PhpModules.php), redis, ldap for user_ldap, smbclient for SMB external storage, opcache and imagick, which have setup checks of their own. absent: apcu and memcached (all caches live in Redis / Valkey), bcmath (gmp covers WebAuthn and SFTP) and imap (only the IMAP backend of user_external). php-json is dropped from the list instead of set absent, since on RHEL it is only a name that php-common provides. Verified on Rocky 10 with Remi PHP 8.5: the first run removes the four packages, the second reports no change, and the Molecule verify passes.
CONTRIBUTING: no upstream version numbers in texts that stay in the repo, they age with the next release. The role checks the requirements itself and names the versions it expects in the error message, so the README points at that instead of repeating "10.11+ for Nextcloud 35". The CHANGELOG entries outside Breaking Changes are one sentence again.
The version check connected through /var/lib/mysql/mysql.sock on every platform, which does not exist on Debian and Ubuntu (/run/mysqld/mysqld.sock). Take the path from vars/<os>.yml, as icingaweb2 does.
…version description fail2ban moves to the roles that setup_nextcloud leaves off by default. The Troubleshooting entries use the bold heading of roles/example and get two blank lines above the section. argument_specs and the task comment no longer offer 'latest' for nextcloud__version, matching the README.
… in setup_nextcloud Stat the socket Nextcloud uses (mode 0770, group redis or valkey) and check that the config file keeps the <server>:root ownership and 0640 that the package's tmpfiles.d rule restores at every boot. This covers the redis role on Rocky 8 and 9 and valkey on Rocky 10. Also re-wraps one comment.
cf5368e to
77eefb1
Compare
|
This PR was too large to review in one go, so two parts that are not specific to Nextcloud now have their own PRs:
The collabora commit is dropped:
For the review, in this order:
|
Must fixThe As it stands, the jail protects the login form only, while the filter comment, the fail2ban README and the CHANGELOG promise WebDAV and the OCS API as well. Options, roughly in order of preference:
Same backup problem as in #420. MoleculeRocky 10, Nextcloud 35.0.1, ansible-core 2.16: |
Follows the Nextcloud hardening guide and brings the
nextcloudrole up to the state thewordpressrole reached, after comparing a hand-built production installation against whatsetup_nextclouddeploys.What changes
Redis / Valkey over a Unix socket. Nextcloud talked to the cache over TCP on the loopback. The
redisandvalkeyroles now open a socket at0770owned by their own group, thenextcloudrole adds the web server user to that group, andsetup_nextcloudconfigures SELinux before the services, since on RHEL 10 the shipped policy does not know Valkey yet and the daemon would otherwise keep running unconfined.A dedicated database user. The installer received the database administrator and created an
oc_user that could connect from any host.nextcloud__database_loginis now mandatory andsetup_nextcloudcreates that user with access fromlocalhostonly. Existing installations keep their user.Requirements are checked before anything is touched. The role reads the installed Nextcloud, PHP and MariaDB versions and aborts with a message naming what to upgrade. The table covers Nextcloud 30 to 35.
The PHP module list matches what Nextcloud actually loads. APCu, bcmath, IMAP and memcached are removed: every cache lives in Redis or Valkey, and the remaining entries carry a comment saying why they are there. Checked against
OC_Util::checkServer()andapps/settings/lib/SetupChecks/PhpModules.php.Credentials no longer sit where local users can read them. Passwords reach
occon stdin instead of the command line, the Icinga API credentials move out of/etc/profile.dinto a root-only script, andborg-backup,do-reboot,schedule-icinga-downtimeandnextcloud-updateare0700. Theborg_localdowntime request also sent malformed JSON, which Icinga rejected with a 400.fail2ban, opt-in. Behind a reverse proxy a ban would hit the proxy, so
setup_nextcloud__skip_fail2bandefaults totrue. Where it is switched on, the jail readsX-Forwarded-Forand the proxies listed in the Nextcloud settingtrusted_proxiesare never banned. The log moves to/var/log/nextcloud/, because SELinux does not let fail2ban read the data directory.Smaller fixes. The SMTP certificate is verified, the LDAP remnants report runs from its own timer,
nextcloud-updateadds missing primary keys and runs the expensive repairs, the data directory gets its ownership and SELinux label, and a failed installer attempt is retried instead of being skipped forever.Breaking changes
Twelve entries under
### Breaking Changesin the CHANGELOG, each naming the variable to set to keep the previous behaviour. The ones that need action in the inventory before a run:nextcloud__versionandnextcloud__database_loginare mandatory now.Testing
Every run was made against a Rocky 10 VM with Nextcloud 35, PHP 8.5 from Remi, MariaDB and Valkey, from an empty machine to a working installation, and repeated until a second run reported no change. The Molecule scenarios for
valkey,schedule_reboot,fail2banandsetup_nextcloudwere extended to assert the socket, the config ownership, the root-only scripts and the fail2ban counting throughX-Forwarded-For.pre-commit run --all-filesis clean,ansible-lintreports only findings that predate this branch.Not tested: RHEL 8 and 9 with Redis from Remi, Debian and Ubuntu.