After setting up a Postfix and Dovecot Server and securing it with Rspamd and ClamAV we are still lacking a nice web interface to send and receive mail with. This is where SOGo comes in. SOGo is a fully fledged groupware server providing a mail client, calendar, address book, CalDAV, CardDAV, Microsoft ActiveSync and more. There are freely available nightly builds of SOGo which we will use with our SOGo Docker Image to create a groupware server on mydomain.com.
In this article we discuss the necessary configuration to set up SOGo and provide Ansible roles for SOGo and some other services not yet covered in other articles.
Prerequisites
We assume that you have a host that is accessible via Ansible and that you are running Traefik as well as the internal Docker network described in the Traefik article. We will outsource our user database to an Apache LDAP Server. You can of course use another LDAP server. Make sure to adapt the SOGo LDAP configuration. You also need a SMTP and IMAP server like the Postfix and Dovecot Server from a previous article. Spam and virus filtering is not required for this article, but highly recommended. We will not target full Unicode compliance with our setup to keep it simple. There is a guide in the SOGo docs on how to make SOGo Unicode compliant. We will mention it a few times where applicable.
SOGo Database Setup
SOGo uses a MySQL/MariaDB database to save contacts, calendar entries, settings and more. There are no restrictions on how the database must be run: native, in a Docker container or using a cloud service. In this example we use a Docker container, mostly because it’s easy to set up.
Currently I’m running MariaDB 10.3 for SOGo so that’s what we use for this Ansible role. My MariaDB instance already fulfilled the requirements for SOGo Unicode compliance. You might want to check your settings.
roles/mariadb/tasks/main.yml
# This is not very clean, you might wanna remove the coupling -name:IncludeSOGovars include_vars: dir:../../sogo/vars
We have to tell SOGo where in your LDAP structure it can find the user information and how it is formatted. We assume the same LDAP structure as in the Postfix and Dovecot Server setup guide where we find the users under ou=people,o=bored. SOGo needs a user with uid sogo and a password of your choice to access the LDAP directory. To separate actual users from the SOGo user we create a new organizationalUnit branch in our bored partition with uid serviceusers. This service user doesn’t need all the information that the inetOrgPerson structure could hold. Instead, we create it as structural object account and simpleSecurityObject as auxiliary. The final dn is now:
uid=sogo,ou=servicesuers,o=bored.
And the partition structure is:
# Partition o=bored # Users ou=people,o=bored # Example User cn=john+sn=doe,ou=people,o=bored # Service Users ou=servicesuers,o=bored # Example User uid=sogo,ou=servicesuers,o=bored
Set a SSHA256 password of your choice. Note that an account object uses uid instead of cn+sn to identify itself.
SOGo Configuration
Using our SOGo Docker image we have to configure SOGo itself plus the Apache webserver running in the container.
SOGo Main Config
SOGo has one main config file and we use it mostly to point SOGo to all the services around it: SMTP, IMAP, MariaDB, Memcached and LDAP. We supply the respective users to access the services and their passwords. When we configure LDAP access we use the service user from above and direct SOGo to accept everyone in ou=people,o=bored as user. We assume that all your services run as Docker containers on the same Docker network and are named like their respective service. So the Docker DNS would resolve postfix to your Postfix container.
/* Your IMAP and SMTP server */ SOGoIMAPServer = "imaps://mydomain.com:993"; SOGoSieveScriptsEnabled = YES; /* You can leave this out if you do not use sieve */ SOGoSieveServer = "sieve://mydomain.com:4190/?tls=YES"; SOGoSMTPServer = "smtp://postfix:25"; SOGoMailDomain = mydomain.com; SOGoMailingMechanism = smtp;
/* LDAP authentication */ SOGoUserSources = ( { type = ldap; /* Adapt these fields if you have a different structure */ CNFieldName = cn; UIDFieldName = cn; IDFieldName = cn; // first field of the DN for direct binds bindFields = (cn); // array of fields to use for indirect binds baseDN = "ou=people,o=bored"; bindDN = "uid=sogo,ou=servicesuers,o=bored"; bindPassword = {{ sogo_ldap_password }}; canAuthenticate = YES; displayName = "Local Users"; hostname = "ldap://apacheds:389"; id = myhostname; // This can be anything isAddressBook = YES; // Share user list as address book. userPasswordAlgorithm = ssha256; // Has to be the same as in LDAP } );
/* Web Interface */ SOGoPageTitle = mydomain.com;
/* General - SOGoTimeZone *MUST* be defined. Adapt as desired */ SOGoLanguage = English; SOGoTimeZone = Europe/Berlin; SOGoMemcachedHost = "memcached"; }
We left out the switch for SOGo Unicode compliance. But if you followed the SOGo guide it should be easy to add.
SOGo Apache Config
Our SOGo container will also run an Apache instance to serve the SOGo web interface. There is already a Apache config included in the SOGo container. We just have to set our domain name and redirect the output to the console so that Docker can pick it up.
roles/sogo/files/apache2/sogo.conf
<VirtualHost *:80> # Set to our domain ServerName https://mydomain.com
# Here we redirect the output to the console ErrorLog /dev/stderr CustomLog /dev/stdout common
# Config file provided by SOGo, already present in the SOGo image Include conf.d/SOGo.conf </VirtualHost>
To make the address book work with Apple Addressbook we need a separate redirect from port 8800 to SOGo. There is already a config example from SOGo which we will just used with our own domain name.
<Location /> Orderallow,deny Allow from all </Location> <Proxy http://127.0.0.1:20000> RequestHeader set "x-webobjects-server-port""8800" RequestHeader set "x-webobjects-server-name""mydomain.com:8800" RequestHeader set "x-webobjects-server-url""https://mydomain.com:8800" RequestHeader set "x-webobjects-server-protocol""HTTP/1.0" RequestHeader set "x-webobjects-remote-host""127.0.0.1" AddDefaultCharset UTF-8 </Proxy> # Again: redirect output to console ErrorLog /dev/stderr CustomLog /dev/stdout common </VirtualHost>
If we don’t redirect the Apache output to console the SOGo container would bloat up with logfiles. Also, we couldn’t see them through docker logs.
Deploying SOGo with Ansible
We create an Ansible role that copies our config files from above to the right directories and starts the SOGo container. Traefik will pick up the labels attached to SOGo and serve the SOGo frontend as well as the addressbook sync route. In our config it will also get a Let’s Encrypt certficate for our domain and redirect insecure HTTP traffic to HTTPS.