Getting started
Default setup
If you don't specify any configuration values, the role will setup a Nginx
HTTP server running a default installation of the latest Roundcube stable
release which is then accessible via https://webmail.<your-domain>
.
SQLite is used as database backend for storing the user settings.
When the LDAP infrastructure is detected on the Roundcube
host, the role will install and configure LDAP support in Roundcube. The
default address book will be configured to allow only searches in the
directory, which is beneficial in larger environments. The password
plugin
will be enabled and configured to use the LDAP Password Modify Extended
Operation (RFC 3062) driver to allow users to change their passwords.
Roundcube will use the current user credentials to login to the LDAP directory, therefore access to the LDAP entries and attributes depends on the LDAP ACL configuration in the directory itself.
Local spell check support will be configured using the Enchant library with
aspell
spell checker. By default only the English dictionary
(aspell-en
) is installed, more dictionaries can be added using the
roundcube__packages
variable.
Deployment from private or internal git repository
The debops.roundcube role supports deployment of Roundcube from private/internal git repositories over HTTPS. This can be useful when a Roundcube codebase is forked to include custom themes or other changes in the application required for a particular installation.
To do this, you can specify the URL to the git repository using the
roundcube__git_repo
variable in the form:
https://<username>:<password>@<git-host>/<organization>/<repository>.git
In GitLab, this functionality is called Deploy Tokens, while on GitHub users can create Personal Access Tokens. The tokens can be generated per-project and allow for read-only access to the git repository.
The access credentials will be stored in the .git/
directory of the
cloned Roundcube repository. The role by default puts it in the location
specified by the roundcube__git_dir
variable, under the
/usr/local/src/
subdirectories, with access restricted via UNIX
permissions.
The role expects that the checked out commits or tags are signed by a valid GPG
key. To include the GPG keys for the staff that creates the modifications of
the Roundcube code base, you can include their GPG fingerprints in the
roundcube__git_additional_gpg_keys
list. They will be imported to the
Roundcube UNIX account by the debops.keyring role.
Here are an example Ansible inventory variables that hide the token using the debops.secret role:
roundcube_access_token: '{{ lookup("file", secret + "/roundcube/access_token") }}'
roundcube__git_repo: '{{ "https://" + roundcube_access_token
+ "@code.example.org/mail-infra/roundcubemail.git" }}'
roundcube__git_additional_gpg_keys: [ 'fingerprint1', 'fingerprint2' ]
Before running the role, make sure to put the credentials in the
ansible/secret/roundcube/access_token
file inside of the DebOps project
directory. The file should contain the credentials in the form of:
<username>:<password>
There should be no new line character at the end.
IMAP, SMTP and Sieve server detection
The role detects the preferred IMAP, SMTP and Sieve servers by using DNS SRV Records for the following services:
_imaps._tcp.{{ roundcube__domain }} (default port 993)
_submissions._tcp.{{ roundcube__domain }} (default port 465)
_sieve._tcp.{{ roundcube__domain }} (default port 4190)
At the moment, only a single SRV resource record is supported for each service.
If the above SRV resource records are not available, the role will check for the presence of the debops.dovecot and debops.postfix roles using Ansible local facts on the host. If they are found, the respective service (IMAP, SMTP (submission) and/or Sieve) will be configured to be accessed via the host's own FQDN address to support X.509 certificate verification. In this case the services will also use Implicit TLS.
Finally, the role will fall back to using static domain names for the
respective services, based on the host domain (roundcube__domain
):
IMAP: imap.example.org:993
SMTP: smtp.example.org:465
Sieve: sieve.example.org:4190
This allows for deployment of the RoundCube Webmail independent from the respective services, for example on a separate host or VM. The communication with the mail services will be encrypted by default using Implicit TLS, as recommended by RFC 8314.
Example inventory
To install and configure Roundcube on a host, it needs to be present in the
[debops_service_roundcube]
Ansible inventory group. Additional services
like memcached, Redis,
MariaDB and
PostgreSQL can help increase the website
performance.
[debops_all_hosts]
webmail
[debops_service_mariadb_server]
webmail
[debops_service_memcached]
webmail
[debops_service_postgresql_server]
webmail
[debops_service_redis_server]
webmail
[debops_service_roundcube]
webmail
Example playbook
The following playbook can be used with DebOps. If you are using these role without DebOps you might need to adapt them to make them work in your setup.
---
- name: Install and manage Roundcube Web mail
collections: [ 'debops.debops', 'debops.roles01',
'debops.roles02', 'debops.roles03' ]
hosts: [ 'debops_service_roundcube' ]
become: True
environment: '{{ inventory__environment | d({})
| combine(inventory__group_environment | d({}))
| combine(inventory__host_environment | d({})) }}'
pre_tasks:
- name: Apply keyring configuration for php environment
ansible.builtin.import_role:
name: 'keyring'
vars:
keyring__dependent_apt_keys:
- '{{ php__keyring__dependent_apt_keys }}'
- '{{ nodejs__keyring__dependent_apt_keys }}'
- '{{ nginx__keyring__dependent_apt_keys }}'
- '{{ mariadb__keyring__dependent_apt_keys }}'
keyring__dependent_gpg_user: '{{ roundcube__keyring__dependent_gpg_user }}'
keyring__dependent_gpg_keys:
- '{{ roundcube__keyring__dependent_gpg_keys }}'
tags: [ 'role::keyring', 'skip::keyring',
'role::php', 'role::nodejs', 'role::nginx', 'role::mariadb',
'role::roundcube' ]
- name: Prepare php environment
ansible.builtin.import_role:
name: 'php'
tasks_from: 'main_env'
tags: [ 'role::php', 'role::php:env', 'role::logrotate' ]
roles:
- role: apt_preferences
tags: [ 'role::apt_preferences', 'skip::apt_preferences',
'role::nginx', 'role::php', 'role::nodejs' ]
apt_preferences__dependent_list:
- '{{ nginx__apt_preferences__dependent_list }}'
- '{{ php__apt_preferences__dependent_list }}'
- '{{ nodejs__apt_preferences__dependent_list }}'
- role: cron
tags: [ 'role::cron', 'skip::cron' ]
- role: logrotate
tags: [ 'role::logrotate', 'skip::logrotate' ]
logrotate__dependent_config:
- '{{ php__logrotate__dependent_config }}'
- role: ferm
tags: [ 'role::ferm', 'skip::ferm', 'role::nginx' ]
ferm__dependent_rules:
- '{{ nginx__ferm__dependent_rules }}'
- role: python
tags: [ 'role::python', 'skip::python', 'role::mariadb', 'role::postgresql' ]
python__dependent_packages3:
- '{{ ldap__python__dependent_packages3 }}'
- '{{ mariadb__python__dependent_packages3 if roundcube__database_map[roundcube__database].dbtype == "mysql" else [] }}'
- '{{ nginx__python__dependent_packages3 }}'
- '{{ postgresql__python__dependent_packages3 if roundcube__database_map[roundcube__database].dbtype == "postgresql" else [] }}'
python__dependent_packages2:
- '{{ ldap__python__dependent_packages2 }}'
- '{{ mariadb__python__dependent_packages2 if roundcube__database_map[roundcube__database].dbtype == "mysql" else [] }}'
- '{{ nginx__python__dependent_packages2 }}'
- '{{ postgresql__python__dependent_packages2 if roundcube__database_map[roundcube__database].dbtype == "postgresql" else [] }}'
- role: ldap
tags: [ 'role::ldap', 'skip::ldap' ]
ldap__dependent_tasks:
- '{{ roundcube__ldap__dependent_tasks }}'
- role: php
tags: [ 'role::php', 'skip::php' ]
php__dependent_packages:
- '{{ roundcube__php__dependent_packages }}'
php__dependent_pools:
- '{{ roundcube__php__dependent_pools }}'
- role: nodejs
tags: [ 'role::nodejs', 'skip::nodejs' ]
nodejs__npm_dependent_packages:
- '{{ roundcube__nodejs__npm_dependent_packages }}'
- role: nginx
tags: [ 'role::nginx', 'skip::nginx' ]
nginx__dependent_servers:
- '{{ roundcube__nginx__dependent_servers }}'
nginx__dependent_upstreams:
- '{{ roundcube__nginx__dependent_upstreams }}'
- role: mariadb
tags: [ 'role::mariadb', 'skip::mariadb' ]
mariadb__dependent_users:
- database: '{{ roundcube__database_map[roundcube__database].dbname }}'
user: '{{ roundcube__database_map[roundcube__database].dbuser }}'
password: '{{ roundcube__database_map[roundcube__database].dbpass }}'
owner: '{{ roundcube__user }}'
group: '{{ roundcube__group }}'
home: '{{ roundcube__home }}'
system: True
priv_aux: False
mariadb__server: '{{ roundcube__database_map[roundcube__database].dbhost }}'
when: roundcube__database_map[roundcube__database].dbtype == 'mysql'
- role: postgresql
tags: [ 'role::postgresql', 'skip::postgresql' ]
postgresql__dependent_roles:
- db: '{{ roundcube__database_map[roundcube__database].dbname }}'
role: '{{ roundcube__database_map[roundcube__database].dbuser }}'
password: '{{ roundcube__database_map[roundcube__database].dbpass }}'
postgresql__server: '{{ roundcube__database_map[roundcube__database].dbhost
if roundcube__database_map[roundcube__database].dbhost != "localhost"
else "" }}'
when: roundcube__database_map[roundcube__database].dbtype == 'postgresql'
- role: roundcube
tags: [ 'role::roundcube', 'skip::roundcube' ]
This playbook is also shipped with DebOps at ansible/playbooks/service/roundcube.yml
.