$_ BSDHowTo.ch
How To... Why not..? Scripts Patches RSS logo

Install Roundcube on OpenBSD

Last update: 2023-08-19

Introduction

This post is about installing Roundcube on OpenBSD. The web server for Roundcube is httpd(8) together with PHP and MariaDB installed from packages(7).

Installation of packages

With the following command you get all the packages installed which are required for Roundcube:

$ doas pkg_add -i roundcubemail mariadb-server php-pdo_mysql php-intl

The last package will present you probably with list of available versions to choose from. Make sure you choose the same version of PHP as the one that got installed by the roundcubemail package. At the time of writing this is 8.1 on OpenBSD 7.3.

Configuration of PHP

You must make sure that the required PHP extensions are enabled. The easiest way to this is the following:

$ cd /etc/php-8.1.sample/
$ for i in * ; do
> doas ln -sf ../php-8.1.sample/$i ../php-8.1/
> done

And you need to prepare the chroot(2) for the usage of TLS with PHP:

$ doas mkdir -p /var/www/etc/ssl
$ doas install -m 444 -o root -g bin /etc/ssl/cert.pem /etc/ssl/openssl.cnf \
  /var/www/etc/ssl/

Make sure you add the above install(1) command to /etc/rc.local in order to update the files whenever the originals change.

Roundcube itself requires some settings in /etc/php-fpm.conf in order to work properly:

; Settings for Roundcube
php_flag[display_errors] = off
php_admin_flag[log_errors] = on
php_admin_value[upload_max_filesize] = 5M
php_admin_value[post_max_size] = 6M
php_admin_value[memory_limit] = 64M
php_flag[zlib.output_compression] = off
php_flag[suhosin.session.encrypt] = off
php_flag[session.auto_start] = off
php_admin_value[session.gc_maxlifetime] = 21600
php_admin_value[session.gc_probability] = 1

Configuration of MariaDB

I recommend that you create a dedicated login group for mysqld - although the package read-me tells you that you only need it on busy servers. Append the following to /etc/login.conf:

mysqld:\
    :openfiles-cur=1024:\
    :openfiles-max=2048:\
    :tc=daemon:

Create the initial database for MariaDB:

$ doas mysql_install_db

Now you can start mysqld and secure the installation:

$ doas rcctl enable mysqld
$ doas rcctl start mysqld
$ doas mysql_secure_installation

With httpd(8) chrooted to /var/www you must make sure that the connection to the socket of the MariaDB server is available within the chroot. First create a folder in which the socket will be placed:

$ doas install -d -m 0711 -o _mysql -g _mysql /var/www/var/run/mysql

Second you must change the socket path in /etc/my.cnf:

[client-server]
socket = /var/www/var/run/mysql/mysql.sock

I recommend commenting out the existing entries and place the new ones below the existing ones. You must restart mysqld in order to activate the new socket:

$ doas rcctl restart mysqld

Now you are ready to create the actual database for Roundcube:

$ doas -s
$ mysql
> CREATE DATABASE roundcube /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
> GRANT ALL PRIVILEGES ON roundcube.* TO roundcube@localhost
    -> IDENTIFIED BY 'password';
> QUIT
# mysql roundcube < /var/www/roundcubemail/SQL/mysql.initial.sql
# ^D

Configuration of httpd(8)

For security reasons you should offer access to Roundcube only over HTTPS. I presume that you have a proper certificate and its private key stored already on the server. The configuration of httpd(8) is done in httpd.conf(5):

server "rcube.example.org" {
    listen on egress tls port https
    log style combined
    tls certificate "/etc/ssl/rcube.example.org"
    tls key "/etc/ssl/private/rcube.example.org"

    root "/roundcubemail"
    directory index index.php

    location "*.php" {
        fastcgi socket "/run/php-fpm.sock"
    }
}

types {
    include "/usr/share/misc/mime.types"
}

You may want to change the log style from combined to forwarded if you run httpd(8) behind a proxy that sets the headers X-Forwarded-For and X-Forwarded-Port (see below).

In order to make name resolving work within the chroot(2) you should copy your hosts(5) file and your resolv.conf(5) file into it:

$ cd /var/www
$ for f in hosts resolv.conf ; do doas cp /etc/$f etc/ ; done

Consider running relayd(8) in front of httpd(8). It gives you the ability to add some headers for security. And it allows you to efficiently block access to certain URLs that you don’t want to be accessible by the public.

Add something like this to relayd.conf(5):

log connection

ipv4=192.0.2.66
ipv6=2001:db8::c000:0242

table <rcube4> { 127.0.0.1 }
table <rcube6> { ::1 }

http protocol "www" {
    tls keypair rcube.example.org

    match request header set "X-Forwarded-For" value "$REMOTE_ADDR"
    match request header set "X-Forwarded-Port" value "$REMOTE_PORT"

    match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubdomains"
    match response header set "X-Content-Type-Options" value "nosniff"
    match response header set "X-Frame-Options" value "SAMEORIGIN"
    match response header set "X-Robots-Tag" value "noindex,nofollow"
    match response header set "X-XSS-Protection" value "1; mode=block"

    pass
    block request url file "/etc/roundcube.blocklist"
}

relay "rcube4" {
    listen on ipv4 port https tls
    protocol "rcube"
    forward to <rcube4> port http
}

relay "rcube6" {
    listen on ipv6 port https tls
    protocol "rcube"
    forward to <rcube6> port http
}

The configuration above assumes that relayd(8) and httpd(8) run on the same system. In this case there is no need to encrypt the traffic between the two daemons. Remove all the tls statements from httpd.conf(5) and change the log style to forwarded:

server "rcube.example.org" {
    listen on lo0 port http
    log style forwarded

The file /etc/roundcube.blacklist should contain the URLs you don’t want to be accessible by the public:

rcube.example.org/CHANGELOG.md
rcube.example.org/INSTALL
rcube.example.org/LICENSE
rcube.example.org/README.md
rcube.example.org/SECURITY.md
rcube.example.org/SQL/
rcube.example.org/UPGRADING
rcube.example.org/bin/
rcube.example.org/composer.json
rcube.example.org/composer.json-dist
rcube.example.org/composer.lock
rcube.example.org/config/
rcube.example.org/db/
rcube.example.org/installer/
rcube.example.org/logs/
rcube.example.org/roundcubemail.conf-dist
rcube.example.org/temp/

Any client trying to access any of these will cause relayd(8) to immediately drop the connection without any answer. Alternatively you could add a line return error to the protocol "rcube" block. That will deliver an error message to the client. In both cases relayd(8) will log the message 403 Forbidden together with the IP of the offending client to syslog(3). You can easily use these log entries to block offending IPs.

Configuration of Roundcube

The basic configuration of Roundcube is done in its config file /var/www/roundcubemail/config/config.inc.php. You should at least set proper values for the following variables:

$config['db_dsnw'] = 'mysql://roundcube:password@localhost/roundcube';
$config['default_host'] = 'imap.example.org';
$config['smtp_server'] = 'smtp.example.org';
$config['des_key'] = 'Exactly24BytesRandomStr!'

To generate a quality random string of 24 bytes for the parameter des_key use the following commands:

$ cat /dev/urandom | tr -dc [:print:] | fold -w 24 | head -n 1

Optional: Redis

Roundcube supports Redis as session storage since version 1.2 and as cache since version 1.4. Using Redis for both might give you a performance boost - or not, that depends on your setup. In case you want to try it here are the instructions:

First install the required components:

$ doas pkg_add -i pecl81-redis redis

Make sure the PHP module for Redis is enabled and php-fpm knows about it. Then you can start redis:

$ cd /etc/php-8.1
$ doas ln -s ../php-8.1.sample/redis.ini redis.ini
$ doas rcctl enable redis
$ doas rcctl start redis

Adding the following settings to /var/www/roundcube/config/config.inc.php to make Roundcube use Redis for both session storage and caching:

$config['redis_hosts'] = array('localhost:6379');
$config['session_storage'] = 'redis';

Optional: Logging

The default settings of Roundcube will write dedicated log file in the directory /var/www/roundcube/logs. If you are happy with this solution I suggest you let newsyslog(8) rotate the files in order to prevent your /var from filling up.

Roundcube is also capable of using syslog(3). You can even configure Roundcube to send its log entries to a specific syslog facility. And you can enable/disable logging for certain parts of Roundcube. For Roundcube on a mail server I usually use the following settings in /var/www/roundcubemail/config/config.inc.php:

$config['log_driver'] = 'syslog';
$config['syslog_id'] = 'roundcube';
$config['syslog_facility'] = LOG_MAIL;
$config['log_logins'] = true;

Optional: Plugins

Roundcube comes with a bunch of plugins, and some more are available as packages under OpenBSD. You can enable a plugin by adding its name to the array $config['plugins'] in config.inc.php.

If you want to add plugins to Roundcube that are not part of the base package, first check if there is an OpenBSD package for it:

$ pkg_info -Q rcube

Should the package you want be missing in the list you can still obtain it by installing and using composer:

$ doas pkg_add composer
$ cd /var/www/roundcube
$ doas ...

Once you found a plugin on Packagist, click on it and replace the ... in the last command with the string found beneath the plugin name on the website. Something similar to composer require author/plugin

Start services and finish setup

The time has come to actually start the required services:

$ doas rcctl enable httpd php81_fpm
$ doas rcctl start httpd php81_fpm

If both start commands return OK you can finish the setup of Roundcube by opening the URL of your server in a browser, e. g. https://rcube.example.org/:

Log in page

Log in with valid credentials for your IMAP server. Then switch to the Settings screen to tweak the configuration of Roundcube further according to your needs:

Initial setup screen