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.
… use The RHEL package enables /run/valkey/valkey.sock, the templates commented it out. valkey__conf_unixsocket follows the package path on RHEL and uses /run/valkey/valkey-server.sock on Debian and Ubuntu, whose package ships the socket off. valkey__conf_unixsocketperm defaults to 770: without it the mode follows the umask of the service (0755), so only valkey itself could connect. valkey__conf_unixsocket stays out of argument_specs, since its default comes from vars/<os>.yml, which is loaded after the role-entry validation.
Same as for Valkey. The Remi package for EL9 (redis 8.8) enables /run/redis/redis.sock with the mode left to the umask. Redis has no unixsocketgroup (Valkey added it in 8.0), so access goes through the redis group on both.
Derived from the valkey role, whose variables and static defaults are the same; checked with validate_argument_spec against every static default of the role.
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).
… sets The RHEL package ships `Z /etc/valkey ~0750 valkey root` in tmpfiles.d, which systemd-tmpfiles applies at every boot and on every RPM tmpfiles trigger, so the root:valkey the role set flipped back to valkey:root and the next run reported a change (verified on Rocky 10). Debian and Ubuntu keep root:valkey. With this, a second run of setup_nextcloud on a fresh Rocky 10 reports changed=0.
Same as for Valkey: the Remi package ships `Z /etc/redis ~0750 redis root` in tmpfiles.d (redis 8.8 for EL9).
… 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 template is the coolwsd.xml of coolwsd-26.04.4.1-1 with the adaptations of the 26.04.3 template carried over (experimental features, post_allow and lok_allow entries, WOPI alias groups and the settings the 26.04.3 patch already applied). An element-wise comparison against the shipped file shows exactly these differences. Upstream switched post_allow and lok_allow to CIDR notation; the entries from collabora__coolwsd_post_allow and collabora__coolwsd_lok_allow stay regular expressions, which coolwsd still accepts.
The filter had a stray quote before the second match() and the body a trailing comma, so the Icinga API answered 400 and borg-backup, which runs curl with --silent and discards the output, never set the downtime (verified against Icinga 2.14.6: 400 before, 200 now).
…l users borg-backup was deployed 0755 and passed the credentials to curl with --user. It is now 0700, since only root runs it (systemd timers), and the credentials reach curl as a header read from a pipe that the printf builtin fills.
…cal users do-reboot was deployed 0744 and passed the Icinga API credentials with --user and the Rocket.Chat webhook URL, which carries its token, as curl arguments. It is now 0700, since only schedule-reboot.service runs it, the credentials reach curl as a header from a pipe, and the URL through --config <(printf ...), verified with curl 7.61.1 on Rocky 8. The Molecule reboot scenario still sees the same Authorization header.
schedule-icinga-downtime was a shell function in /etc/profile.d/alias.sh, which every login shell reads, so every local user could read the credentials and set downtimes with them. It is now /usr/local/sbin/schedule-icinga-downtime, readable and executable by root only, which the reboot alias calls through root's PATH. The function also ran `exit 1` on a missing argument, which closed the calling shell. Verified on Rocky 10: the script sets the downtime for the host and all its services on Icinga 2.14.6, and strace shows the password in no execve argument.
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.
…only scripts The valkey scenario asserted root:valkey on valkey.conf, which the role no longer sets on RHEL, where it keeps the valkey:root that the package's tmpfiles.d rule restores; the assertion now follows the platform and covers the Unix socket, read back from the running server and 0770 with the valkey group. The schedule_reboot and setup_nextcloud scenarios assert that do-reboot and nextcloud-update, which carry the Icinga API credentials, are readable by root only. Verified on Rocky 10.
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.
…o root Set tools__icinga2_api_* so that schedule-icinga-downtime gets deployed. Assert that it is 0700 root, and that a login shell of nobody sees the reboot alias but not the password, and cannot run the script.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.