Nextcloud 17 installation guide (Ubuntu 18.04)

Ubuntu 18.04 / NGINX 1.17 / MariaDB 10.4

Following this guide you will be able to install and configure Nextcloud 17 latest based on Ubuntu 18.04.x LTS 64Bit, NGINX 1.17, TLSv1.3, PHP 7.3, MariaDB 10.4, Redis, UFW and fail2ban. You will achieve an A+ rating from both, Nextcloud and Qualys SSL Labs. We will request and implement your ssl certificate(s) from Let’s Encrypt – you only have to ammend all the red marked values like your.dedyn.io, 192.168.2.x with regards to your environment!

Find the entire update history here.


Table of content

  1. Prepare your server and install nginx
  2. PHP 7.3
  3. MariaDB 10.4
  4. Redis-server
  5. Nextcloud (SSL enabled, A+)
  6. Hardenings (fail2ban and ufw)

1. Prepare your server

Change into sudo mode

sudo -s

Prepare your server for the installation itself:

apt install curl gnupg2 git lsb-release ssl-cert ca-certificates apt-transport-https tree locate software-properties-common dirmngr screen htop net-tools zip unzip curl ffmpeg ghostscript libfile-fcntllock-perl -y

Add new software repositories

cd /etc/apt/sources.list.d
echo "deb [arch=amd64] http://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" | tee nginx.list
echo "deb [arch=amd64] http://ppa.launchpad.net/ondrej/php/ubuntu $(lsb_release -cs) main" | tee php.list
echo "deb [arch=amd64] http://ftp.hosteurope.de/mirror/mariadb.org/repo/10.4/ubuntu $(lsb_release -cs) main" | tee mariadb.list

Download the required keys to trust all the new sources:

curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com:443 4F4EA0AAE5267A6C
apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com:443 0xF1656F24C74CD1D8

Update your server and generate self signed certificates:

apt update && apt upgrade -y
make-ssl-cert generate-default-snakeoil -y

Remove old nginx software if exists:

apt remove nginx nginx-extras nginx-common nginx-full -y --allow-change-held-packages

Install and configure NGINX

First ensure Apache(2) isn’t running otherwise NGINX won’t start because the required port (:80) would be in use by Apache(2):

systemctl stop apache2.service && systemctl disable apache2.service
apt install nginx -y
systemctl enable nginx.service

Change the default nginx configuration:

mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak && touch /etc/nginx/nginx.conf
nano /etc/nginx/nginx.conf

Paste all the following rows to the new file. Substitute the red marked parameters properly:

user www-data;
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on; use epoll;
}
http {
server_names_hash_bucket_size 64;
upstream php-handler {
server unix:/run/php/php7.3-fpm.sock;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.0/24;
# please customize the ip-range properly
real_ip_header X-Forwarded-For;
real_ip_recursive on;
include /etc/nginx/mime.types;
#include /etc/nginx/proxy.conf;
#include /etc/nginx/ssl.conf;
#include /etc/nginx/header.conf;
#include /etc/nginx/optimization.conf; 
default_type application/octet-stream;
sendfile on;
send_timeout 3600;
tcp_nopush on;
tcp_nodelay on;
open_file_cache max=500 inactive=10m;
open_file_cache_errors on;
keepalive_timeout 65;
reset_timedout_connection on;
server_tokens off;
resolver 192.168.2.1 valid=30s;
#resolver 127.0.0.53 valid=30s; *is recommended but requires a valid DNS configuration*
resolver_timeout 5s;
include /etc/nginx/conf.d/*.conf;
}

Test and restart the webserver

nginx -t && service nginx restart

Create four folders and apply the appropriate permissions:

mkdir -p /var/nc_data /var/www/letsencrypt
chown -R www-data:www-data /var/nc_data /var/www

The preparation and installation of nginx has finished and we will install PHP latest in the next chapter.


2. Install and configure PHP 7.3 (fpm)

The PHP repository was enabled in chapter 1 – so just perform the following statement to install PHP latest as needed by and recommended for Nextcloud:

apt update && apt install php7.3-fpm php7.3-gd php7.3-mysql php7.3-curl php7.3-xml php7.3-zip php7.3-intl php7.3-mbstring php7.3-json php7.3-bz2 php7.3-ldap php-apcu imagemagick php-imagick php-smbclient -y

Awesome, PHP 7.3 is already installed. Verify your timezone settings :

date

If it is not set properly customize it as examplarily shown for Germany:

timedatectl set-timezone Europe/Berlin

Backup and then tweak PHP for optimization and security reasons:

cp /etc/php/7.3/fpm/pool.d/www.conf /etc/php/7.3/fpm/pool.d/www.conf.bak
cp /etc/php/7.3/cli/php.ini /etc/php/7.3/cli/php.ini.bak
cp /etc/php/7.3/fpm/php.ini /etc/php/7.3/fpm/php.ini.bak
cp /etc/php/7.3/fpm/php-fpm.conf /etc/php/7.3/fpm/php-fpm.conf.bak
cp /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xml.bak
sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.3/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/7.3/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.3/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.3/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.3/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.3/cli/php.ini
sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/7.3/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.3/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/7.3/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.3/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.3/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.3/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.3/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=8/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/rights=\"none\" pattern=\"PS\"/rights=\"read|write\" pattern=\"PS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"EPI\"/rights=\"read|write\" pattern=\"EPI\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"PDF\"/rights=\"read|write\" pattern=\"PDF\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"XPS\"/rights=\"read|write\" pattern=\"XPS\"/" /etc/ImageMagick-6/policy.xml

Restart both services, php and nginx:

service php7.3-fpm restart
service nginx restart

PHP was successfully installed and configured – we will go ahead with the installation of MariaDB.


3. Install, harden and configure MariaDB 10.4

Install MariaDB by issuing the following statement:

apt update && apt install mariadb-server -y

How to upgrade from MariaDB v. 10.3 to v. 10.4

Verify your database server version:

mysql --version

A version like …

mysql  Ver 15.1 Distrib 10.4.6-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

should appear.

Harden and secure your MariaDB by issuing:

mysql_secure_installation
Switch to unix_socket authentication [Y/n] N
Enter current password for root (enter for none): <ENTER> or type the password
Set root password? [Y/n] Y

If already set during the MariaDB installation you will be asked wether to change or keep the password

Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

With regards to Nextcloud we optimize the configuration of MariaDB:

service mysql stop
mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak
nano /etc/mysql/my.cnf

Paste all the following rows:

[client]
 default-character-set = utf8mb4
 port = 3306
 socket = /var/run/mysqld/mysqld.sock
[mysqld_safe]
 log_error = /var/log/mysql/mysql_error.log
 nice = 0
 socket = /var/run/mysqld/mysqld.sock
[mysqld]
 basedir = /usr
 bind-address = 127.0.0.1
 binlog_format = ROW
 bulk_insert_buffer_size = 16M
 character-set-server = utf8mb4
 collation-server = utf8mb4_general_ci
 concurrent_insert = 2
 connect_timeout = 5
 datadir = /var/lib/mysql
 default_storage_engine = InnoDB
 expire_logs_days = 10
 general_log_file = /var/log/mysql/mysql.log
 general_log = 0
 innodb_buffer_pool_size = 1024M
 innodb_buffer_pool_instances = 1
 innodb_flush_log_at_trx_commit = 2
 innodb_log_buffer_size = 32M
 innodb_max_dirty_pages_pct = 90
 innodb_file_per_table = 1
 innodb_open_files = 400
 innodb_io_capacity = 4000
 innodb_flush_method = O_DIRECT
 key_buffer_size = 128M
 lc_messages_dir = /usr/share/mysql
 lc_messages = en_US
 log_bin = /var/log/mysql/mariadb-bin
 log_bin_index = /var/log/mysql/mariadb-bin.index
 log_error = /var/log/mysql/mysql_error.log
 log_slow_verbosity = query_plan
 log_warnings = 2
 long_query_time = 1
 max_allowed_packet = 16M
 max_binlog_size = 100M
 max_connections = 200
 max_heap_table_size = 64M
 myisam_recover_options = BACKUP
 myisam_sort_buffer_size = 512M
 port = 3306
 pid-file = /var/run/mysqld/mysqld.pid
 query_cache_limit = 2M
 query_cache_size = 64M
 query_cache_type = 1
 query_cache_min_res_unit = 2k
 read_buffer_size = 2M
 read_rnd_buffer_size = 1M
 skip-external-locking
 skip-name-resolve
 slow_query_log_file = /var/log/mysql/mariadb-slow.log
 slow-query-log = 1
 socket = /var/run/mysqld/mysqld.sock
 sort_buffer_size = 4M
 table_open_cache = 400
 thread_cache_size = 128
 tmp_table_size = 64M
 tmpdir = /tmp
 transaction_isolation = READ-COMMITTED
 user = mysql
 wait_timeout = 600
[mysqldump]
 max_allowed_packet = 16M
 quick
 quote-names
[isamchk]
 key_buffer = 16M 

Restart MariaDB and connect to MariaDB:

service mysql restart
mysql -uroot -p

Create

  • the database nextcloud
  • the user nextcloud
  • and its password (nextcloud):
CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER nextcloud@localhost identified by 'nextcloud'; GRANT ALL PRIVILEGES on nextcloud.* to nextcloud@localhost; FLUSH privileges; quit;

Verify the transaction isolation level is set to READ_Commit and the collation is set to UTF8MB4 properly:

mysql -h localhost -uroot -p -e "SELECT @@TX_ISOLATION; SELECT SCHEMA_NAME 'database', default_character_set_name 'charset', DEFAULT_COLLATION_NAME 'collation' FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='nextcloud'"

The resultset should consist of “READ-COMMITTED” and “utf8mb4_general_ci” … go ahead with the installation of the redis-server.


4. Install and configure Redis

Install the redis-server to optimize Nextclouds performance and to minimize the load on the database:

apt update
apt install redis-server php-redis -y

Change the redis configuration and set the group membership properly:

cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
sed -i "s/port 6379/port 0/" /etc/redis/redis.conf
sed -i s/\#\ unixsocket/\unixsocket/g /etc/redis/redis.conf
sed -i "s/unixsocketperm 700/unixsocketperm 770/" /etc/redis/redis.conf
sed -i "s/# maxclients 10000/maxclients 512/" /etc/redis/redis.conf
usermod -aG redis www-data
cp /etc/sysctl.conf /etc/sysctl.conf.bak
sed -i '$avm.overcommit_memory = 1' /etc/sysctl.conf

Now reboot your server once:

reboot now

We are now ready to install Nextcloud.


5. Prepare NGINX for Nextcloud (create all vhosts)

First create all the configuration and webhost files (aka vhost). Change into sudo mode and create the /etc/nginx/conf.d/nextcloud.conf (vhost):

sudo -s
[ -f /etc/nginx/conf.d/default.conf ] && mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
touch /etc/nginx/conf.d/default.conf
nano /etc/nginx/conf.d/nextcloud.conf

Paste all the following rows and customize the red marked parameters:

server {
server_name your.dedyn.io;
listen 80 default_server;
listen [::]:80 default_server;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name your.dedyn.io;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
root /var/www/nextcloud/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
#SOCIAL app enabled? Please uncomment the following row
#rewrite ^/.well-known/webfinger /public.php?service=webfinger last;
#WEBFINGER app enabled? Please uncomment the following two rows.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
client_max_body_size 10240M;
location / {
rewrite ^ /index.php;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ^~ /apps/rainloop/app/data {
deny all;
}
location ~ .(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100M;
mp4_max_buffer_size 1024M;
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+).php(?:$|\/) {
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
location ~ .(?:css|js|woff2?|svg|gif|map|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$request_uri;
access_log off;
expires 360d;
}
}

Create the letsencrypt.conf (vhost):

nano /etc/nginx/conf.d/letsencrypt.conf

Paste all the following rows:

server
{
server_name 127.0.0.1;
listen 127.0.0.1:81 default_server;
charset utf-8;
location ^~ /.well-known/acme-challenge
{
default_type text/plain;
root /var/www/letsencrypt;
}
}

Create the ssl.conf:

nano /etc/nginx/ssl.conf

Paste all the following rows:

ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_trusted_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
#ssl_certificate /etc/letsencrypt/rsa-certs/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/rsa-certs/privkey.pem;
#ssl_certificate /etc/letsencrypt/ecc-certs/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/ecc-certs/privkey.pem;
#ssl_trusted_certificate /etc/letsencrypt/ecc-certs/chain.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384';
ssl_ecdh_curve X448:secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;

Create the proxy.conf :

nano /etc/nginx/proxy.conf

Paste all the following rows:

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_redirect off;

Create the header.conf:

nano /etc/nginx/header.conf

Paste the following rows:

add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Robots-Tag none; add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Frame-Options "SAMEORIGIN";

Create the optimization.conf:

nano /etc/nginx/optimization.conf

Paste all the following rows:

fastcgi_hide_header X-Powered-By;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
fastcgi_connect_timeout 3600;
fastcgi_buffers 64 64K;
fastcgi_buffer_size 256k;
fastcgi_busy_buffers_size 3840K;
fastcgi_cache_key $http_cookie$request_method$host$request_uri;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
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;
gzip_disable "MSIE [1-6]\.";

Create the php_optimization.conf:

nano /etc/nginx/php_optimization.conf

Paste all the following rows:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_cache_valid 404 1m;
fastcgi_cache_valid any 1h;
fastcgi_cache_methods GET HEAD;

To enhance the security just create your own dhparam.pem:

openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Please be patient, it will take a while depending on your hardware. If your dhparam.pem exists please customize and restart your webserver:

sed -i s/\#\include/\include/g /etc/nginx/nginx.conf
service nginx restart

Download and extract the Nextcloud software, then request your SSL certificates from Let’s Encrypt using acme:

cd /usr/local/src
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
tar -xjf latest.tar.bz2 -C /var/www && chown -R www-data:www-data /var/www/ && rm -f latest.tar.bz2

Create a technical user to install and renew your ssl certificates

adduser acmeuser
usermod -a -G www-data acmeuser

Issue visudo

visudo

and add the the following row at the end of the file

acmeuser ALL=NOPASSWD: /bin/systemctl reload nginx.service

to e.g. restart nginx without a password.

To request ssl certificates from letsencrypt just install acme and request your ssl-certificate(s):

su - acmeuser
curl https://get.acme.sh | sh
exit

Create three folders to request and store your ssl certificates to (substitute your.dedyn.io):

mkdir -p /var/www/letsencrypt/.well-known/acme-challenge /etc/letsencrypt/rsa-certs /etc/letsencrypt/ecc-certs
chmod -R 775 /var/www/letsencrypt /etc/letsencrypt && chown -R www-data:www-data /var/www/ /etc/letsencrypt
su - acmeuser
acme.sh --issue -d your.dedyn.io --keylength 4096 -w /var/www/letsencrypt --key-file /etc/letsencrypt/rsa-certs/privkey.pem --ca-file /etc/letsencrypt/rsa-certs/chain.pem --cert-file /etc/letsencrypt/rsa-certs/cert.pem --fullchain-file /etc/letsencrypt/rsa-certs/fullchain.pem
acme.sh --issue -d your.dedyn.io --keylength ec-384 -w /var/www/letsencrypt --key-file /etc/letsencrypt/ecc-certs/privkey.pem --ca-file /etc/letsencrypt/ecc-certs/chain.pem --cert-file /etc/letsencrypt/ecc-certs/cert.pem --fullchain-file /etc/letsencrypt/ecc-certs/fullchain.pem
exit

Apply the appropiate permissions using a permissions.sh script:

vi /root/permissions.sh

Paste all the following rows:

#!/bin/bash
find /var/www/ -type f -print0 | xargs -0 chmod 0640
find /var/www/ -type d -print0 | xargs -0 chmod 0750
chown -R www-data:www-data /var/www/
chown -R www-data:www-data /var/nc_data/
chmod 0644 /var/www/nextcloud/.htaccess
chmod 0644 /var/www/nextcloud/.user.ini
chmod 600 /etc/letsencrypt/rsa-certs/fullchain.pem
chmod 600 /etc/letsencrypt/rsa-certs/privkey.pem
chmod 600 /etc/letsencrypt/rsa-certs/chain.pem
chmod 600 /etc/letsencrypt/rsa-certs/cert.pem
chmod 600 /etc/letsencrypt/ecc-certs/fullchain.pem
chmod 600 /etc/letsencrypt/ecc-certs/privkey.pem
chmod 600 /etc/letsencrypt/ecc-certs/chain.pem
chmod 600 /etc/letsencrypt/ecc-certs/cert.pem
chmod 600 /etc/ssl/certs/dhparam.pem
exit 0

Mark the script as executable and issue it:

chmod +x /root/permissions.sh
/root/permissions.sh

Remove the links to your self signed certificates and restart nginx:

sed -i '/ssl-cert-snakeoil/d' /etc/nginx/ssl.conf
sed -i s/\#\ssl/\ssl/g /etc/nginx/ssl.conf
service nginx restart

From now your webserver interacts withs your ssl certificates from let’s encrypt. Go ahead and install your Nextcloud software silently by issuing the following statement:

sudo -u www-data php /var/www/nextcloud/occ maintenance:install --database "mysql" --database-name "nextcloud" --database-user "nextcloud" --database-pass "nextcloud" --admin-user "YourNextcloudAdmin" --admin-pass "YourNextcloudAdminPasssword" --data-dir "/var/nc_data"

Information:

–database-name “nextcloud” : As set above while creating the database

–database-user “nextcloud” : As set above while creating the database user

–database-pass “nextcloud” : As set above while creating the database user password

–admin-user “YourNextcloudAdmin” : your free choice

–admin-pass “YourNextcloudAdminPasssword” : your free choice

Wait for the installation and finally make amendments to your Nextcloud ‘config.php’ as the webuser www-data:

1. Set your trusted domain:

sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 0 --value=your.dedyn.io

2. Set your domain as overwrite.cli.url:

sudo -u www-data php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value=https://your.dedyn.io

Enhance your Nextcloud configuration – back up the current config.php and issue the statements below:

sudo -u www-data cp /var/www/nextcloud/config/config.php /var/www/nextcloud/config/config.php.bak 

Expand your Nextcloud config.php:

sudo -u www-data sed -i 's/^[ ]*//' /var/www/nextcloud/config/config.php
sudo -u www-data sed -i '/);/d' /var/www/nextcloud/config/config.php
sudo -u www-data cat <<EOF >>/var/www/nextcloud/config/config.php
'activity_expire_days' => 14,
'auth.bruteforce.protection.enabled' => true,
'blacklisted_files' => 
array (
0 => '.htaccess',
1 => 'Thumbs.db',
2 => 'thumbs.db',
),
'cron_log' => true,
'enable_previews' => true,
'enabledPreviewProviders' => 
array (
0 => 'OC\Preview\PNG',
1 => 'OC\Preview\JPEG',
2 => 'OC\Preview\GIF',
3 => 'OC\Preview\BMP',
4 => 'OC\Preview\XBitmap',
5 => 'OC\Preview\Movie',
6 => 'OC\Preview\PDF',
7 => 'OC\Preview\MP3',
8 => 'OC\Preview\TXT',
9 => 'OC\Preview\MarkDown',
),
'filesystem_check_changes' => 0,
'filelocking.enabled' => 'true',
'htaccess.RewriteBase' => '/',
'integrity.check.disabled' => false,
'knowledgebaseenabled' => false,
'logfile' => '/var/nc_data/nextcloud.log',
'loglevel' => 2,
'logtimezone' => 'Europe/Berlin',
'log_rotate_size' => 104857600,
'maintenance' => false,
'memcache.local' => '\OC\Memcache\APCu',
'memcache.locking' => '\OC\Memcache\Redis',
'overwriteprotocol' => 'https',
'preview_max_x' => 1024,
'preview_max_y' => 768,
'preview_max_scale_factor' => 1,
'redis' => 
array (
'host' => '/var/run/redis/redis-server.sock',
'port' => 0,
'timeout' => 0.0,
),
'quota_include_external_storage' => false,
'share_folder' => '/Shares',
'skeletondirectory' => '',
'theme' => '',
'trashbin_retention_obligation' => 'auto, 7',
'updater.release.channel' => 'stable',
);
EOF

Edit Nextclouds file ‘.user.ini’ and adjust some Nextcloud apps as user www-data:

sudo -u www-data sed -i "s/output_buffering=.*/output_buffering='Off'/" /var/www/nextcloud/.user.ini
sudo -u www-data php /var/www/nextcloud/occ app:disable survey_client
sudo -u www-data php /var/www/nextcloud/occ app:disable firstrunwizard
sudo -u www-data php /var/www/nextcloud/occ app:enable admin_audit
sudo -u www-data php /var/www/nextcloud/occ app:enable files_pdfviewer

Your Nextcloud is now installed, optimized and secured – finally restart all relevant services:

service nginx stop
service php7.3-fpm stop
service mysql restart
service php7.3-fpm restart
service redis-server restart
service nginx restart

Add a Nextcloud cronjob for www-data:

crontab -u www-data -e

Paste the following rows:

*/5 * * * * php -f /var/www/nextcloud/cron.php > /dev/null 2>&1

Switch from Ajax to Cron using Nextclouds cli:

sudo -u www-data php /var/www/nextcloud/occ background:cron

6. Hardenings (fail2ban and ufw)

First install and configure fail2ban to harden your server:

apt update && apt install fail2ban -y

Paste all the following rows to the fail2ban filter for Nextcloud:

nano /etc/fail2ban/filter.d/nextcloud.conf
[Definition]
failregex=^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: ''\)","level":2,"time":".*"}$
          ^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user,:".*","app":"no app in context".*","method":".*","message":"Login failed: '.*' \(Remote IP: ''\)".*}$
          ^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user":".*","app":".*","method":".*","url":".*","message":"Login failed: .* \(Remote IP: \).*}$

Create a new jail file:

nano /etc/fail2ban/jail.d/nextcloud.local

Paste all the following rows:

[nextcloud]
backend = auto
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
maxretry = 5
bantime = 36000
findtime = 36000
logpath = /var/nc_data/nextcloud.log

[nginx-http-auth]
enabled = true

Restart the fail2ban-service and verify the fail2ban-status:

service fail2ban restart
fail2ban-client status nextcloud

At least we will install and customize the ufw (uncomplicated firewall):

Before issuing the following statement verify the used ports. If e.g. SSH operates on a different port than 22 you won’t be able to connect via ssh anymore – please customize the ports if necessary!

apt install ufw -y
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 22/tcp

Set the firewall logging to medium and deny non defined incoming connections. Finally enable and restart ufw by issuing the last statement:

ufw logging medium
ufw default deny incoming
ufw enable
service ufw restart

Enjoy your personal data in your secured and hardened Nextcloud! If you are interested in operating more web applications don’t hesitate to read the guide: Roundcube, WordPress, Shellinabox and Pi-hole behind a NGINX reverse proxy.

Don’t forget to backup your Nextcloud

Find more instructions here: Nextcloud backup and restore


Carsten Rieger

Carsten Rieger is a senior system engineer in full-time and also working as an IT freelancer. He is working with linux environments for more than 15 years, an Open Source enthusiast and highly motivated on linux installation and troubleshooting. Mostly working with Debian/Ubuntu Linux, Nginx and Apache web server, MariaDB/MySQL/PostgreSQL, PHP, Cloud infrastructure (e.g. Nextcloud) and other open source projects (e.g. Roundcube) and in voluntary work for the Dr. Michael & Angela Jacobi Stiftung for more than 7 years.