From Mageia wiki
Jump to: navigation, search


Drakconf multiflag.png
Other languages
English ; français ;
Synopsis:
This wiki provides guidance to install a Nextcloud server based on NGINX. Update of the Nextcloud server can then be done directly via the web updater provided in the admin interface.

Introduction - Purpose

This wiki page is an alternative to using the Mageia Nextcloud package. It proposes to use NGINX server instead of Apache. It offers also the choice between MaridaDB and PostgreSQL databases. Then, additional optimisations are proposed such as :

  • Redis memory cache
  • SSL certificate thanks to Letsencrypt
  • Intruder protection thanks to Fail2ban filter


With this option, you will be able to upgrade the Nextcloud version distributed by the stable branch releases.

It has been tested successfully on Mageia 8 with Nextcloud 21.


Warning!
If you plan to migrate from Mageia 7 with Nextcloud 20,
  • Do note the NGINX user on Mageia 7 is nginx:nginx and it becomes apache:apache on Mageia 8; update accordingly your server setup and any scripts, especially running the occ Nextcloud command.
  • The Nexcloud virtual host configuration file of Nginx needs some tweaks with Nextcloud 21 (you will find a proposal on this wiki page).
  • Of course, a proper backup of your server, database and data is mandatory.

Server installation

NGINX

I will not open a debate on which one is best between Nginx and Apache. The user competences and operation contexts will have also an influence on this choice. So, the main purpose here is to offer an alternative.

On technical side, it can be said NGINX is usually better on less powerful machines, like a Raspberry Pi, and uses less memory. In short, NGINX releases allocated resources to re-use them and adjusts the resource usage with a more on-demand principle.

Before getting into the details, let us have the look at the 2 main configuration files :

  • /etc/nginx/nginx.conf : it is a global configuration file. Global parameters, which will apply to all web sites on the server, are set here.
  • /etc/nginx/conf.d/*.conf : in this folder, NGINX will look for virtual hosts -vHosts (same philosophy than for Apache). It is where the specific Nextcloud host configuration file will be located. Global parameters will be inherited from /etc/nginx/nginx.conf. There is then no need to redefine them for each vHost but, if needed, they can be overridden by each vHosts.


Nginx can be installed through MCC or using, as root, the following command :

# urpmi nginx

Then, to check the installed version:

$ nginx -v

for instance:

nginx version: nginx/1.18.0


The service needs to be enabled at boot and started. MCC can be used or, as root, run the following command:

# systemctl enable --now nginx

Then, to test NGINX is installed properly, open your favorite web browser and type localhost in the address bar. You should get a beautiful NGINX & Mageia welcome page as below:

Nginx welcome.png

PHP

NGINX needs to be able to interpret PHP scripts. In our case, PHP will be run through FastCGI Process Manager (PHP-FPM). It is an efficient communication mean between PHP and the web server. We need then to install PHP and its modules required by Nextcloud. Nextcloud administrator manual provides more information on the modules. Let note the Apache web server doesn't need to be installed.

The installation can be made through MCC but it might be cumbersome because of the number of modules and because there is no specific package bundling them. The recommendation is then to use the following command line, as root :

# urpmi php php php-apcu php-bcmath php-bz2 php-ctype php-cgi php-cli php-curl php-dom php-exif php-fileinfo php-filter php-fpm php-gd php-gmp php-iconv php-imagick php-intl php-json php-ldap php-mbstring php-mysql php-opcache php-openssl php-pcntl php-posix php-session php-sodium php-tokenizer php-xml php-xmlreader php-xmlwriter php-zip php-zlib ffmpeg


Then, to check the installed version:

$ php -v

for instance:

PHP 8.0.2 (cli) (built: Feb 3 2021 22:24:23) ( ZTS ) Copyright (c) The PHP Group Zend Engine v4.0.2, Copyright (c) Zend Technologies with Zend OPcache v8.0.2, Copyright (c), by Zend Technologies
Dragon-head.png Here be dragons!
php -i command provides lot of details on the installed modules, as phpinfo() used just below in a script


Here as well, the php-fpm service needs to be enabled at boot and started:

# systemctl enable --now php-fpm

Connection of NGINX and PHP

Let us work now on connecting NGINX and PHP-FPM.

At first, we are going to write a small PHP script to run on our NGINX server.

To create this script, open your favorite text editor (nano or vi, for instance) in a console to create the file /var/www/html/info.php. Then, copy and paste the following block in:

<?php phpinfo(); ?>


Next step is to instruct NGINX where to find the PHP script to interpret them. We need to modify /etc/nginx/conf.d/default.conf Before doing so, it is recommended to save a copy of the original file by running, as root :

# cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf-original

Then, open the file /etc/nginx/conf.d/default.conf with your favorite text editor in a console, as root, in order to do the following changes:

  • Find the bloc starting by location ~ \.php$ and remove all the # comment sign until the }
  • Within this block, let change:
    • root html by root /var/www/html
    • fastcgi_pass 127.0.0.1:9000 by unix:/var/lib/php-fpm/php-fpm.sock;
    • fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name by fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name

The whole block should then look like:

location ~ \.php$ { root /var/www/html; fastcgi_pass unix:/var/lib/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

You will notice later fastcgi_pass parameter is now similar between NGINX and PHP-FPM to listen to the same socket.

To test the syntax of the change, run as root:

# nginx -t

which will return if no error:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

then, restart NGINX service:

# systemctl restart nginx


Then, to test NGINX and PHP-FPM work well together, open your favorite web browser and type localhost/info.php in the address bar. You should get a page listing some system and PHP modules installation, like above:

Php info.png

Preparation for Nextcloud

NGINX tweaks

In most cases, the default configuration file /etc/nginx/nginx.conf is quite good, but some points should be checked and adjusted.

  • 'user' : specifies the user under which the web server is running. This should always be 'apache' on Mageia 8 (used to be 'nginx' on Mageia 7 as for other Linux distributions like openSUSE).
  • 'worker_processes' : the number of threads nginx uses to handle client requests. 'Auto' specifies one thread per CPU core, which in most cases already represents the optimal setting.
  • 'server_tokens' : by specifying 'off', you ensure that NGINX does not reveal any version information (eg on error pages). Therefore, this option should be turned off for security reasons.
  • 'tcp_nodelay' and 'tcp_nopush' : they will be adjusted for performance reasons.
    • The 'tcp_nodelay' option allows to bypass the buffering algorithm, and then send the data as soon as it’s available.
      • NGINX uses 'tcp_nodelay' on HTTP keepalive connections. keepalive connections are sockets that stay open for a few times after sending data. keepalive allows to send more data without initiating a new connection and replaying a TCP 3 ways handshake for every HTTP request. This saves both time and sockets as they don’t switch to FIN_WAIT after every data transfer. Keep-alive is an option in HTTP 1.0 and HTTP 1.1 default behavior.
      • When downloading a full Web page, 'tcp_nodelay' can save you up to 0.2 second on every HTTP request.
      • On Nginx, the configuration option 'tcp_nopush' works as an opposite to 'tcp_nodelay'. Instead of optimizing delays, it optimizes the amount of data sent at once.
    • 'sendfile' :
      • This has lots to do with the association of 'sendfile', 'tcp_nodelay' and 'tcp_nopush' in nginx.conf. If you’re serving locally stored static files, 'sendfile' is totally essential to speed your Web server. Combined to 'sendfile', 'tcp_nopush ensures that the packets are full before being sent to the client. This greatly reduces network overhead and speeds the way files are sent. When it reaches the last packet, NGINX removes 'tcp_nopush'. Then, 'tcp_nodelay' forces the socket to send the data, saving up to 0.2 seconds per file.

So, at the end you need to make the following limited adjustments in /etc/nginx/ningx.conf. As root, edit /etc/nginx/nginx.conf and :

  • Check 'user' is set to 'apache'
  • Set 'worker_process' to 'auto'
  • In the section 'events', below 'worker_connections 1024', add 'use epoll'
  • Check 'sendfile is set to 'on'
  • Set
    • 'tcp_nopush' to 'on'
    • 'tcp_nodelay' to 'on
    • 'server_tokens' to 'off';


Your /etc/nginx/nginx.conf should look like that :

####################################################################### # # This is the main Nginx configuration file. # # More information about the configuration options is available on # * the English wiki - http://wiki.nginx.org/Main # * the Russian documentation - http://sysoev.ru/nginx/ # ####################################################################### #---------------------------------------------------------------------- # Main Module - directives that cover basic functionality # # http://wiki.nginx.org/NginxHttpMainModule # #---------------------------------------------------------------------- user apache; worker_processes auto; error_log /var/log/nginx/error.log; #error_log /var/log/nginx/error.log notice; #error_log /var/log/nginx/error.log info; pid /run/nginx.pid; #---------------------------------------------------------------------- # Events Module # # http://wiki.nginx.org/NginxHttpEventsModule # #---------------------------------------------------------------------- events { worker_connections 1024; use epoll; } #---------------------------------------------------------------------- # HTTP Core Module # # http://wiki.nginx.org/NginxHttpCoreModule # #---------------------------------------------------------------------- http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; server_tokens off; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; # Load config files from the /etc/nginx/conf.d directory # The default server is in conf.d/default.conf include /etc/nginx/conf.d/*.conf; }


Test NGINX syntax by :

# nginx -t

Restart NGINX service by :

# systemctl restart nginx

PHP configuration

The 2 main files to modify are /etc/php-fpm.d/www.conf and /etc/php.ini

/etc/php-fpm.d/www.conf

Save a copy by running, as root :

# cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf-original

Then, as root, edit /etc/php-fpm.d/www.conf and perform the followings actions:

  • Check these parameters:
'user' and 'group': The webserver user under which PHP runs is 'apache'.
user = apache group = apache
'listen': the communication between the web server and PHP should run via a socket.
listen = /var/lib/php-fpm/php-fpm.sock
  • Adjust the resources allocated to the server:
The formula is Total Max Processes = (Allocated RAM) / (Memory per php process)
If you think you can allocate 5Gb of RAM to your server, PHP using about 35Mb max of RAM per request, you can start with: (1024*5) / 35 = 146. So, round 'pm.max_children' to 150.
pm.max_children = 150
  • By default, environment variables are not set by PHP, but they are essential to run Nextcloud. Then, uncomment the following lines by removing the '; sign at the beginning of each of them:
env[HOSTNAME] = $HOSTNAME env[PATH] = /usr/local/bin:/usr/bin:/bin env[TMP] = /tmp env[TMPDIR] = /tmp env[TEMP] = /tmp
and add a comment in front of:
;env[PATH] = $PATH
  • To prevent a timeout during Nextcloud installation, set the following:
request_terminate_timeout = 300


/etc/php.ini

We will need also to adjust /etc/php.d/05_date.ini and /etc/php.d/99_opcache.ini which are embedded into.

At first, let us make a copy of the original files, as root:

# cp /etc/php.ini /etc/php.ini-original
# cp /etc/php.d/05_date.ini /etc/php.d/05_date.ini-original
# cp /etc/php.d/99_opcache.ini /etc/php.d/99_opcache.ini-original

Then, edit /etc/php.ini and make the following changes:

  • 'cgi.fix_pathinfo': from a security stand point (arbitrary script injection), it is important to prevent NGINX to send request to the PHP-FPM backend if the file doesn't exist. So, it is set to 0.
cgi.fix_pathinfo = 0
  • To prevent exposing the PHP version to the outside, set the following:
expose_php = Off
  • Nextcloud requires a PHP memory of 512Mb
memory_limit = 512M
  • Here, upload of up to 10Gb file will be allowed. To be adjusted according to your need; especially depending on the data storage quota of your Nextcloud users.
post_max_size = 10240M upload_max_filesize = 10240M
  • Output Buffering must be turned off or PHP will return memory-related errors:
output_buffering = Off

As root, edit /etc/php.d/05_date.ini and adjust your time zone. Example for Paris:

date.timezone = Europe/Paris

As root, edit /etc/php.d/99_opcache.ini and adjust some cache parameters:

opcache.enable_cli=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.revalidate_freq=1 opcache.save_comments=1


As root, restart both PHP-FPM and NGINX services :

# systemctl restart php-fpm
# systemctl restart nginx


Database

Looking at Nextcloud administrator manual, MySQL/MariaDB is the recommended database. However, PostgreSQL is also a valid and supported option.

Many tutorials make use of MariaDB. It looks like Nextcloud developers prefer MySQL/MariaDB as well. One reason being, according to some discussions on Nextcloud forum, there are more competencies on MySQL/MariaDB in the developer network. As it is more used, there is also more tests performed.

That being said, when testing Nextcloud 21 beta with Mageia 8 beta (i.e. using PHP8), the first tests were quickly successful using PostgreSQL and it took quite some time to get it to work with MariaDB. You will see also later PostgreSQL is easier to setup.

I have not performed any performance tests. Based on my readings, it is at least comparable. Actually, some have noticed better performance with PostgreSQL but I will be cautious before spreading this message as I haven't seen yet any detailed comparison with Nextcloud. More can be read here : Example of migration to PostgreSQL in 2017

You will have here the choice.


MariaDB

Installation is done either through MCC or, as root, by:

# urpmi mariadb php-pdo_mysql

then, enable the service at boot and start it:

# systemctl enable --now mysqld

To chech the installed version, run:

# mysql --version

which will return, for instance:

mysql Ver 15.1 Distrib 10.5.8-MariaDB, for Linux (x86_64) using readline 5.1


MariaDB needs to be secured. As root, run:

# mysql_secure_installation

and you will have to answer the following questions, with the answers stated in red:

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB

SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY! In order to log into MariaDB to secure it, we'll need the current password for the root user. If you've just installed MariaDB, and haven't set the root password yet, you should just press enter here.

Enter current password for root (enter for none): <ENTER> Enter

OK, successfully used password, moving on... Setting the root password or using the unix_socket ensures that nobody can log into the MariaDB root user without the proper authorisation.

You already have your root account protected, so you can safely answer 'n'.

Switch to unix_socket authentication [Y/n] N

Set root password? [Y/n] Y

New password: Your-MariaDB_password

Re-enter new password: Your_MariaDB_password

Password updated successfully! Reloading privilege tables.. ... Success!

By default, a MariaDB installation has an anonymous user, allowing anyone to log into MariaDB without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment.

Remove anonymous users? [Y/n] Y ... Success!

Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess the root password from the network.

Disallow root login remotely? [Y/n] Y ... Success!

By default, MariaDB comes with a database named 'test' that anyone can access. This is also intended only for testing and should be removed before moving into a production environment.

Remove test database and access to it? [Y/n] Y - Dropping test database... ... Success! - Removing privileges on test database... ... Success!

Reloading the privilege tables will ensure that all changes made so far will take effect immediately.

Reload privilege tables now? [Y/n] Y ... Success!

Cleaning up...

All done! If you've completed all of the above steps, your MariaDB installation should now be secure.

Thanks for using MariaDB!


It is time now to create your database for Nextcloud. It will have the following specification:

  • database name: nextcloud
  • database administrator: adminmdb
  • database administrator password: pwd-nc

Then, as root, run:

# mysql -uroot -p

You will have to enter the MariaDB password you just set and you will enter the command to create the Nextcloud database, as showed below after the MariaDB [(none)]> prompt:

Enter password: Your_MariaDB_password

Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 11 Server version: 10.5.8-MariaDB Mageia MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER adminmdb@localhost IDENTIFIED BY 'pwd-nc'; GRANT ALL PRIVILEGES on nextcloud.* to adminmdb@localhost; FLUSH privileges; quit;


PostgreSQL

Installation is done either through MCC or, as root, by:

# urpmi postgresql13 postgresql13-server php-pdo_pgsql

then, enable the service at boot and start it:

# systemctl enable --now postgresql

To chech the installed version, run:

# psql --version

which will return, for instance:

psql (PostgreSQL) 13.1


It is time now to create your database for Nextcloud. It will have the following specification:

  • database name: nextcloud
  • database administrator: adminpgsql
  • database administrator password: pwd-nc


Then, as root, run:

# sudo -u postgres psql

and you will have to enter the command to create the Nextcloud database, as showed below after the postgres=# prompt:

psql (13.1)

Type "help" for help.

postgres=# CREATE USER adminpgsql WITH PASSWORD 'pwd-nc';

CREATE ROLE

postgres=# CREATE DATABASE nextcloud TEMPLATE template0 ENCODING 'UNICODE';

CREATE DATABASE

postgres=# ALTER DATABASE nextcloud OWNER TO adminpgsql;

ALTER DATABASE

postgres=# GRANT ALL PRIVILEGES ON DATABASE nextcloud TO adminpgsql;

GRANT

postgres=# \q


As you see, it is a leaner configuration process than with MariaDB.

Nextcloud configuration

Virtual host for NGINX

There are 2 options to access your Nextcloud server, depending on how your domain is set:

  • You could reach it through my_domain/nextcloud. In that case, you need a domain name pointing to the fixed IP address of your router and with port forwarding set to reach the machine hosting the Nextcloud server.
    • This option is also valid if you restrict access to your Nextcloud server from a local network. In that case, you still need to assign a fixed IP to your Nextcloud machine, which will be an internal network address like 192.168.x.y
You will then reach your Nextcloud server through 192.168.x.y/nextcloud
  • You have a more advanced domain configuration and you have the ability to use a subdomain.
In that case, you could reach your server through, for instance, nextcloud.my_domain


These 2 options will be covered in this section, to configure the proper virtual host.


As you know already, NGINX works with virtual hosts allowing to run several applications (Web site, Nextcloud,...) on the same machine. Hence, we need now to create the virtual host (vHost) for Nextcloud.


Local network or fixed IP address

In this section, the server will be reachable thanks to a domain name forwarded by your router to your machine hosting Nextcloud. This domain name will be called my_domain and your Nextcloud server will be reachable at http://my_domain/nextcloud

It works as well if you want to restrict your Nextcloud server to your local network. In that case, instead of a domain name, you can use the local address of your machine. One way to get it, is to run the command ip -4 a. You might get an address looking like 192.168.x.y. Let make sure this address will be a fixed one assigned by your router. Instead of my_domain, you can then use 192.168.x.y in the vHost configuration file. Your Nextcloud server will be reachable at http://192.168.x.y/nextcloud


The vHost configuration file proposed comes from the administrator manual of Nextcloud, with little adjustments:

  • The socket name is set according to Mageia configuration.
  • The server name is called my_domain, as per above. Use 192.168.x.y is you remain within your local network.
  • The 'add_header Strict-Transport-Security' has been uncommented to activate it.
  • The maximum size of an upload 'client_max_body_size' is set to 10240M, to consistent with PHP configuration previously done.
  • All the part related to HTTPS and SSL certificates is removed. It will be covered in another section.


To create this Nextcloud vHost configuration, as root, with your favorite text editor, create the file /etc/nginx/conf.d/nextcloud.conf.

Copy and paste this block in:

upstream php-handler { server unix:/var/lib/php-fpm/php-fpm.sock; } server { listen 80; listen [::]:80; server_name my_domain; # HSTS settings # WARNING: Only add the preload option once you read about # the consequences in https://hstspreload.org/. This option # will add the domain to a hardcoded list that is shipped # in all major browsers and getting removed from this list # could take several months. add_header Strict-Transport-Security "max-age=15768000; includeSubDomains;" always; # Path to the root of the domain root /var/www; location = /robots.txt { allow all; log_not_found off; access_log off; } location /.well-known { # The following 6 rules are borrowed from `.htaccess` location = /.well-known/carddav { return 301 /nextcloud/remote.php/dav/; } location = /.well-known/caldav { return 301 /nextcloud/remote.php/dav/; } # Anything else is dynamically handled by Nextcloud location ^~ /.well-known { return 301 /nextcloud/index.php$uri; } try_files $uri $uri/ =404; } location ^~ /nextcloud { # set max upload size client_max_body_size 10240M; fastcgi_buffers 64 4K; # 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/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; # 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; # Specify how to handle directories -- specifying `/nextcloud/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/ /nextcloud/index.php$request_uri` # always provides the desired behaviour. index index.php index.html /nextcloud/index.php$request_uri; # Rule borrowed from `.htaccess` to handle Microsoft DAV clients location = /nextcloud { if ( $http_user_agent ~ ^DavClnt ) { return 302 /nextcloud/remote.php/webdav/$is_args$args; } } # Rules borrowed from `.htaccess` to hide certain paths from clients location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } location ~ ^/nextcloud/(?:\.|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 # `/nextcloud/index.php` to the URI, resulting in a HTTP 500 error response. location ~ \.php(?:$|/) { 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)$ { try_files $uri /nextcloud/index.php$request_uri; expires 6M; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets } location ~ \.woff2?$ { try_files $uri /nextcloud/index.php$request_uri; expires 7d; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets } location /nextcloud { try_files $uri $uri/ /nextcloud/index.php$request_uri; } } }


Restart NGINX and PHP-FPM services, as root:

# systemctl restart nginx
# systemctl restart php-fpm

Sub-domain address

In this section, the server will be reachable thanks to a subdomain of your domain name, forwarded by your router to your machine hosting Nextcloud. Your Nextcloud server will then be reachable at http://nextcloud.my_domain, for instance.

Let note the /nextcloud has disappeared.

The vHost configuration file proposed comes from the administrator manual of Nextcloud, with little adjustments:

  • The socket name is set according to Mageia configuration.
  • The server name is called nextcloud.my_domain, as per above.
  • The 'add_header Strict-Transport-Security' has been uncommented to activate it.
  • The maximum size of an upload 'client_max_body_size' is set to 10240M, to consistent with PHP configuration previously done.
  • All the part related to HTTPS and SSL certificates is removed. It will be covered in another section.


To create this Nextcloud vHost configuration, as root, with your favorite text editor, create the file /etc/nginx/conf.d/nextcloud.conf.

Copy and paste this block in:

upstream php-handler { server unix:/var/lib/php-fpm/php-fpm.sock; } server { listen 80; listen [::]:80; server_name nextcloud.my_domain; # HSTS settings # WARNING: Only add the preload option once you read about # the consequences in https://hstspreload.org/. This option # will add the domain to a hardcoded list that is shipped # in all major browsers and getting removed from this list # could take several months. add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always; # set max upload size client_max_body_size 10240M; fastcgi_buffers 64 4K; # 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/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; # 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 /var/www/nextcloud; # 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 following 6 rules are borrowed from `.htaccess` location = /.well-known/carddav { return 301 /remote.php/dav/; } location = /.well-known/caldav { return 301 /remote.php/dav/; } # Anything else is dynamically handled by Nextcloud location ^~ /.well-known { return 301 /index.php$uri; } try_files $uri $uri/ =404; } # 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 an HTTP 500 error response. location ~ \.php(?:$|/) { 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)$ { 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 ~ \.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 } location / { try_files $uri $uri/ /index.php$request_uri; } }


Restart NGINX and PHP-FPM services, as root:

# systemctl restart nginx
# systemctl restart php-fpm

Complete installation

Get Nextcloud

The Nextcloud server package will be downloaded directly from Nextcloud official website.

In the script below, NC_VER will have to be adjusted to reflect the Nextcloud major version number you target.

For a new installation, after checking the system requirements (especially the PHP version), the advice is to get the latest stable version available at nextcloud.com/install

At the time this wiki is written, connected to Mageia8 release, the latest version is 21. So NC_VER is here equal to 21.


Let remember from the warning in the introduction Nextcloud version 20 doesn't support PHP8 and, then, does not work with Mageia8.

Versions greater than 21 will work as long as they support PHP8.


The script below will download Nextcloud and will set the proper permissions.

Create the file get_nc.sh in your home folder, for instance.

Copy and paste this block in:

#!/bin/bash NC_VER="21" wget https://download.nextcloud.com/server/releases/latest-$NC_VER.zip -O /var/www/latest.zip unzip /var/www/latest.zip -d /var/www chown -R apache:apache /var/www/nextcloud rm /var/www/latest.zip

Then, make the script executable:

$ chmod a+x ./get_nc.sh

and run it, as root:

# ./get_nc.sh

Create data storage

Database will be put outside the server root directory. Ideally, it should be on a separate disk, independent from the system disk.

Create /data/ncdata folder to store Nextcloud data, as root:

# mkdir -p /data/ncdata
# chown -R apache:apache /data/ncdata

Nextcloud cron job

Nextcloud needs to get some maintenance tasks (database cleaning, checking for update,...) done regularly. There is already a script provided by Nextcloud to do that. It is just required to run it as a cron job.

If systemd is installed on the system, like in Mageia, a systemd timer could be an alternative to a crontab job. It is easier to manage (start and stop for maintenance for instance), especially when you have created many cron jobs.

This approach requires two files: /usr/lib/systemd/system/nextcloudcron.service and /usr/lib/systemd/system/nextcloudcron.timer


Create these two files with your favorite text editor, as root.


/usr/lib/systemd/system/nextcloudcron.service should look like this:

[Unit] Description=Nextcloud cron.php job [Service] User=apache ExecStart=/usr/bin/php -f /var/www/nextcloud/cron.php


/usr/lib/systemd/system/nextcloudcron.timer should look like this:

[Unit] Description=Run Nextcloud cron.php every 5 minutes [Timer] OnBootSec=5min OnUnitActiveSec=5min Unit=nextcloudcron.service [Install] WantedBy=timers.target


The important parts in the timer-unit are OnBootSec and OnUnitActiveSec. OnBootSec will start the timer 5 minutes after boot, otherwise, you would have to start it manually after every boot. OnUnitActiveSec will set a 5-minute timer after the service-unit was last activated.

Now, start and enable the timer by running this command, as root:

# systemctl enable --now nextcloudcron.timer

Firewall management

Firewall needs to have its HTTP port 80 opened. Have a look at the firewall documentation to do so in MCC, for instance.


Nextcloud Installation Wizard

Here is the last configuration step (or almost) for Nextcloud. You deserve this reward!

To reach the Nextcloud installation wizard page, open your favorite Web browser and go to either http://my_domain/nextcloud or http://local_ip/nextcloud or http//nextcloud.my_domain, depending on your configuration.

MariaDB

If you have chosen MariaDB, you will get an installation wizard page looking like this:

Wizard mariadb.png

On this page, create your Nextcloud administrator account and fill in the empty fields with the information used to create the MariaDB database:

  • Nextcloud administrator : for instance, ncadmin
  • Assign a strong password to it.
  • Database folder: /data/ncdata
  • MariadDB database user: adminmdb
  • MariadDB database user password: pwd-nc
  • MariaDB database name: nextcloud

Then, click 'Finish setup'

Note:
You have now a Nextcloud server you can use. The next steps will be about performance and security tuning.
PostgreSQL

If you have chosen PostgreSQL, you will get an installation wizard page looking like this:

Wizard postgresql.png

On this page, create your Nextcloud administrator account and fill in the empty fields with the information used to create the MariaDB database:

  • Nextcloud administrator : for instance, ncadmin
  • Assign a strong password to it.
  • Database folder: /data/ncdata
  • PostgreSQL database user: adminpgsql
  • PostgreSQL database user password: pwd-nc
  • PostgreSQL database name: nextcloud

Then, click 'Finish setup'

Note:
You have now a Nextcloud server you can use. The next steps will be about performance and security tuning.

Additional optimizations

Edit /var/www/nextcloud/config/config.php and paste the following block before the last ); signs at the end of the file. Don't forget to set the time zone and the phone region according to yours :

'logtimezone' => 'Europe/Paris', 'default_phone_region' => 'FR', 'remember_login_cookie_lifetime' => 0, 'session_keepalive' => false, 'log_rotate_size' => 110000, 'preview_libreoffice_path' => '/usr/bin/libreoffice', 'activity_expire_days' => 1, 'trashbin_retention_obligation' => 'auto, 7',

Redis

Nextcloud uses the so-called Transactional File Locking to realize locks with parallel access to files. Simply put, files are "lured" when in use. As a result, no data loss can occur if a file is opened or saved by two users at the same time, for example. In the default configuration, Nextcloud uses the database to manage the locks. However, with Redis, there is an in-memory database that is optimized for just such tasks and can therefore achieve improved performance in certain scenarios.

Installation

# urpmi redis php-redis

Restart PHP-FPM, as a new module was just installed :

# systemctl restart php-fpm


Enable and start Redis service :

# sudo systemctl enable --now redis


Resart Nginx server :

# systemctl restart nginx

Configuration file is /etc/redis.conf

Let us make a copy of the original file:

# cp /etc/redis.conf /etc/redis.conf-original


As for PHP, Redis recommends running the communication over a socket. The following settings must be made for in /etc/redis.conf :

port 0 unixsocket /tmp/redis.sock unixsocketperm 770

This sets the following options:

  • 'port': Redis listens on port 6379 by default. However, since we prefer a socket for communication, Redis should not listen to any port.
  • 'unixsocket': it defines the actual socket.
  • 'unixsocketperm': permissions for this socket. 700 comes by default in redis.conf and it would lead to the following error in Nextcloud: 'RedisException: Permission denied in /var/www/nextcloud/lib/private/RedisFactory.php:92'


To operate Redis, apache user of the serveur needs to be in the same group than redis. Otherwise, it leads to this typical Nextcloud error: Fatal internal error.

Hence, as root, run:

# usermod -a -G redis apache

Restart Redis service:

# systemctl restart redis

Then, nextcloud configuration file /var/www/nextcloud/config/config.php needs to get the block below added before the last ); sign at the end of the file:

'memcache.local' => '\OC\Memcache\APCu', 'memcache.distributed' => '\OC\Memcache\Redis', 'filelocking.enabled' => true, 'memcache.locking' => '\OC\Memcache\Redis', 'redis' => [ 'host' => '/tmp/redis.sock', 'port' => 0, 'dbindex' => 0, 'timeout' => 0.0, ],

By default recent versions of Redis don’t close the connection with the client if the client is idle for many seconds: the connection will remain open forever. However, if you don’t like this behavior, you can configure a timeout, so that if the client is idle for more than the specified number of seconds, the client connection will be closed.

Letsencrypt SSL certificate

So far, the server configuration offers only unsecured connections through HTTP. It might be enough if you restrict the access from a local network. However, opening the server to the outside with a domain name, it is highly recommended to install an HTTPS configuration.

Letsencrypt is a free and non profit certificate authority. Certbot is a client allowing to get and to maintain a certificate provided by Letsencrypt.


The SSL certificates themselves are the most important point when it comes to encrypting the connection over HTTPS. To further increase security, so-called Diffie-Hellman parameters should be used. It is about a secure key exchange when establishing a connection. The generation of the key must be done only once and Certbot will take care of it.

The web server must already be configured for certificates to be obtained from Let's Encrypt. To do this, connections must be possible via port 80.

urpmi certbot certbot-nginx

That command installed certbot along with the Nginx plugin. Don't be afraid by the number of python3 dependencies which will be installed.


To obtain a free SSL/TLS certificate from Let’s Encrypt for our domain, as root, run the following command, replacing my_emailL and my_domain with your own. If you use a subdomain, then you should use subdomain.my_domain in place of my_domain.

# certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email my_email -d my_domain --rsa-key-size 4096

Option explanations:

  • --nginx – Use the Nginx authenticator and installer
  • --agree-tos – Agree to the Terms of Service
  • --redirect – Enable 301 redirection from http:// to https://
  • --hsts – HTTP Strict Transport Security tells the browser to never request content from your site using HTTP
  • --staple-ocsp – Enable Online Certificate Status Protocol (OCSP) stapling, which is a safe and quick way of determining if an SSL certificate is valid or not.
  • --email – Your email where you’ll receive emails regarding the status of your certificate, and if you want you can also receive updates from the Electronic Frontier Foundation
  • -d – The domain for which you’re requesting the certificate. You can add up to 100 domains after it.
  • --rsa-key-size Diffie-Hellman parameters strength


You will get:

------------------------------------------------------------------------------- Congratulations! You have successfully enabled https://my_domain You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=my_domain ------------------------------------------------------------------------------- IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/my_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/my_domain/privkey.pem Your cert will expire on 20xx-xx-xx. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.


If you edit /etc/nginx/conf.d/nextcloud.conf, you will notice certbot has automatically updated it with the SSL certificate just created. It is thanks to the NGINX plug-in you have installed.

The encryption setup is stored in /etc/letsencrypt/options-ssl-nginx.conf They should follow the recommendation found in Mozilla best practice


The letsencrypt certificates are valid for 3 months and they need to be renewed. While installating certbot, you got systemd timer and service to take care of that.

However, the timer sends a renewal request every day at a random time. Doing it weekly is enough and, as the server needs to be stopped to renew, I prefer stopping my server in the middle of the night when most probably nobody will use it. It is up to you to adjust to your need or taste.


To do so, edit as root /usr/lib/systemd/system/certbot-renew.timer and adjust the Timer section as below to renew every week, on Thurday at 3:00AM.

[Timer] OnCalendar=Thu *-*-* 03:00:00 Persistent=true Unit=certbot-renew.service


In addition, to avoid getting the error cannot bind in IPv4 or IPv6 while renewing, you need to stop the NGINX server before and restart it after. To do so, edit as root /etc/sysconfig/certbot and set PRE_HOOK and POST_HOOK as below:

PRE_HOOK="--pre-hook 'systemctl stop nginx'" POST_HOOK="--post-hook 'systemctl start nginx'"


To complete this part, enable and start the timer by, as root:

# systemctl enable --now certbot-renew.timer

Enhance security with FAIL2BAN

Installation

First, install it in MCC or as root:

# urpmi fail2ban

The following dependencies will be installed:

Packet Version Revision Arch (media « Core Release ») fail2ban 0.11.2 1.mga8 noarch python3-dnspython 1.16.0 4.mga8 noarch (recommandé) python3-ecdsa 0.16.1 1.mga8 noarch (recommandé) python3-pycryptodome 3.9.8 1.mga8 x86_64 (recommandé) python3-pyinotify 0.9.6 10.mga8 noarch (recommandé) python3-systemd 234 6.mga8 x86_64

Enable at boot and start the fail2ban service :

# systemctl enable --now fail2ban


There are two types of configuration files: * .conf and * .local . The conf files come with the installation of Fail2ban. If you want to make changes to the configuration, you should never edit the conf files directly, as they can be overwritten at any time during an update by Fail2ban. For this, you can use a file with the same name, but with the file extension .local. The local files are loaded "on top" onto the conf files and thus overwrite the default settings. If the conf files are changed in an update of Fail2ban, the individual customizations in the local files are not affected. This means that updating Fail2ban does not inadvertently change the configuration.

General setup

As root, with your favorite text editor, create /etc/fail2ban/jail.local

and paste the following block inside:

# Do all your modifications to the jail's configuration in jail.local! [DEFAULT] findtime = 1800 # shorewall is Mageia firewall banaction = shorewall # send email action assuming Dragonfly Mail Agent is installed action = %(action_mwl)s mta = mail # This will ignore connection coming from local private network 192.168.x.x ignoreip = 192.168.0.0/16


Nextcloud setup

Nextcloud filter

As there is no filter for Nextcloud with the default installation, one is created. As root, create /etc/fail2ban/filter.d/nextcloud.conf with your favorite text editor.

Paste the following block inside:

[Definition] failregex = ^.*Login failed: '?.*'? \(Remote IP: '?<HOST>'?\).*$ ^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*$ ignoreregx =

Activation of Nextcloud filter

As root, create a new jail file /etc/fail2ban/jail.d/nextcloud.local with your favorite text editor and paste all the following block inside :

[nextcloud] backend = auto enabled = true port = 80,443 protocol = tcp filter = nextcloud logpath = /data/ncdata/nextcloud.log maxretry = 4 bantime = 360 action = %(action_mwl)s


Reload rules, as root:

# sudo fail2ban-client reload

FAIL2BAN test

Here are some tests to check FAIL2BAN works.

First, let us check its jail status by running as root:

# fail2ban-client status

It shows there is one filter or jail running which is the nextcloud one:

Status |- Number of jail: 1 `- Jail list: nextcloud


Then, more specifically, let us look at the status and ban history for nextcloud, by running as root:

# fail2ban-client status nextcloud

Here, nothing has been blocked yet as it returns:

Status for the jail: nextcloud |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- File list: /data/ncdata/nextcloud.log `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list:


So, now, let us check FAIL2BAN detects IP address failing to connect to your Nextcloud server several times. To do so, go to your Nextcloud welcome page with your favorite Web browser and, on purpose, enter twice a wrong password.


Then, as root, run the following command which will go through the Nextcloud log and will apply the nextcloud filter defined in FAIL2BAN:

# fail2ban-regex /data/ncdata/nextcloud.log /etc/fail2ban/filter.d/nextcloud.conf -v

You will get this output showing the IP ip.x.y.z has failed to connect twice:

Running tests ============= Use failregex filter file : nextcloud, basedir: /etc/fail2ban Use log file : /data/ncdata/nextcloud.log Use encoding : UTF-8 Results ======= Failregex: 2 total |- #) [# of hits] regular expression | 1) [2] ^.*Login failed: '?.*'? \(Remote IP: '?<HOST>'?\).*$ | ip.x.y.z Wed Jan 06 12:29:18 2021 | ip.x.y.z Wed Jan 06 12:29:20 2021 | 2) [0] ^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*$


Dragon-head.png Here be dragons!
It is recommended to run this test each time you upgrade to a new major version of Nextcloud, in case the log file format has changed.

Check firewall action and unban

Just before, FAIL2BAN detection was checked. In this section, it will be checked the firewall blocks the IP villain after 4 failures.

If you do this test on the server using its local address to reach Nextcloud, there is default protection in FAIL2BAN to prevent blocking the server IP itself. So, to do this test, add the following at the end of /etc/fail2ban/jail.local

ignoreself = false

DO NOT FORGET TO REMOVE IT AFTER THE TEST !

If you use a domain or a subdomain address, there is no need to add this line.


Then, simply enter an incorrect password in your Nextcloud login four times. Fail2ban has then banned your IP and you should have received an e-mail if you have configured the mailing (Dma_Dragonfly_Mail_Agent for instance).

To control which IPs are currently banned for Nextcloud, the following command is sufficient, as root:

# fail2ban-client status nextcloud

returning the expected 4 failed attempts and IP ip.x.y.z is banned:

Status for the jail: nextcloud |- Filter | |- Currently failed: 1 | |- Total failed: 4 | `- File list: /data/ncdata/nextcloud.log `- Actions |- Currently banned: 1 |- Total banned: 1 `- Banned IP list: ip.x.y.z

To check the firewall did bloc the intruder, run as root:

# shorewall show dynamic

returning:

Shorewall 5.2.8 Chain dynamic at localhost - mer. 06 janv. 2021 15:42:48 CET Counters reset mer. 06 janv. 2021 15:12:22 CET Chain dynamic (1 references) pkts bytes target prot opt in out source destination 0 0 reject all -- * * ip.x.y.z. 0.0.0.0/0


GOOD JOB !  :-)


As root, this command unbans the IP ip.x.y.z used in our example above:

# fail2ban-client set nextcloud unbanip ip.x.y.z


Useful links