nextcloud / roundcube

Roundcube beside Nextcloud


We will run Roundcube (youremail.dedyn.io) beside Nextcloud (yourcloud.dedyn.io) on NGINX and will enhance security (using TOTP (2FA) and fail2ban) and functionality (using a carddav plugin for Nextcloud contacts).



To run Roundcube beside Nextcloud the NGINX configuration has to be enhanced. First backup/move your old *.conf files inside the nginx-directories but the letsencrypt.conf remains. Please substitute all the red values below properly to your environment.

sudo -s
service nginx stop
mv /etc/nginx/conf.d/gateway.conf /etc/nginx/conf.d/gateway.conf.bak
mv /etc/nginx/conf.d/nextcloud.conf /etc/nginx/conf.d/nextcloud.conf.bak
mv /etc/nginx/ssl.conf /etc/nginx/ssl.conf.bak
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

Then create a new /etc/nginx/ssl.conf:

ssl_certificate /etc/letsencrypt/live/yourcloud.dedyn.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourcloud.dedyn.io/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourcloud.dedyn.io/fullchain.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.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_stapling on;
ssl_stapling_verify on;

Then create a new /etc/nginx/header.conf:

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

Then create a new /etc/nginx/optimization.conf:

fastcgi_buffers 64 8K;
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]\.";

Then create a new /etc/nginx/php_optimization.conf:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_cache_valid 60m;
fastcgi_cache_methods GET HEAD;

Then change your /etc/nginx/nginx.conf to this:

user www-data;
worker_processes 4;
error_log /var/log/nginx/error.log warn;
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.1-fpm.sock;
 }
 include /etc/nginx/mime.types;
 include /etc/nginx/ssl.conf;
 include /etc/nginx/header.conf;
 include /etc/nginx/optimization.conf;
 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;
 send_timeout 3600;
 tcp_nopush on;
 tcp_nodelay on;
 keepalive_timeout 65;
 reset_timedout_connection on;
 server_tokens off;
 resolver 192.168.2.1;
 resolver_timeout 10s;
 include /etc/nginx/conf.d/*.conf;
}

Now create the new nextcloud.conf (no ‘gateway.conf’ is needed anymore!):

######################
# START -> NEXTCLOUD #
######################
fastcgi_cache_path /usr/local/tmp/cache levels=1:2 keys_zone=NEXTCLOUD:100m inactive=60m;
map $request_uri $skip_cache {
 default 1;
 ~*/thumbnail.php 0;
 ~*/apps/galleryplus/ 0;
 ~*/apps/gallery/ 0;
}
server {
 listen 80;
 server_name yourcloud.dedyn.io;
 location ^~ /.well-known/acme-challenge {
 proxy_pass http://127.0.0.1:81;
 proxy_redirect off;
 }
 location / {
 return 301 https://$host$request_uri;
 }
}
server {
 listen 443 ssl http2;
 server_name yourcloud.dedyn.io;
 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;
 }
 client_max_body_size 10240M;
 location / {
 rewrite ^ /index.php$uri;
 }
 location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
 deny all;
 }
 location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
 deny all;
 }
 location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
 fastcgi_split_path_info ^(.+\.php)(/.*)$;
 include fastcgi_params;
 include php_optimization.conf;
 fastcgi_pass php-handler;
 fastcgi_param HTTPS on;
 fastcgi_cache_bypass $skip_cache;
 fastcgi_no_cache $skip_cache;
 fastcgi_cache NEXTCLOUD;
 }
 location ~ ^/(?:updater|ocs-provider)(?:$|/) {
 try_files $uri/ =404;
 index index.php;
 }
 location ~ \.(?:css|js|woff|svg|gif)$ {
 try_files $uri /index.php$uri$is_args$args;
 add_header Cache-Control "public, max-age=15778463";
 access_log off;
 expires 30d;
 }
 location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
 try_files $uri /index.php$uri$is_args$args;
 access_log off;
 expires 30d;
 }
}

######################
# START -> ROUNDCUBE #
######################
server { 
 listen 80;
 server_name youremail.dedyn.io;
 location ^~ /.well-known/acme-challenge {
 proxy_pass http://127.0.0.1:81;
 proxy_redirect off;
 }
 location / {
 return 301 https://$host$request_uri;
 }
}
server {
 listen 443 ssl http2;
 server_name youremail.dedyn.io;
 root /var/www/emails/;
 client_max_body_size 512M;
 index index.php;
 charset utf-8;
 location / {
 try_files $uri $uri/ index.php;
 }
 location ~ ^/(README|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
 deny all;
 }
 location ~ ^/(bin|SQL|config|temp|logs)/ {
 deny all;
 }
 location ~* \.php$ {
 fastcgi_split_path_info ^(.+\.php)(/.*)$;
 include fastcgi_params;
 include php_optimization.conf;
 fastcgi_pass php-handler;
 fastcgi_param HTTPS on;
 fastcgi_index index.php;
 try_files $uri =404;
 }
}

Validate your NGINX configuration

nginx -t

and if no errors will appear we will expand the existing ssl certificates running:

letsencrypt certonly -a webroot --webroot-path=/var/www/letsencrypt --rsa-key-size 4096 -d yourcloud.dedyn.io -d youremail.dedyn.io
service nginx restart

To get Roundcube started, you need to download Roundcube. Find more instructions here. Having Roundcube installed we will start harden roundcube using TOTP (2FA). Please ensure having switched into sudo mode and change into roundcube’s plugin directory:

sudo -s
cd /var/www/emails/plugins

Then download the TOTP-app from github:

git clone https://github.com/alexandregz/twofactor_gauthenticator.git

Logout from roundcube and edit the main configuration to enable TOTP:

sudo -u www-data vi ../config/config.inc.php

Add ‘twofactor_gauthenticator’ to the plugins-section:

$config['plugins'] = array('enigma', 'fail2ban', 'twofactor_gauthenticator', 'markasjunk', 'newmail_notifier', 'zipdownload');

Save and quit the file (:wq!) and logon to your roundcube instance. Then activate twofactor_gauthenticator in the settings-panel:

Logout and re-login to roundcube again. From now your account is even more secure using a second factor for authentication.

Logout from roundcube and go ahead witht the implementation of fail2ban to prevent bruteforce attacks. Change to the plugin-directory again:

cd /var/www/emails/plugins

Download and extract the fail2ban-plugin:

wget https://github.com/mattrude/rc-plugin-fail2ban/archive/1.3.zip
unzip 1.3 && rm 1.3.zip

Then rename the plugin to fail2ban:

mv rc-plugin-fail2ban-1.3 fail2ban && chown -R www-data:www-data /var/www/emails

Create the fail2ban configuration for roundcube:

vi /etc/fail2ban/jail.d/roundcube.local

Add the following rows:

[roundcube]
ignoreip = 192.168.2.0/24
enabled = true
port = 80,443
protocol = tcp
maxretry = 4
bantime = 3600
logpath = /var/www/emails/logs/errors

Save and quit the file (:wq!) and create the roundcube filter expressions for fail2ban:

vi /etc/fail2ban/filter.d/roundcube.conf

Add the following rows:

[Definition]
failregex =  Login failed for .*. from <HOST>
ignoreregex =

Restart fail2ban:

service fail2ban restart

Logout from roundcube and edit the main configuration to enable fail2ban:

sudo -u www-data vi ../config/config.inc.php

Add ‘fail2ban’ to the plugins-section:

$config['plugins'] = array('enigma', 'fail2ban', 'twofactor_gauthenticator', 'markasjunk', 'newmail_notifier', 'zipdownload');

Save and quit the file (:wq!) and logon to your roundcube instance. Then verify fail2ban is working as expected.

fail2ban-client status nextcloud && fail2ban-client status roundcube

Re-logon to Nextcloud and roundcube using wrong credentials once. Then open the fail2ban-status again:

fail2ban-client status nextcloud && fail2ban-client status roundcube

If you’ll find e.g. “currently failed: 1” twice your Nextcloud and roundcube was successfully hardened with TOTP and fail2ban. At least we will add Nextcloud contacts to our roundcube instance using the carddav plugin. First logout from roundcube and change to the plugin directory again:

cd /var/www/emails/plugins

Download the plugin and extract it:

wget https://github.com/blind-coder/rcmcarddav/archive/master.zip && unzip master.zip

Rename the directory to carddav and change into that directory:

mv rcmcarddav-master carddav && rm master.zip && cd carddav

Download the binaries using curl:

curl -sS https://getcomposer.org/installer | php

Then execute the following command:

php composer.phar install

Copy the default config:

cp config.inc.php.dist config.inc.php && chown -R www-data:www-data /var/www/emails

Then modify pwstore_scheme to ‘des_key’:

sudo -u www-data vi config.inc.php

Change the ‘pwstore_scheme’-value:

...
$prefs['_GLOBAL']['pwstore_scheme'] = 'des_key';
...

Save and quit the file (:wq!) and edit roundcube’s main configuration:

sudo -u www-data vi ../../config/config.inc.php

Add ‘carddav’ to the plugins-section:

$config['plugins'] = array('carddav', 'enigma', 'fail2ban', 'twofactor_gauthenticator', 'markasjunk', 'newmail_notifier', 'zipdownload');

Save and quit the file (:wq!) and logon to your roundcube instance again. You will find the carddav option in roundcube’s settings panel:

Please fill in your url and your app-password from your Nextcloud. Adjust the carddav settings properly and enjoy your Roundcube beside your Nextcloud.


update history

v. 1.1: If you want to use Roundcube within Nextcloud as an external site, you just have to modify Roundcubes “defaults.inc.php”.

sudo -u www-data vi /var/www/emails/config/defaults.inc.php

But first backup this file and then change the “x_frame_option” parameter to  “false”

$config['x_frame_options'] = false;

From now you can use Roundcube within your Nextcloud as an external site either.

But please be aware that your Roundcube instance won’t be protected against Clickjacking and other similar attacks anymore, if x_frame_options would be set to false.

 

 


Have fun and enjoy both, Nextcloud and Roundcube!