From 58428220f767b5c5cef0902d0fb0038efdc8e3f9 Mon Sep 17 00:00:00 2001 From: "Jorge E. Gomez" Date: Fri, 17 Jun 2022 21:41:05 -0500 Subject: [PATCH] First copy from original playbook --- .gitignore | 9 + LICENSE.md | 24 + README.md | 523 ++++++++++ local.yml | 11 + roles/nextcloud/defaults/main.yml | 94 ++ roles/nextcloud/files/mysql_nextcloud.cnf | 12 + .../files/nextcloud_choosing_version.png | Bin 0 -> 44487 bytes .../nextcloud_custom_mimetypemapping.json | 182 ++++ roles/nextcloud/files/nextcloud_file_name.xml | 1 + roles/nextcloud/handlers/main.yml | 41 + roles/nextcloud/tasks/db_mysql.yml | 89 ++ roles/nextcloud/tasks/db_postgresql.yml | 32 + roles/nextcloud/tasks/http_nginx.yml | 88 ++ roles/nextcloud/tasks/main.yml | 88 ++ roles/nextcloud/tasks/nc_apps.yml | 63 ++ roles/nextcloud/tasks/nc_download.yml | 46 + roles/nextcloud/tasks/nc_installation.yml | 168 +++ roles/nextcloud/tasks/redis_server.yml | 13 + roles/nextcloud/templates/nginx_nc.j2 | 146 +++ .../nextcloud/templates/nginx_php_handler.j2 | 9 + roles/nextcloud/templates/redis.conf.j2 | 957 ++++++++++++++++++ roles/nextcloud/vars/main.yml | 58 ++ 22 files changed, 2654 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 local.yml create mode 100644 roles/nextcloud/defaults/main.yml create mode 100644 roles/nextcloud/files/mysql_nextcloud.cnf create mode 100644 roles/nextcloud/files/nextcloud_choosing_version.png create mode 100644 roles/nextcloud/files/nextcloud_custom_mimetypemapping.json create mode 100644 roles/nextcloud/files/nextcloud_file_name.xml create mode 100644 roles/nextcloud/handlers/main.yml create mode 100644 roles/nextcloud/tasks/db_mysql.yml create mode 100644 roles/nextcloud/tasks/db_postgresql.yml create mode 100644 roles/nextcloud/tasks/http_nginx.yml create mode 100644 roles/nextcloud/tasks/main.yml create mode 100644 roles/nextcloud/tasks/nc_apps.yml create mode 100644 roles/nextcloud/tasks/nc_download.yml create mode 100644 roles/nextcloud/tasks/nc_installation.yml create mode 100644 roles/nextcloud/tasks/redis_server.yml create mode 100644 roles/nextcloud/templates/nginx_nc.j2 create mode 100644 roles/nextcloud/templates/nginx_php_handler.j2 create mode 100644 roles/nextcloud/templates/redis.conf.j2 create mode 100644 roles/nextcloud/vars/main.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af20b15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +Session.vim +.netrwhist +*~ +tags +.fuse_hidden* +.directory +.Trash-* diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..531d47c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ + +Copyright (c) 2016, Aalaesar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 14f2aea..9cb7d15 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,526 @@ apt install ansible ansible-pull -U "ssh://git@gitea.agofer.net:22001/jegomez/ansible-role-nextcloud.git" ``` +Based on [Aalaesar's role][1] + +[1]: https://github.com/aalaesar/install_nextcloud + +## install_nextcloud + +This role installs and configures an Nextcloud instance for a Debian/Ubuntu server. + +The role's main actions are: +- [x] Packages dependencies installation. +- [x] Database configuration (if located on the same host). +- [x] Strengthened files permissions and ownership following Nextcloud recommendations. +- [x] Web server configuration. +- [x] Redis Server installation. +- [x] Strengthened TLS configuration following _Mozilla SSL Configuration Generator_, intermediate profile by default, modern profile available. +- [x] Post installation of Nextcloud applications + +## Requirements +### Ansible version +Ansible >2.4 +### Python libraries +To use `ipwrap` filter in Ansible, you need to install the netaddr Python library on a computer on which you use Ansible (it is not required on remote hosts). It can usually be installed with either your system package manager or using pip: +```bash +$ pip install netaddr +``` +### Setup module: +The role uses facts gathered by Ansible on the remote host. If you disable the Setup module in your playbook, the role will not work properly. +### Root access +This role requires root access, so either configure it in your inventory files, run it in a playbook with a global `become: yes` or invoke the role in your playbook like: +> playbook.yml: + +```YAML +- hosts: dnsserver + become: yes + roles: + - role: aalaesar.install_nextcloud +``` + +## Role Variables + +Role's variables (and their default values): + +### Choose the version + +**_WARNING: Since Nexcloud 11 requires php v5.6 or later, command line installation will fail on old OS without php v5.6+ support._** + +_Known issue while installing Nextcloud 11 on an Ubuntu 14.04 system:_ [#27](https://github.com/aalaesar/install_nextcloud/issues/27) + +An URL will be generated following naming rules used in the nextcloud repository +_Not following this rules correctly may make the role unable to download nextcloud._ + +#### Repository naming rules: +Some variables changes depending on the channel used and if get_latest is true. +This table summarize the possible cases. + +|channel|latest|major&latest|major|full|special| +|---|---|---|---|---|---| +|**releases**|yes/no|_null_ \|9\|10\|...|_null_|"10.0.3"|_null_| +|**prereleases**|_null_|_null_|_null_|"11.0.1"|_null_ \|"RC(n)\|beta(n)"| +|**daily**|yes/no|_null_ \|master\|stable9\|...|master\|9\|10\|...|_null_|_null_ \|"YYYY-MM-DD"| + +**major&latest** = major value when latest is true +_null_ = "not used" +#### version variables: +```YAML +nextcloud_version_channel: "releases" # releases | prereleases | daily +``` +Specify the main channel to use. +```YAML +nextcloud_get_latest: true +``` +Specify if the "latest" archive should be downloaded. + +```YAML +# nextcloud_version_major: 10 +``` +Specify what major version you desire. + +```YAML +# nextcloud_version_full: "10.0.3" +``` +The full version of the desired nextcloud instance. type **M.F.P** _(Major.Feature.Patch)_ + +```YAML +# nextcloud_version_special: "" +``` +Specify a special string in the archive's filename. +For prereleases: "RCn|beta" | for daily "YYYY-MM-DD" + +```YAML +nextcloud_repository: "https://download.nextcloud.com/server" +``` +Repository's URL. + +```YAML +nextcloud_archive_format: "zip" # zip | tar.bz2 +``` +Choose between the 2 archive formats available in the repository. + +```YAML +# nextcloud_full_url: +``` +_If you don't like rules..._ +Specify directly a full URL to the archive. The role will skip the url generation and download the archive. **Requires nextcloud_version_major to be set along**. +#### Examples: +- Download your own archive: + (_you **must** specify the nextcloud major version along_) +```YAML +nextcloud_full_url: https://download.nextcloud.com/server/releases/nextcloud-23.0.0.zip +nextcloud_version_major: 42 +``` +- Choose the latest release (default): +```YAML +nextcloud_version_channel: "releases" +nextcloud_get_latest: true +``` +- Choose the latest v23 release: +```YAML +nextcloud_version_channel: "releases" +nextcloud_get_latest: true +nextcloud_version_major: 23 +``` +- Choose a specific release: +```YAML +nextcloud_version_channel: "releases" +nextcloud_get_latest: false +nextcloud_version_full: "23.0.0" +``` +- Get the nextcloud 24.0.1 prerelease 1: +```YAML +nextcloud_version_channel: "prereleases" +nextcloud_version_full: "23.0.0" +nextcloud_version_special: "RC3" +``` +- Get the latest daily: +```YAML +nextcloud_version_channel: "daily" +nextcloud_get_latest: true +``` +- Get the latest daily for stable 10: +```YAML +nextcloud_version_channel: "daily" +nextcloud_get_latest: true +nextcloud_version_major: "stable23" +``` +- Get the daily for master at january 1rst 2022: +```YAML +nextcloud_version_channel: "daily" +nextcloud_get_latest: false +nextcloud_version_major: "master" +nextcloud_version_special: "2022-01-01" +``` +### Main configuration +```YAML +nextcloud_trusted_domain: + - "{{ ansible_fqdn }}" + - "{{ ansible_default_ipv4.address }}" +``` +The list of domains you will use to access the same Nextcloud instance. +```YAML +nextcloud_trusted_proxies: [] +``` +The list of trusted proxies IPs if Nextcloud runs through a reverse proxy. +```YAML +nextcloud_instance_name: "{{ nextcloud_trusted_domain | first }}" +``` +The name of the Nextcloud instance. By default, the first element in the list of trusted domains +### WebServer configuration +```YAML +nextcloud_install_websrv: true +``` +The webserver setup can be skipped if you have one installed already. +```YAML +nextcloud_websrv: "apache2" +``` +The http server used by nextcloud. Available values are: **apache2** or **nginx**. +```YAML +nextcloud_disable_websrv_default_site: false +``` +Disable the default site of the chosen http server. (`000-default.conf` in Apache, `default` in Nginx.) +```YAML +nextcloud_websrv_template: "templates/{{nextcloud_websrv}}_nc.j2" +``` +The jinja2 template creating the instance configuration for your webserver. +You can provide your own through this parameter. +```YAML +nextcloud_webroot: "/opt/nextcloud" +``` +The Nextcloud root directory. +```YAML +nextcloud_data_dir: "/var/ncdata" +``` +The Nextcloud data directory. This directory will contain all the Nextcloud files. Choose wisely. +```YAML +nextcloud_admin_name: "admin" +``` +Defines the Nextcloud admin's login. +```YAML +nextcloud_admin_pwd: "secret" +``` +Defines the Nextcloud admin's password. +**Not defined by default** +If not defined by the user, a random password will be generated. + +```YAML +nextcloud_max_upload_size: "512m" +``` +Defines the max size allowed to be uploaded on the server. +Use 0 to __disable__. + +### Redis Server configuration +```YAML +nextcloud_install_redis_server: true +``` +Whenever the role should install a redis server on the same host. +```YAML +nextcloud_redis_host: '/var/run/redis/redis.sock' +``` +The Hostname of redis server. It is set to use UNIX socket as redis is on same host. Set to hostname if it is not the case. +```YAML +nextcloud_redis_port: 0 +``` +The port of redis server. Port 0 is for socket use. Default redis port is 6379. +```YAML +nextcloud_redis_settings: + - { name: 'redis host', value: '"{{ nextcloud_redis_host }}"' } + - { name: 'redis port', value: "{{ nextcloud_redis_port }}" } + - { name: 'memcache.locking', value: '\OC\Memcache\Redis' } +``` +Settings to use redis server with Nextcloud + +### Nextcloud Background Jobs +```YAML +nextcloud_background_cron: True +``` +Set operating system cron for executing Nextcloud regular tasks. This method enables the execution of scheduled jobs without the inherent limitations the Web server might have. + +### Custom nextcloud settings +```YAML +nextcloud_config_settings: + - { name: 'overwrite.cli.url', value: 'https://{{ nextcloud_trusted_domain | first }}' } + - { name: 'memcache.local', value: '\OC\Memcache\APCu' } + - { name: 'open_basedir', value: '/dev/urandom' } + - { name: 'mysql.utf8mb4', value: 'true' } + - { name: 'updater.release.channel', value: 'production' } # production | stable | daily | beta +``` +Setting custom Nextcloud setting in config.php ( [Config.php Parameters Documentations](https://docs.nextcloud.com/server/stable/admin_manual/) ) + +Default custom settings: +- **Base URL**: 'https:// {{nextcloud_instance_name}}' +- **Memcache local**: APCu +- **Mysql Character Set**: utf8mb4 +- **PHP read access to /dev/urandom**: Enabled +- **Updater Relese Channel:** Production +### Database configuration +```YAML +nextcloud_install_db: true +``` +Whenever the role should install and configure a database on the same host. +```YAML +nextcloud_db_host: "127.0.0.1" +``` +The database server's ip/hostname where Nextcloud's database is located. +```YAML +nextcloud_db_backend: "mysql" +``` +Database type used by nextcloud. + +Supported values are: +- mysql +- mariadb +- pgsql _(PostgreSQL)_ + +```YAML +nextcloud_db_name: "nextcloud" +``` +The Nextcloud instance's database name. +```YAML +nextcloud_db_admin: "ncadmin" +``` +The Nextcloud instance's database user's login +```YAML +nextcloud_db_pwd: "secret" +``` +The Nextcloud instance's database user's password. + +**Not defined by default.** + +If not defined by the user, a random password will be generated. + +### TLS configuration +```YAML +nextcloud_install_tls: true +``` +TLS setup can be skipped if you manage it separately (e.g. behind a reverse proxy). +```YAML +nextcloud_tls_enforce: true +``` +Force http to https. +```YAML +nextcloud_mozilla_modern_ssl_profile: true +``` +Force Mozilla modern SSL profile in webserver configuration (intermediate profile is used when false). +```YAML +nextcloud_hsts: false +``` +Set HTTP Strict-Transport-Security header (e.g. "max-age=15768000; includeSubDomains; preload"). + +_(Before enabling HSTS, please read into this topic first)_ +```YAML +nextcloud_tls_cert_method: "self-signed" +``` +Defines various method for retrieving a TLS certificate. +- **self-signed**: generate a _one year_ self-signed certificate for the trusted domain on the remote host and store it in _/etc/ssl_. +- **signed**: copy provided signed certificate for the trusted domain to the remote host or in /etc/ssl by default. + Uses: +```YAML + # Mandatory: + nextcloud_tls_src_cert: /local/path/to/cert + # ^local path to the certificate's key. + nextcloud_tls_src_cert_key: /local/path/to/cert/key + # ^local path to the certificate. + + # Optional: + nextcloud_tls_cert: "/etc/ssl/{{ nextcloud_trusted_domain }}.crt" + # ^remote absolute path to the certificate's key. + nextcloud_tls_cert_key: "/etc/ssl/{{ nextcloud_trusted_domain }}.key" + # ^remote absolute path to the certificate. +``` +- **installed**: if the certificate for the trusted domain is already on the remote host, specify its location. + Uses: +```YAML + nextcloud_tls_cert: /path/to/cert + # ^remote absolute path to the certificate's key. mandatory + nextcloud_tls_cert_key: /path/to/cert/key + # ^remote absolute path to the certificate. mandatory + nextcloud_tls_cert_chain: /path/to/cert/chain + # ^remote absolute path to the certificate's full chain- used only by apache - Optional +``` +```YAML +nextcloud_tls_session_cache_size: 50m +``` +Set the size of the shared nginx TLS session cache to 50 MB. + +### System configuration + +install and use a custom version for PHP instead of the default one: +```YAML +php_ver: '7.1' +php_custom: yes +php_ver: "{{ php_ver }}" +php_dir: "/etc/php/{{ php_ver }}" +php_bin: "php-fpm{{ php_ver }}" +php_pkg_apcu: "php-apcu" +php_pkg_spe: + - "php{{ php_ver }}-imap" + - "php{{ php_ver }}-imagick" + - "php{{ php_ver }}-xml" + - "php{{ php_ver }}-zip" + - "php{{ php_ver }}-mbstring" + - "php-redis" +php_socket: "/run/php/{{ php_ver }}-fpm.sock" +php_memory_limit: 512M +``` + +```YAML +nextcloud_websrv_user: "www-data" +``` +system user for the http server +```YAML +nextcloud_websrv_group: "www-data" +``` +system group for the http server +```YAML +nextcloud_mysql_root_pwd: "secret" +``` +root password for the mysql server + +**Not defined by default** + +If not defined by the user, and mysql/mariadb is installed during the run, a random password will be generated. + +### Generated password +The role uses Ansible's password Lookup: +- If a password is generated by the role, ansible stores it **locally** in **nextcloud_instances/{{ nextcloud_trusted_domain }}/** (relative to the working directory) +- if the file already exist, it reuse its content +- see [the ansible password lookup documentation](https://docs.ansible.com/ansible/latest/plugins/lookup/password.html) for more info + +### Post installation: +#### Applications installation + +Since **v1.3.0**, it is possible to download, install and enable nextcloud applications during a post-install process. + +The application (app) to install have to be declared in the `nextcloud_apps` dictionary in a "key:value" pair. +- The app name is the key +- The download link, is the value. + +```YAML +nextcloud_apps: + app_name_1: "http://download_link.com/some_archive.zip" + app_name_2: "http://getlink.com/another_archive.zip" +``` + +Alternatively, if you need to configure an application after enabling it, you can use this structure. +```YAML +nextcloud_apps: + app_name_1: + source: "http://download_link.com/some_archive.zip" + conf: + parameter1: ldap:\/\/ldapsrv + parameter2: another_value +``` + +**Notes:** +- Because the role is using nextcloud's occ, it is not possible to install an app from the official nextcloud app store. +- If you know that the app is already installed, you can give an empty string to skip the download. +- The app name need the be equal to the folder name located in the **apps folder** of the nextcloud instance, which is extracted from the downloaded archive. +The name may not be canon some times. (like **appName-x.y.z** instead of **appName**) +- The role will **not** update an already enabled application. +- The configuration is applied only when the app in enabled the first time: +Changing a parameter, then running the role again while the app is already enabled will **not** update its configuration. +- this post_install process is tagged and can be called directly using the `--tags install_apps` option. + +## Dependencies + +none + +## Example Playbook +### Case 1: Installing a quick Nextcloud demo +In some case, you may want to deploy quickly many instances of Nextcloud on multiple hosts for testing/demo purpose and don't want to tune the role's variables for each hosts: Just run the playbook without any additional variable (all default) ! + +```YAML +--- +- hosts: server + roles: + - role: aalaesar.install_nextcloud +``` + +- This will install a Nextcloud 10.0.1 instance in /opt/nextcloud using apache2 and mysql. +- it will be available at **https:// {{ ansible default ipv4 }}** using a self signed certificate. +- Generated passwords are stored in **nextcloud_instances/{{ nextcloud_trusted_domain }}/** from your working directory. + +### Case 1.1: specifying the version channel, branch, etc. +You can choose the version channel to download a specific version of nextcloud. Here's a variation of the previous case, this time installing the latest nightly in master. +```YAML +--- +- hosts: server + roles: + - role: aalaesar.install_nextcloud + nextcloud_version_channel: "daily" + nextcloud_version_major: "master" +``` + +### Case 2: Using letsencrypt with this role. +This role is not designed to manage letsencrypt certificates. However you can still use your certificates with nextcloud. + +You must create first your certificates using a letsencrypt ACME client or an Ansible role like [this one] (https://github.com/jaywink/ansible-letsencrypt) + +then call _install_nextcloud_ by setting `nextcloud_tls_cert_method: "installed"` + +Here 2 examples for apache and nginx (because they have slightly different configurations) +```YAML +--- +- hosts: apache_server + roles: + - role: aalaesar.install_nextcloud + nextcloud_trusted_domain: + - "example.com" + nextcloud_tls_cert_method: "installed" + nextcloud_tls_cert: "/etc/letsencrypt/live/example.com/cert.pem" + nextcloud_tls_cert_key: "/etc/letsencrypt/live/example.com/privkey.pem" + nextcloud_tls_cert_chain: "/etc/letsencrypt/live/example.com/chain.pem" + +- hosts: nginx_server + roles: + - role: aalaesar.install_nextcloud + nextcloud_trusted_domain: + - "example2.com" + nextcloud_tls_cert_method: "installed" + nextcloud_tls_cert: "/etc/letsencrypt/live/example2.com/fullchain.pem" + nextcloud_tls_cert_key: "/etc/letsencrypt/live/example2.com/privkey.pem" +``` +### Case 3: integration to an existing system. +- An Ansible master want to install a new Nextcloud instance on an existing Ubuntu 14.04 server with nginx & mariadb installed. +- As is server do not meet the php requirements for Nextcloud 11, he chooses to use the lastest Nextcloud 10 release. +- He wants it to be accessible from internet at _cloud.example.tld_ and from his intranet at _dbox.intra.net_. +- He already have a valid certificate for the intranet domain in /etc/nginx/certs/ installed +- he wants the following apps to be installed & enabled : files_external, calendar, agenda, richdocuments (Collabora) +- The richdocuments app has to be configured to point out to the Collabora domain. + +He can run the role with the following variables to install Nextcloud accordingly to its existing requirements . + +```YAML +--- +- hosts: server + roles: + - role: aalaesar.install_nextcloud + nextcloud_version_major: 23 + nextcloud_trusted_domain: + - "cloud.example.tld" + - "dbox.intra.net" + nextcloud_websrv: "nginx" + nextcloud_admin_pwd: "secret007" + nextcloud_webroot: "/var/www/nextcloud/" + nextcloud_data_dir: "/ncdata" + nextcloud_db_pwd: "secretagency" + nextcloud_tls_cert_method: "installed" + nextcloud_tls_cert: "/etc/nginx/certs/nextcloud.crt" + nextcloud_tls_cert_key: "/etc/nginx/certs/nextcloud.key" + nextcloud_mysql_root_pwd: "42h2g2" + nextcloud_apps: + files_external: "" #enable files_external which is already installed in nextcloud + calendar: "https://github.com/nextcloud/calendar/releases/download/v1.5.0/calendar.tar.gz" + contacts: "https://github.com/nextcloud/contacts/releases/download/v1.5.3/contacts.tar.gz" + richdocuments-1.1.25: # the app name is equal to the extracted folder name from the archive + source: "https://github.com/nextcloud/richdocuments/archive/1.1.25.zip" + conf: + wopi_url: 'https://office.example.tld' +``` + +## License + +BSD diff --git a/local.yml b/local.yml new file mode 100644 index 0000000..e9ba392 --- /dev/null +++ b/local.yml @@ -0,0 +1,11 @@ +- hosts: localhost + gather_facts: yes + user: root + + roles: + - nextcloud + + vars: + nextcloud_trusted_domain: + - cloud.agofer.net + diff --git a/roles/nextcloud/defaults/main.yml b/roles/nextcloud/defaults/main.yml new file mode 100644 index 0000000..49e84b5 --- /dev/null +++ b/roles/nextcloud/defaults/main.yml @@ -0,0 +1,94 @@ +--- +# defaults file for nextcloud +# [DOWNLOAD] + # An URL will be generated following naming rules used by nextcloud's repository + # Not following this rules correctly will make the role unable to download nextcloud. +nextcloud_version_channel: "releases" # mandatory # releases | prereleases | daily + # channel releases requires version_full. + # channel prereleases requires version_full. Optional: version_special. + # channel daily requires requires version_full & version_special. +nextcloud_get_latest: true # mandatory # specify if the latest archive should be downloaded. + # Override generated file name for channels: releases | daily. + # optional : version_major. +# nextcloud_version_major: 10 # (9 | 10 | 11| ..) for releases | for daily : (master | stable9 | stable10 | ...) +# nextcloud_version_full: "10.0.3" # full version string +# nextcloud_version_special: "" # For prereleases: "RCn|beta" | for daily "YYYY-MM-DD" +nextcloud_repository: "https://download.nextcloud.com/server" # Domain URL where to download Nextcloud. +nextcloud_archive_format: "zip" # zip | tar.bz2 +# nextcloud_full_url: "https://download.nextcloud.com/server/releases/nextcloud-23.0.0.zip" # specify directly a full URL to the archive if you don't like rules. + + +# [CONFIG] +nextcloud_trusted_domain: + - "{{ ansible_fqdn }}" + - "{{ ansible_default_ipv4.address }}" + +nextcloud_ipv6: false + +nextcloud_trusted_proxies: [] + +nextcloud_instance_name: "{{ nextcloud_trusted_domain | first }}" + +nextcloud_disable_websrv_default_site: false +nextcloud_websrv_template: "templates/nginx_nc.j2" +nextcloud_webroot: "/opt/nextcloud" +nextcloud_data_dir: "/var/ncdata" +nextcloud_admin_name: "admin" +# nextcloud_admin_pwd: "secret" + +nextcloud_install_redis_server: true +nextcloud_redis_host: '/var/run/redis/redis.sock' +nextcloud_redis_port: 0 + +nextcloud_redis_settings: + - { name: 'redis host', value: '"{{ nextcloud_redis_host }}"' } + - { name: 'redis port', value: "{{ nextcloud_redis_port }}" } + - { name: 'memcache.locking', value: '\OC\Memcache\Redis' } + +nextcloud_background_cron: True + +## Custom nextcloud settings +## https://docs.nextcloud.com/server/12/admin_manual/configuration_server/config_sample_php_parameters.html +nextcloud_config_settings: + - { name: 'overwrite.cli.url', value: 'https://{{ nextcloud_trusted_domain | first }}' } + - { name: 'memcache.local', value: '\OC\Memcache\APCu' } + - { name: 'open_basedir', value: '/dev/urandom' } + - { name: 'mysql.utf8mb4', value: 'true' } + - { name: 'updater.release.channel', value: 'production' } # production | stable | daily | beta + +# [DATABASE] +nextcloud_install_db: true +nextcloud_db_host: "127.0.0.1" +nextcloud_db_backend: "mysql" # "mysql"/"mariadb" | "pgsql" +nextcloud_db_name: "nextcloud" +nextcloud_db_admin: "ncadmin" +# nextcloud_db_pwd: "secret" + +# [TLS] parameters used in the apache2 & nginx templates +## max file's size allowed to be uploaded on the server +nextcloud_max_upload_size: 512m # in Byte or human readable size notation (g|m|k) +nextcloud_mozilla_modern_ssl_profile: false # when false, intermediate profile is used +nextcloud_tls_dhparam: "/etc/ssl/dhparam.pem" +nextcloud_hsts: false # recommended >= 15552000 +# nextcloud_tls_cert: /path/to/cert +# nextcloud_tls_cert_key: /path/to/cert/key +# nextcloud_tls_cert_chain: /path/to/cert/chain +# nextcloud_tls_src_cert: /path/to/cert +# nextcloud_tls_src_cert_key: /path/to/cert/key +nextcloud_tls_session_cache_size: 50m # in Byte or human readable size notation (g|m|k) + +# [APPS] +nextcloud_apps: {} + +# [SYSTEM] +#nextcloud_mysql_root_pwd: "secret" + +php_custom: false +php_memory_limit: 512M + +# [system] +# system_configuration for php 7.4: +php_dir: "/etc/php/7.4" +php_bin: "php-fpm7.4" + +php_socket: "/run/php/php7.4-fpm.sock" diff --git a/roles/nextcloud/files/mysql_nextcloud.cnf b/roles/nextcloud/files/mysql_nextcloud.cnf new file mode 100644 index 0000000..c2a1bdc --- /dev/null +++ b/roles/nextcloud/files/mysql_nextcloud.cnf @@ -0,0 +1,12 @@ +################################################################################ +# This file was generated by Ansible +# Do NOT modify this file by hand! +################################################################################ + +# Nextcloud mysql.cnf + +[mysqld] +binlog_format = MIXED +innodb_large_prefix=on +innodb_file_format=barracuda +innodb_file_per_table=true \ No newline at end of file diff --git a/roles/nextcloud/files/nextcloud_choosing_version.png b/roles/nextcloud/files/nextcloud_choosing_version.png new file mode 100644 index 0000000000000000000000000000000000000000..174825848133194924800a26954ae5bb17fcce78 GIT binary patch literal 44487 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 \ No newline at end of file diff --git a/roles/nextcloud/handlers/main.yml b/roles/nextcloud/handlers/main.yml new file mode 100644 index 0000000..a375e3e --- /dev/null +++ b/roles/nextcloud/handlers/main.yml @@ -0,0 +1,41 @@ +--- +# handlers file for nextcloud +- name: restart mysql + service: + name: mysql + state: restarted + +- name: start http + service: + name: nginx + state: started + +- name: restart http + service: + name: nginx + state: restarted + +- name: reload http + service: + name: nginx + state: reloaded + +- name: start php-fpm + service: + name: php{{ php_ver }}-fpm + state: started + +- name: reload php-fpm + service: + name: php{{ php_ver }}-fpm + state: reloaded + +- name: start redis + service: + name: redis-server + state: started + +- name: restart redis + service: + name: redis-server + state: restarted diff --git a/roles/nextcloud/tasks/db_mysql.yml b/roles/nextcloud/tasks/db_mysql.yml new file mode 100644 index 0000000..09a74da --- /dev/null +++ b/roles/nextcloud/tasks/db_mysql.yml @@ -0,0 +1,89 @@ +--- +- name: "[mySQL: Debian] - Service is installed." + package: + name: "{{ 'default-' if ((ansible_distribution|lower) == 'debian' and nextcloud_db_backend == 'mysql') else '' }}{{ nextcloud_db_backend }}-server" + state: present + register: nc_mysql_db_install + +- name: "[mySQL] - Packages are installed." + package: + name: "{{ nc_mysql_deps }}" + state: present + vars: + nc_mysql_deps: + - "php{{ php_ver }}-mysql" + - "python{{ '3' if ansible_python.version.major == 3 else '' }}-pymysql" + +- name: "[mySQL] - generate {{ nextcloud_db_backend }} root Password:" + set_fact: + nextcloud_mysql_root_pwd: "{{ lookup( 'password', 'nextcloud_instances/'+ nextcloud_instance_name +'/mysql_root.pwd' ) }}" + when: nextcloud_mysql_root_pwd is not defined + +- name: "[mySQL] - save {{ nextcloud_db_backend }} root password in config file" + ini_file: + path: "{{ mysql_credential_file[(ansible_os_family|lower)] }}" + section: client + option: password + value: "{{ nextcloud_mysql_root_pwd }}" + no_log: true + when: mysql_credential_file[(ansible_os_family|lower)] is defined + +# use debian default credentials to work on user root password +- name: "[mySQL] - Update {{ nextcloud_db_backend }} root password" + mysql_user: + name: root + + password: "{{ nextcloud_mysql_root_pwd }}" + config_file: "{{ mysql_credential_file[(ansible_os_family|lower)] | default(omit) }}" + check_implicit_admin: yes + priv: "*.*:ALL,GRANT" + # Assuming the root user has only localhost access + host_all: yes + + +- name: "[mySQL] - Delete the anonymous user." + mysql_user: + user: "" + state: "absent" + login_user: root + login_password: "{{ nextcloud_mysql_root_pwd }}" + config_file: "{{ mysql_credential_file[(ansible_os_family|lower)] | default(omit) }}" + ignore_errors: yes + +- name: "[mySQL] - Removes the MySQL test database" + mysql_db: + name: test + state: absent + login_user: root + login_password: "{{ nextcloud_mysql_root_pwd }}" + config_file: "{{ mysql_credential_file[(ansible_os_family|lower)] | default(omit) }}" + ignore_errors: yes + +- name: "[mySQL] - Set mysql config option for nextcloud" + copy: + dest: /etc/mysql/conf.d/nextcloud.cnf + src: files/mysql_nextcloud.cnf + notify: restart mysql + +- name: "[mySQL] - Generate database user Password." + set_fact: + nextcloud_db_pwd: "{{ lookup( 'password', 'nextcloud_instances/'+ nextcloud_instance_name +'/db_admin.pwd' ) }}" + when: nextcloud_db_pwd is not defined + +- name: "[mySQL] - Add Database {{ nextcloud_db_name }}." + mysql_db: + name: "{{ nextcloud_db_name }}" + login_user: root + login_password: "{{ nextcloud_mysql_root_pwd }}" + config_file: "{{ mysql_credential_file[(ansible_os_family|lower)] | default(omit) }}" + state: present + +- name: "[mySQL] - Configure the database user." + mysql_user: + name: "{{ nextcloud_db_admin }}" + password: "{{ nextcloud_db_pwd }}" + priv: "{{ nextcloud_db_name }}.*:ALL" + login_user: root + login_password: "{{ nextcloud_mysql_root_pwd }}" + config_file: "{{ mysql_credential_file[(ansible_os_family|lower)] | default(omit) }}" + state: present diff --git a/roles/nextcloud/tasks/db_postgresql.yml b/roles/nextcloud/tasks/db_postgresql.yml new file mode 100644 index 0000000..f543ffe --- /dev/null +++ b/roles/nextcloud/tasks/db_postgresql.yml @@ -0,0 +1,32 @@ +--- +- name: "[PostgreSQL] - PostgreSQL packages are installed" + package: + name: "{{ pg_deps }}" + state: "present" + vars: + pg_deps: + - "postgresql" + - "php{{ php_ver }}-pgsql" + - "python{{ '3' if ansible_python.version.major == 3 else '' }}-psycopg2" + +- name: "[PostgreSQL] - generate nextcloud role password." + set_fact: nextcloud_db_pwd="{{ lookup( 'password', 'nextcloud_instances/'+ nextcloud_instance_name +'/pgsql_nc.pwd' ) }}" + when: nextcloud_db_pwd is not defined + +- name: "[PostgreSQL] - nextcloud role is created." + postgresql_user: + name: "{{ nextcloud_db_admin }}" + password: "{{ nextcloud_db_pwd }}" + encrypted: yes + state: present + role_attr_flags: CREATEDB + become_user: postgres + become: yes + +- name: "[PostgreSQL] - nextcloud database is created." + postgresql_db: + name: "{{ nextcloud_db_name }}" + state: present + owner: "{{ nextcloud_db_admin }}" + become_user: postgres + become: yes diff --git a/roles/nextcloud/tasks/http_nginx.yml b/roles/nextcloud/tasks/http_nginx.yml new file mode 100644 index 0000000..6f7c844 --- /dev/null +++ b/roles/nextcloud/tasks/http_nginx.yml @@ -0,0 +1,88 @@ +--- +- name: Configure php-fpm + lineinfile: + dest: "{{ php_dir }}/fpm/pool.d/www.conf" + regexp: '^\;env' + state: absent + # validate: "/usr/sbin/{{ php_bin }} -t #%s" + notify: reload php-fpm + +- name: "[NGINX] - Add path variable to php-fpm" + blockinfile: + dest: "{{ php_dir }}/fpm/pool.d/www.conf" + insertafter: '^; Default Value: clean env$' + marker: "; {mark} ANSIBLE MANAGED BLOCK" + block: | + env[HOSTNAME] = $HOSTNAME + env[PATH] = $PATH + env[TMP] = /tmp + env[TMPDIR] = /tmp + env[TEMP] = /tmp + notify: reload php-fpm + +- name: "[NGINX] - enable APC for php CLI" + lineinfile: + dest: "{{ php_dir }}/cli/php.ini" + line: "apc.enable_cli = 1" + insertbefore: "^; End:$" + state: present + # validate: "/usr/sbin/{{ php_bin }} -t #%s" + notify: reload php-fpm + +- name: "[NGINX] - enable PHP OPcache for php.ini" + lineinfile: + dest: "{{ php_dir }}/fpm/php.ini" + state: present + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + backrefs: yes + with_items: + - { regexp: 'opcache.enable=0', line: 'opcache.enable=1' } + - { regexp: 'opcache.enable_cli', line: 'opcache.enable_cli=1' } + - { regexp: 'opcache.interned_strings_buffer', line: 'opcache.interned_strings_buffer=8' } + - { regexp: 'opcache.max_accelerated_files', line: 'opcache.max_accelerated_files=10000' } + - { regexp: 'opcache.memory_consumption', line: 'opcache.memory_consumption=128' } + - { regexp: 'opcache.save_comments', line: 'opcache.save_comments=1' } + - { regexp: 'opcache.revalidate_freq', line: 'opcache.revalidate_freq=1' } + - { regexp: 'memory_limit', line: 'memory_limit={{ php_memory_limit }}'} + # validate: "/usr/sbin/{{ php_bin }} -t #%s" + notify: reload php-fpm + + +- name: "[NGINX] - Public Diffie-Hellman Parameter are generated. This might take a while." + command: "openssl dhparam -out {{ nextcloud_tls_dhparam }} 2048" + args: + creates: "{{ nextcloud_tls_dhparam }}" + +- name: "[NGINX] - php handler configuration is present." + template: + dest: /etc/nginx/sites-available/php_handler.cnf + src: templates/nginx_php_handler.j2 + notify: reload http + +- name: "[NGINX] - php handler is enabled" + file: + path: /etc/nginx/sites-enabled/php_handler + src: /etc/nginx/sites-available/php_handler.cnf + state: link + notify: reload http + +- name: "[NGINX] - generate Nextcloud configuration for nginx" + template: + dest: /etc/nginx/sites-available/nc_{{ nextcloud_instance_name }}.cnf + src: "{{ nextcloud_websrv_template }}" + notify: reload http + +- name: "[NGINX] - Enable Nextcloud in nginx conf" + file: + path: /etc/nginx/sites-enabled/nc_{{ nextcloud_instance_name }} + src: /etc/nginx/sites-available/nc_{{ nextcloud_instance_name }}.cnf + state: link + notify: reload http + +- name: "[NGINX] - Disable nginx default site" + file: + path: /etc/nginx/sites-enabled/default + state: absent + when: nextcloud_disable_websrv_default_site | bool + notify: reload http diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml new file mode 100644 index 0000000..b1b0e1d --- /dev/null +++ b/roles/nextcloud/tasks/main.yml @@ -0,0 +1,88 @@ +--- +# Main tasks in Nextcloud installation + +- name: Install required packages + apt: + name: '{{ required_packages }}' + state: present + notify: + - start http + - start php-fpm + +- fail: + msg: Debugging + +- name: Configure Nginx web server + include_tasks: http_nginx.yml + +- name: Configure Redis server + include_tasks: ./redis_server.yml + when: (nextcloud_install_redis_server | bool) + +- block: + - name: Configure mysql/mariadb database + include_tasks: ./db_mysql.yml + when: nextcloud_db_backend in ["mysql", "mariadb"] + + - name: Configure PostgreSQL database + include_tasks: ./db_postgresql.yml + when: nextcloud_db_backend in ["pgsql"] + when: nextcloud_install_db + +- name: Check Nextcloud installed + stat: "path={{ nextcloud_webroot }}/index.php" + register: nc_nextcloud_installed + +- name: Downloading Nextcloud + include_tasks: ./nc_download.yml + when: not nc_nextcloud_installed.stat.exists + +- name: Check Nextcloud configuration exists. + stat: path="{{ nextcloud_webroot }}/config/config.php" + register: nc_nextcloud_conf + +- name: Check Nextcloud is configured + command: grep -q "{{ nextcloud_trusted_domain| first }}" {{ nextcloud_webroot }}/config/config.php + failed_when: False + changed_when: False + register: nc_nextcloud_configured + when: nc_nextcloud_conf.stat.exists + +- name: Nextcloud installation + include_tasks: ./nc_installation.yml + when: | + (not nc_nextcloud_conf.stat.exists) or + (nc_nextcloud_configured.rc is defined and nc_nextcloud_configured.rc != 0) + +- block: + - name: "[NC apps] - lists the number of apps available in the instance." + command: php occ app:list --output=json_pretty --no-warnings + args: + chdir: "{{ nextcloud_webroot }}" + become_user: "{{ nextcloud_websrv_user }}" + become: yes + become_flags: "{{ ansible_become_flags | default(omit) }}" + changed_when: false + register: nc_apps_list + + - name: "[NC apps] - convert list to yaml." + set_fact: nc_available_apps="{{ nc_apps_list.stdout | from_json }}" + + - name: "[NC apps] - installation." + include_tasks: ./nc_apps.yml + # do if the app is not enabled and ( (archive path is not "") or (app is disabled) ) + when: + - item.key not in nc_available_apps.enabled + - (item.value is not none) or (item.key in nc_available_apps.disabled) + with_dict: "{{ nextcloud_apps }}" + when: + - nextcloud_apps is defined + - nextcloud_apps is mapping + +- name: Add indices + command: php occ db:add-missing-indices + args: + chdir: "{{ nextcloud_webroot }}" + become_user: "{{ nextcloud_websrv_user }}" + become: yes + become_flags: "{{ ansible_become_flags | default(omit) }}" diff --git a/roles/nextcloud/tasks/nc_apps.yml b/roles/nextcloud/tasks/nc_apps.yml new file mode 100644 index 0000000..835818c --- /dev/null +++ b/roles/nextcloud/tasks/nc_apps.yml @@ -0,0 +1,63 @@ +--- +- name: parse the item values + set_fact: + nc_app_name: "{{ item.key }}" + nc_app_cfg: "{{ item.value }}" + +- block: + - name: "[ App {{ nc_app_name }} ] - Download Archive in apps folder." + unarchive: + copy: no + src: "{{ nc_app_cfg }}" + dest: "{{ nextcloud_webroot }}/apps/" + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + creates: "{{ nextcloud_webroot }}/apps/{{ nc_app_name }}" + when: nc_app_cfg is not none + + - name: "[ App {{ nc_app_name }} ] - enable the application." + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ app:enable "{{ nc_app_name }}" + args: + chdir: "{{ nextcloud_webroot }}" + when: nc_app_cfg is string + +- block: + - name: verify the app's yaml declaration + assert: + that: + - (nc_app_cfg.source is defined) and (nc_app_cfg.source is string) + msg: "{{ nc_app_name }} is not well declared." + + - name: "[ App {{ nc_app_name }} ] - Download Archive in apps folder." + unarchive: + copy: no + src: "{{ nc_app_cfg.source }}" + dest: "{{ nextcloud_webroot }}/apps/" + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + creates: "{{ nextcloud_webroot }}/apps/{{ nc_app_name }}" + when: nc_app_cfg.source is not none + + - name: "[ App {{ nc_app_name }} ] - enable the application." + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ app:enable "{{ nc_app_name }}" + args: + chdir: "{{ nextcloud_webroot }}" + - block: + - name: "[ App {{ nc_app_name }} ] - Configure the application " + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ config:app:set {{ nc_app_name }} {{ item_cfg.key }} --value="{{ item_cfg.value }}" + args: + chdir: "{{ nextcloud_webroot }}" + with_dict: "{{ nc_app_cfg.conf | default({}) }}" + loop_control: + loop_var: item_cfg + when: nc_app_cfg.conf is defined + when: (nc_app_cfg is mapping) diff --git a/roles/nextcloud/tasks/nc_download.yml b/roles/nextcloud/tasks/nc_download.yml new file mode 100644 index 0000000..7b11a34 --- /dev/null +++ b/roles/nextcloud/tasks/nc_download.yml @@ -0,0 +1,46 @@ +--- +- name: "[NC-DL] - Unzip is installed" + package: name=unzip state=present + when: nextcloud_archive_format == "zip" + +- name: "[NC-DL] - bunzip2 is installed" + package: name=bzip2 state=present + when: nextcloud_archive_format == "tar.bz2" + +- name: you must specify the major version + assert: + that: nextcloud_version_major is defined + when: nextcloud_full_url is defined + +#- name: "[NC-DL] - Create webroot folder" +# file: +# path: "{{ nextcloud_webroot }}" +# recurese: yes +# state: directory + +- name: "[NC-DL] - Create and set directory ownership & permissions for the webroot folder" + file: + path: "{{ nextcloud_webroot }}" + mode: "u=rwX,g=rX,o-rwx" + recurse: yes + state: directory + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + +- block: + - name: "Download & extract Nextcloud to /tmp." + unarchive: + copy: no + src: "{{ nextcloud_full_url | default(nextcloud_calculated_url) }}" + dest: "/tmp/" + vars: + nextcloud_calculated_url: "{{ nextcloud_repository }}/{{ nextcloud_version_channel }}/{{ nextcloud_calculated_file }}" + nextcloud_calculated_file: "{{ [nextcloud_dl_file_name[just_a_dict_key], nextcloud_archive_format]|join('.') }}" + just_a_dict_key: "{{ 'latest' if ((nextcloud_get_latest|bool) and (nextcloud_version_channel != 'prereleases')) else nextcloud_version_channel }}" + + + - name: "[NC-DL] - Move extracted files to {{ nextcloud_webroot }}." + command: "cp -r /tmp/nextcloud/. {{ nextcloud_webroot }}/" + + - name: "[NC-DL] - Remove nextcloud archive files" + file: path=/tmp/nextcloud state=absent diff --git a/roles/nextcloud/tasks/nc_installation.yml b/roles/nextcloud/tasks/nc_installation.yml new file mode 100644 index 0000000..ae9121d --- /dev/null +++ b/roles/nextcloud/tasks/nc_installation.yml @@ -0,0 +1,168 @@ +--- +######### +# Run command line installation. +# the web server must be running by now in order to launch the installation +- name: Trigger all pending handlers + meta: flush_handlers + +- name: "[NC] - Setting directory ownership & permissions for the data folder" + file: + path: "{{ nextcloud_data_dir }}" + mode: "u=rwX,g=rX,o-rwx" + recurse: yes + state: directory + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + +- name: "[NC] - generate {{ nextcloud_admin_name }} password:" + set_fact: nextcloud_admin_pwd="{{ lookup( 'password', 'nextcloud_instances/'+ nextcloud_instance_name +'/web_admin.pwd length=10' ) }}" + when: nextcloud_admin_pwd is not defined + +- name: "[NC] - Set temporary permissions for command line installation." + file: + path: "{{ nextcloud_webroot }}" + state: directory + recurse: yes + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + +- block: + - name: "[NC] - removing possibly old or incomplete config.php" + file: + path: "{{ nextcloud_webroot }}/config/config.php" + state: absent + + - name: "[NC] - Run occ installation command" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: > + php occ maintenance:install + --database={{ nextcloud_tmp_backend }} + --database-host={{ nextcloud_db_host }} + --database-name={{ nextcloud_db_name }} + --database-user={{ nextcloud_db_admin }} + --database-pass={{ nextcloud_db_pwd }} + --admin-user={{ nextcloud_admin_name }} + --admin-pass={{ nextcloud_admin_pwd }} + --data-dir={{ nextcloud_data_dir }} + args: + chdir: "{{ nextcloud_webroot }}" + creates: "{{ nextcloud_webroot }}/config/config.php" + vars: + # mariadb is equal to mysql for occ + nextcloud_tmp_backend: "{{ 'mysql' if nextcloud_db_backend == 'mariadb' else nextcloud_db_backend }}" + notify: reload http + + - name: "[NC] - Verify config.php - check filesize" + stat: path="{{ nextcloud_webroot }}/config/config.php" + register: nc_installation_confsize + failed_when: nc_installation_confsize.stat.size is undefined or nc_installation_confsize.stat.size <= 100 + + - name: "[NC] - Verify config.php - php syntax check" + command: "php -l {{ nextcloud_webroot }}/config/config.php" + register: nc_installation_confphp + changed_when: False + failed_when: + - nc_installation_confphp.rc is defined + - nc_installation_confphp.rc != 0 + + rescue: + # - name: Unfix su issue with occ + # include_tasks: tasks/unfix_su.yml + # when: ansible_become_method == "su" + + - name: "[NC] - removing config.php when occ fail" + file: + path: "{{ nextcloud_webroot }}/config/config.php" + state: absent + failed_when: True + +- name: "[NC] - Set Trusted Domains" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ config:system:set trusted_domains {{ item.0 }} --value="{{ item.1 | ipwrap }}" + args: + chdir: "{{ nextcloud_webroot }}" + with_indexed_items: "{{ nextcloud_trusted_domain }}" + changed_when: true + +- name: "[NC] - Set Trusted Proxies" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ config:system:set trusted_proxies {{ item.0 }} --value="{{ item.1 }}" + args: + chdir: "{{ nextcloud_webroot }}" + with_indexed_items: "{{ nextcloud_trusted_proxies }}" + changed_when: true + +- name: "[NC] - Set Nextcloud settings in config.php" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ config:system:set {{ item.name }} --value="{{ item.value }}" + args: + chdir: "{{ nextcloud_webroot }}" + with_items: + - "{{ nextcloud_config_settings }}" + changed_when: true + +- name: "[NC] - Set Redis Server" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ config:system:set {{ item.name }} --value="{{ item.value }}" + args: + chdir: "{{ nextcloud_webroot }}" + with_items: + - "{{ nextcloud_redis_settings }}" + when: (nextcloud_install_redis_server | bool) + +- name: "[NC] - Install Cronjob" + cron: + name: "Nextcloud Cronjob" + minute: "*/15" + user: "{{ nextcloud_websrv_user }}" + job: "php {{ nextcloud_webroot }}/cron.php" + cron_file: "nextcloud" + when: (nextcloud_background_cron | bool) + +- name: "[NC] - Set Cron method to Crontab" + become_user: "{{ nextcloud_websrv_user }}" + become_flags: "{{ ansible_become_flags | default(omit) }}" + become: yes + command: php occ background:cron + args: + chdir: "{{ nextcloud_webroot }}" + when: (nextcloud_background_cron | bool) + +- name: "[NC] - Set Custom Mimetype" + copy: + dest: "{{ nextcloud_webroot }}/config/mimetypemapping.json" + src: files/nextcloud_custom_mimetypemapping.json + +- name: "[NC] - Ensure Nextcloud directories are 0750" + command: find {{ nextcloud_data_dir }} -type d -exec chmod -c 0750 {} \; + register: nc_installation_chmod_result + changed_when: "nc_installation_chmod_result.stdout != \"\"" + +- name: "[NC] - Ensure Nextcloud files are 0640" + command: find {{ nextcloud_data_dir }} -type f -exec chmod -c 0640 {} \; + register: nc_installation_chmod_result + changed_when: "nc_installation_chmod_result.stdout != \"\"" + +- name: "[NC] - Setting stronger directory ownership" + file: + path: "{{ nextcloud_webroot }}/{{ item }}/" + recurse: yes + owner: "{{ nextcloud_websrv_user }}" + group: "{{ nextcloud_websrv_group }}" + state: directory + with_items: + - apps + - assets + - config + - themes + - updater diff --git a/roles/nextcloud/tasks/redis_server.yml b/roles/nextcloud/tasks/redis_server.yml new file mode 100644 index 0000000..3663946 --- /dev/null +++ b/roles/nextcloud/tasks/redis_server.yml @@ -0,0 +1,13 @@ +--- +- name: "[REDIS] - Required packages are installed." + package: "name={{ redix_deps + php_pkg_spe }} state=present" + vars: + redix_deps: + - redis-server + notify: start redis + +- name: "[REDIS] - Redis configuration is present." + template: + dest: /etc/redis/redis.conf + src: templates/redis.conf.j2 + notify: restart redis diff --git a/roles/nextcloud/templates/nginx_nc.j2 b/roles/nextcloud/templates/nginx_nc.j2 new file mode 100644 index 0000000..4b7944f --- /dev/null +++ b/roles/nextcloud/templates/nginx_nc.j2 @@ -0,0 +1,146 @@ +################################################################################ +# This file was generated by Ansible for {{ansible_fqdn}} +# Do NOT modify this file by hand! +################################################################################ + +server { + server_name {{ nextcloud_trusted_domain | ipwrap | join(' ') }}; + + listen 80; + + # set max upload size and increase upload timeout: + client_max_body_size {{ nextcloud_max_upload_size }}; + client_body_timeout 300s; + fastcgi_buffers 64 4K; + +{% if (nextcloud_computed_major_version|int) > 13 %} + # Enable gzip but do not remove ETag headers + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; +{% else %} + # Disable gzip to avoid the removal of the ETag header + gzip off; +{% endif %} + + # Pagespeed is not supported by Nextcloud, so if your server is built + # with the `ngx_pagespeed` module, uncomment this line to disable it. + #pagespeed off; + + # HTTP response headers borrowed from Nextcloud `.htaccess` + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "none" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Remove X-Powered-By, which is an information leak + fastcgi_hide_header X-Powered-By; + + # Path to the root of your installation + root {{ nextcloud_webroot }}; + + # Specify how to handle directories -- specifying `/index.php$request_uri` + # here as the fallback means that Nginx always exhibits the desired behaviour + # when a client requests a path that corresponds to a directory that exists + # on the server. In particular, if that directory contains an index.php file, + # that file is correctly served; if it doesn't, then the request is passed to + # the front-end controller. This consistent behaviour means that we don't need + # to specify custom rules for certain paths (e.g. images and other assets, + # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus + # `try_files $uri $uri/ /index.php$request_uri` + # always provides the desired behaviour. + index index.php index.html /index.php$request_uri; + + # Rule borrowed from `.htaccess` to handle Microsoft DAV clients + location = / { + if ( $http_user_agent ~ ^DavClnt ) { + return 302 /remote.php/webdav/$is_args$args; + } + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Make a regex exception for `/.well-known` so that clients can still + # access it despite the existence of the regex rule + # `location ~ /(\.|autotest|...)` which would otherwise handle requests + # for `/.well-known`. + location ^~ /.well-known { + # The rules in this block are an adaptation of the rules + # in `.htaccess` that concern `/.well-known`. + + location = /.well-known/carddav { return 301 /remote.php/dav/; } + location = /.well-known/caldav { return 301 /remote.php/dav/; } + + location /.well-known/acme-challenge { try_files $uri $uri/ =404; } + location /.well-known/pki-validation { try_files $uri $uri/ =404; } + + # Let Nextcloud's API for `/.well-known` URIs handle all other + # requests by passing them to the front-end controller. + return 301 /index.php$request_uri; + } + + # Rules borrowed from `.htaccess` to hide certain paths from clients + location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } + location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } + + # Ensure this block, which passes PHP files to the PHP process, is above the blocks + # which handle static assets (as seen below). If this block is not declared first, + # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` + # to the URI, resulting in a HTTP 500 error response. + location ~ \.php(?:$|/) { + # Required for legacy support + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + set $path_info $fastcgi_path_info; + + try_files $fastcgi_script_name =404; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $path_info; + fastcgi_param HTTPS on; + + fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice + fastcgi_param front_controller_active true; # Enable pretty urls + fastcgi_pass php-handler; + + fastcgi_intercept_errors on; + fastcgi_request_buffering off; + } + + location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite)$ { + try_files $uri /index.php$request_uri; + expires 6M; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + + location ~ \.wasm$ { + default_type application/wasm; + } + } + + location ~ \.woff2?$ { + try_files $uri /index.php$request_uri; + expires 7d; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + } + + # Rule borrowed from `.htaccess` + location /remote { + return 301 /remote.php$request_uri; + } + + location / { + try_files $uri $uri/ /index.php$request_uri; + } +} diff --git a/roles/nextcloud/templates/nginx_php_handler.j2 b/roles/nextcloud/templates/nginx_php_handler.j2 new file mode 100644 index 0000000..3035cd8 --- /dev/null +++ b/roles/nextcloud/templates/nginx_php_handler.j2 @@ -0,0 +1,9 @@ +################################################################################ +# This file was generated by Ansible for {{ansible_fqdn}} +# Do NOT modify this file by hand! +################################################################################ + +upstream php-handler { + #server 127.0.0.1:9000; + server unix:{{ php_socket }}; +} diff --git a/roles/nextcloud/templates/redis.conf.j2 b/roles/nextcloud/templates/redis.conf.j2 new file mode 100644 index 0000000..ec52a3c --- /dev/null +++ b/roles/nextcloud/templates/redis.conf.j2 @@ -0,0 +1,957 @@ +################################################################################ +# This file was generated by Ansible for {{ansible_fqdn}} +# Do NOT modify this file by hand! +################################################################################ + +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################ GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize yes + +# When running daemonized, Redis writes a pid file in /var/run/redis.pid by +# default. You can specify a custom pid file location here. +pidfile /var/run/redis/redis-server.pid + +# Accept connections on the specified port, default is 6379. +# If port 0 is specified Redis will not listen on a TCP socket. +{% if nextcloud_redis_port is defined %} +port {{ nextcloud_redis_port|int}} +{% endif %} + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# By default Redis listens for connections from all the network interfaces +# available on the server. It is possible to listen to just one or multiple +# interfaces using the "bind" configuration directive, followed by one or +# more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +{% if nextcloud_redis_host is defined and (nextcloud_redis_port|int) > 0 %} +bind {{ nextcloud_redis_host }} +{% endif %} + +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /var/run/redis/redis.sock +# unixsocketperm 700 +{% if nextcloud_redis_port is defined and (nextcloud_redis_port|int) == 0 %} +unixsocket {{ nextcloud_redis_host }} +unixsocketperm 777 +{% endif %} + + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 60 seconds. +tcp-keepalive 0 + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile /var/log/redis/redis-server.log + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir /var/lib/redis + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +# masterauth + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU cache, or to set +# a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> remove the key with an expire set using an LRU algorithm +# allkeys-lru -> remove any key according to the LRU algorithm +# volatile-random -> remove a random key with an expire set +# allkeys-random -> remove a random key, any key +# volatile-ttl -> remove the key with the nearest expire time (minor TTL) +# noeviction -> don't expire at all, just return an error on write operations +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs a bit more CPU. 3 is very fast but not very accurate. +# +# maxmemory-samples 5 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have a exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Similarly to hashes, small lists are also encoded in a special way in order +# to save a lot of space. The special representation is only used when +# you are under the following limits: +list-max-ziplist-entries 512 +list-max-ziplist-value 64 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes diff --git a/roles/nextcloud/vars/main.yml b/roles/nextcloud/vars/main.yml new file mode 100644 index 0000000..97bb64e --- /dev/null +++ b/roles/nextcloud/vars/main.yml @@ -0,0 +1,58 @@ +--- +# vars file for nextcloud + +php_ver: '8.1' + +required_packages: + - nginx + - imagemagick + - php-gd + - php-ldap + - php-imap + - php-json + - php-curl + - php-intl + - php-fpm + - php-bcmath + - php-gmp + - php-imagick + - php-mbstring + - php-redis + - php-xml + - php-zip + - php-apcu + - php-mysql + +nextcloud_dl_file_name: + latest: "{{['latest', nextcloud_version_major]|reject('undefined')|join('-')}}" + releases: "{{['nextcloud', nextcloud_version_full]|reject('undefined')|join('-')}}" + prereleases: "nextcloud-{{[nextcloud_version_full, nextcloud_version_special]|reject('undefined')|join()}}" + daily: "nextcloud-{{nextcloud_version_major|d('')}}-daily-{{nextcloud_version_special|d('')}}" + +mysql_credential_file: + debian: '/etc/mysql/debian.cnf' + +nextcloud_max_upload_size_in_bytes: "{{ nextcloud_max_upload_size | human_to_bytes }}" +## finding the major version selected by the user : +## type : string +# the user will set either nextcloud_version_major or nextcloud_version_full +# to pin point a major/specific version. +# if nextcloud_version_major is set it can have 3 values: +# - 'master' : we just set an arbitrary high version (99) -- used for daily +# - a version number [M] : here's what we need. neat ! +# - stable[M] : just remove the 'stable' and here we go ! +# note: There is still a case when the user could use the master branch AND specify a very old date +# and that whould make the computed major version inconsistent. +# But I'm sure at 99.99% nobody wants to do that when using the daily channel. +# if nextcloud_version_full is set, it always have the form [Major.minor.patch] version number. (M.m.p) +# so the major version is the first element. +# if both variables are set we exclude the prerelease channel that do not use the major version on the first condition. +# if none are defined, either the user misconfigured the playbook that's calling the role +# and the play should fail - so we won't manage this case here - +# or nextcloud_get_latest is true which is the equivalent for us of using "master" +nextcloud_computed_major_version: > # type string + {%- if nextcloud_version_major is defined and nextcloud_version_channel not in ['prereleases'] -%} + {{99 if nextcloud_version_major == 'master' else (nextcloud_version_major|regex_replace('stable')) }} + {%- elif nextcloud_version_full is defined -%} + {{ (nextcloud_version_full.split('.'))[0] }} + {%- else -%}99{%- endif -%}