Roundcube¶
Downloading and Installing Roundcube¶
First download the latest package. The latest version can be found by going to
sudo -s
cd /opt
wget https://github.com/roundcube/roundcubemail/releases/download/1.4.9/roundcubemail-1.4.9-complete.tar.gz
Create our web directory:
mkdir /var/www/webmail.example.com
Extract the tarball, move the newly created folder to web root and rename it as html at the same time.
tar xvf roundcubemail-1.4.9-complete.tar.gz
rm roundcubemail-1.4.9-complete.tar.gz
mv roundcubemail-1.4.9 /var/www/webmail.example.com/html
Install any missing dependencies:
apt update && apt install php-net-ldap2 php-net-ldap3 php7.4-imagick php7.4-common php7.4-gd php7.4-imap php7.4-json php7.4-curl php7.4-zip php7.4-xml php7.4-mbstring php7.4-bz2 php7.4-intl php7.4-gmp
Install Composer, which is a dependency manager for PHP.
apt install composer
Change into the roundcube directory
cd /var/www/webmail.example.com/html
Use composer to install all necessary 3rd party dependencies
composer install --no-dev
Create a dedicated user for the PHP pool we'll create later:
groupadd www-roundcube
useradd -g www-roundcube www-roundcube
Now add a temp folder and tighten permissions across the structure:
mkdir /var/www/webmail.example.com/html/tmp
chown -R www-roundcube:www-data /var/www/webmail.example.com/html
chown -R www-roundcube:www-roundcube /var/www/webmail.example.com/html/tmp
chown -R www-roundcube:www-roundcube /var/www/webmail.example.com/html/logs
find /var/www/webmail.example.com/html -type d -exec chmod 750 {} \;
find /var/www/webmail.example.com/html -type d -exec chmod g+s {} \;
find /var/www/webmail.example.com/html -type f -name '*.php' -exec chmod 600 {} \;
Create a MariaDB Database¶
Log in to the MariaDB shell:
mysql -u root -p
Create a new database:
CREATE DATABASE roundcube DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
Create a user:
CREATE USER roundcubeuser@localhost IDENTIFIED BY 'password';
Grant permissions:
GRANT ALL PRIVILEGES ON roundcube.* TO roundcubeuser@localhost;
Flush privileges:
FLUSH PRIVILEGES;
And exit:
EXIT;
Import the initial tables to the Roundcube database:
mysql roundcube < /var/www/webmail.example.com/html/SQL/mysql.initial.sql
Create the Apache Virtual Host¶
Create a virtualhost file:
nano /etc/apache2/sites-available/webmail.example.com.conf
and enter the following:
<VirtualHost 127.0.0.1:80>
ServerName webmail.example.com
ServerAdmin admin@example.com
DocumentRoot /var/www/webmail.example.com/html/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/webmail.example.com/html/>
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:/var/run/php/php7.4-fpm-www-roundcube.sock|fcgi://localhost/"
</FilesMatch>
SetEnvIf HTTPS on HTTPS=on
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1
</VirtualHost>
Create the Nginx Server Block¶
Next we create the Nginx server block:
nano /etc/nginx/sites-available/webmail.example.com
and enter the following:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name webmail.example.com;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
ssl_certificate /etc/letsencrypt/rsa-certs/webmail.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/rsa-certs/webmail.example.com/privkey.pem;
ssl_certificate /etc/letsencrypt/ecc-certs/webmail.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/ecc-certs/webmail.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/ecc-certs/webmail.example.com/chain.pem;
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*)$" /$4 permanent;
# Strong SSL settings and secure headures
include /etc/nginx/custom-config/ssl.conf;
include /etc/nginx/custom-config/header.conf;
# Set Gzip
include /etc/nginx/custom-config/optimisation.conf;
# Nginx Bad Bot Blocker Includes
include /etc/nginx/bots.d/ddos.conf;
include /etc/nginx/bots.d/blockbots.conf;
# Block Bad Countries
if ($allowed_country = no) {
set $blockreason '[geo_blocked]';
return 403;
}
# 7g Firewall
if ( $bad_querystring !~* "\[OK\]" ) {
set $blockreason $bad_querystring;
return 403;
}
if ( $bad_request !~* "\[OK\]" ) {
set $blockreason $bad_request;
return 403;
}
if ( $bad_request_method !~* "\[OK\]" ) {
set $blockreason $bad_request_method;
return 403;
}
# Restrict locations
location ~ ^/(README|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
deny all;
}
location ~ ^/(bin|SQL|tmp|logs)/ {
deny all;
}
# Everything else
location / {
client_max_body_size 50M;
include /etc/nginx/custom-config/proxy.conf;
proxy_pass http://127.0.0.1:8080;
#proxy_buffering off;
}
}
Create the Certificates¶
We now create the SSL certificates:
Issue an RSA certificate and install to a custom location
~/.acme.sh/acme.sh --issue --dns dns_cloudns -d webmail.example.com --keylength 4096 --key-file /etc/letsencrypt/rsa-certs/webmail.example.com/privkey.pem --ca-file /etc/letsencrypt/rsa-certs/webmail.example.com/chain.pem --cert-file /etc/letsencrypt/rsa-certs/webmail.example.com/cert.pem --fullchain-file /etc/letsencrypt/rsa-certs/webmail.example.com/fullchain.pem --pre-hook "mkdir -p /etc/letsencrypt/rsa-certs/webmail.example.com" --post-hook "find /etc/letsencrypt/rsa-certs/webmail.example.com/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/rsa-certs/webmail.example.com/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"
and issue an ECC certificate
~/.acme.sh/acme.sh --issue --dns dns_cloudns -d webmail.example.com --keylength ec-384 --key-file /etc/letsencrypt/ecc-certs/webmail.example.com/privkey.pem --ca-file /etc/letsencrypt/ecc-certs/webmail.example.com/chain.pem --cert-file /etc/letsencrypt/ecc-certs/webmail.example.com/cert.pem --fullchain-file /etc/letsencrypt/ecc-certs/webmail.example.com/fullchain.pem --pre-hook "mkdir -p /etc/letsencrypt/ecc-certs/webmail.example.com" --post-hook "find /etc/letsencrypt/ecc-certs/webmail.example.com/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/ecc-certs/webmail.example.com/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"
Create the PHP Pool¶
Create the PHP Pool:
nano /etc/php/7.4/fpm/pool.d/www-roundcube.conf
Enter the following:
; pool name
[www-roundcube]
; Unix user/group of processes
; will be used.
user = www-roundcube
group = www-roundcube
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Socket to which the Apache will connect
listen = /run/php/php7.4-fpm-www-roundcube.sock
apparmor_hat = roundcube
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500
php_admin_value[allow_url_fopen] = On
php_admin_value[allow_url_include] = On
php_admin_value[memory_limit] = 512M
php_admin_flag[output_buffering] = Off
php_admin_value[max_execution_time] = 1800
php_admin_value[max_input_time] = 3600
php_admin_value[post_max_size] = 50M
php_admin_value[upload_max_filesize] = 50M
php_admin_value[upload_tmp_dir] = /var/www/webmail.example.com/html/tmp
php_admin_value[sys_temp_dir] = /var/www/webmail.example.com/html/tmp
php_admin_value[max_file_uploads] = 100
php_admin_flag[session.cookie_secure] = True
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,show_source,putenv
;php_admin_value[open_basedir] = /var/www/webmail.example.com/html/
Enable Sites and PHP Pool¶
Enable the Apache Virtualhost:
a2ensite webmail.example.com.conf
Enable the Nginx Server Block
ln -s /etc/nginx/sites-available/webmail.example.com /etc/nginx/sites-enabled/
Restart PHP-FPM
service php7.4-fpm restart
Reload the web servers:
service apache2 reload && service nginx reload
Check configurations:
nginx -t
apachectl configtest
Complete Installation¶
Go to the Roundcube installer page:
https://webmail.example.com/installer
Hopefully, all the requirements will be met. Click next and in the 2nd page, enter the database details for the database you created earlier. Enter the appropriate settings too in the IMAP and SMTP sections.
The IMAP default host should be:
ssl://websrv1.example.com
and the smtp server should be:
tls://websrv1.example.com
The IMAP port should be 993 and the SMTP port 587.
In the plugins section, enable all the plugins except autologon, database attachments, and redundant attachments.
Once that’s done, click create config and proceed to the next step which is to check the config.
Now go to the main page at webmail.example.com and login. If all is well, delete the install folder:
rm -r /var/www/webmail.example.com/html/install/
Removing Sensitive Information from Email Headers¶
Run the following command to create a header check file:
nano /etc/postfix/smtp_header_checks
Enter the following into the file, then save.
/^User-Agent.*Roundcube Webmail/ IGNORE
Edit the Postfix main configuration file.
nano /etc/postfix/main.cf
Add the following line at the end of the file:
smtp_header_checks = regexp:/etc/postfix/smtp_header_checks
Save then run the following command to rebuild the hash table:
postmap /etc/postfix/smtp_header_checks
Reload Postfix
systemctl reload postfix
Configure the Password Plugin in Roundcube¶
Roundcube includes a password plugin that allows users to change their password from the webmail interface.
First we copy the distributed config file to a new file:
cp /var/www/webmail.example.com/html/plugins/password/config.inc.php.dist /var/www/webmail.example.com/html/plugins/password/config.inc.php
Edit the config file:
nano /var/www/webmail.example.com/html/plugins/password/config.inc.php
Find and change the following lines:
$config['password_db_dsn'] = 'mysql://postfix:postfixadmin_database_password@127.0.0.1/postfix';
$config['password_query'] = "UPDATE mailbox SET password=%D,modified=NOW() WHERE username=%u";
$config['password_strength_driver'] = 'zxcvbn';
$config['password_zxcvbn_min_score'] = 5;
$config['password_minimum_length'] = 8;
$config['password_algorithm'] = 'dovecot';
$config['password_dovecotpw'] = '/usr/bin/doveadm pw -r 5';
$config['password_dovecotpw_method'] = 'ARGON2I';
$config['password_dovecotpw_with_method'] = true;
Fix permissions on the file:
chown www-roundcube:www-data /var/www/webmail.example.com/html/plugins/password/config.inc.php
chmod /var/www/webmail.example.com/html/plugins/password/config.inc.php
Configure the Enigma GPG Plugin in Roundcube¶
Create a directory a level down from the web root:
mkdir /var/www/webmail.exmple.com/gpg-home
Fix permissions on the new folder:
chown -R www-roundcube:www-roundcube /var/www/webmail.exmple.com/gpg-home
chmod g+s /var/www/webmail.exmple.com/gpg-home
Edit the Roundcube config file:
nano /var/www/webmail.example.com/html/config/config.inc.php
Add the following value under the $config['plugins']
section
$config['enigma_pgp_homedir'] = '/var/www/webmail.example.com/gpg-home/';
Restart Apache2
service apache2 restart
Configure Logrotate¶
We need to create a logrotate config in order to rotate the Roundcube log files:
nano /etc/logrotate.d/roundcube
Enter the following:
/var/www/webmail.example.com/html/logs/*.log {
daily
rotate 7
compress
delaycompress
create
postrotate
service apache2 reload > /dev/null
endscript
sharedscripts
missingok
notifempty
}
Configure the PHP-FPM Apparmor Changehat¶
We now need to create a PHP-FPM changehat apparmor profile for Roundcube:
nano /etc/apparmor.d/php-fpm.d/roundcube
and enter the following:
^roundcube flags=(attach_disconnected, complain) {
#include <abstractions/base>
#include <abstractions/dovecot-common>
#include <abstractions/nameservice>
#include <abstractions/openssl>
#include <abstractions/php>
#include <abstractions/ssl_certs>
signal receive peer=php-fpm7.4,
deny /usr/share/{phpmyadmin,postfixadmin}/** r,
deny /var/www/{anothersite.com,andanothersite.com}/** r,
/usr/bin/dash Px -> roundcube-dash,
/run/mysqld/mysqld.sock rwlk,
/usr/share/dovecot/protocols.d/ r,
/usr/share/dovecot/protocols.d/*.protocol r,
@{run}/php/php7.4-fpm-www-roundcube.sock rwlk,
owner /var/www/webmail.example.com/** r,
owner /var/www/webmail.example.com/html/logs/*.log w,
owner /var/www/webmail.example.com/html/logs/** rw,
owner /var/www/webmail.example.com/gpg-home/* rw,
owner /var/www/webmail.example.com/gpg-home/*/ rw,
/etc/mime.types r,
}
Create a special profile for Dash:
nano /etc/apparmor.d/roundcube.dash
Enter the following:
#include <tunables/global>
profile roundcube-dash flags=(attach_disconnected,complain) {
#include <abstractions/base>
#include <abstractions/mysql>
network inet6 stream,
network inet stream,
/bin/dash mr,
/etc/nsswitch.conf r,
/etc/passwd r,
/proc/*/fd/ r,
/run/php/php7.4-fpm-www-roundcube.sock rw,
/usr/bin/dash r,
/usr/bin/gpg rix,
/usr/bin/gpg-agent rix,
/usr/bin/gpg-connect-agent rix,
/usr/bin/gpgconf rix,
/var/www/webmail.example.com/gpg-home/** rw,
owner /var/www/webmail.example.com/gpg-home/** rwl,
owner /var/www/webmail.example.com/gpg-home/*/random_seed rwk,
}
Reload apparmor:
service apparmor reload
Configure the Apache2 Apparmor Changehat¶
We now need to create an Apache2 changehat apparmor profile for Roundcube:
nano /etc/apparmor.d/apache2.d/roundcube-a2
and enter the following:
^roundcube-a2 flags=(attach_disconnected) {
#include <abstractions/apache2-common>
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/php>
capability setuid,
capability setgid,
# for log writing (could be abstracted)
/var/log/apache2/access.log w,
/var/log/apache2/error.log w,
# Socket access
/run/php/php7.4-fpm-www-roundcube.sock wr,
# Access to standard Roundcube files
/var/www/webmail.example.com/html/ r,
/var/www/webmail.example.com/html/** r,
# Deny access to these locations
deny /usr/share/phpmyadmin/** r,
deny /usr/share/postfixadmin/** r,
deny /var/www/webmail.example.com/html/{vendor/bin,bin,SQL,tmp,logs}/ r,
deny /var/www/webmail.example.com/html/{vendor/bin,bin,SQL,tmp,logs}/** r,
deny /var/www/webmail.example.com/html/{README,INSTALL,LICENSE,CHANGELOG,UPGRADING} r,
deny /var/www/webmail.example.com/html/**.sh rwx,
deny /var/www/webmail.example.com/html/**.{php,sample} r,
# Deny access to Bash
deny /bin/bash r,
deny /bin/dash r,
}
Reload apparmor:
service apparmor reload