I signed up to help with hosting a mail server for some friends and I who are starting a local Smash Ultimate tourney. We need to keep up vendor relations and handle sales and things like that, and generally it’s just more attractive to have a personalized email rather than <orgname>@gmail.com. After just a bit of arm twisting, we acquired the domain and started working on getting everything rolling. Chris is handling the website itself, and I’m doing most of the backend work with my girlfriend Michele. I chose to go with Kolab Winterfell as the WOPI package is available directly in the repos, and I’m comfortable with taking the risk of it eating babies as my kiddo is 2 and a half now, so I think she’s a bit beyond the “baby” stage. As with all bleeding edge software, I have a backup strategy in place (albeit a manual one at this point) and also copious amounts of vodka on hand to account for any update breakage.

Let’s 👏 Just 👏 Jump 👏 In to it.

Installation itself is relatively straight forward, the instructions on the Kolab Installation Guide are generally accurate, aside from lacking a note regarding some packages being signed with an older key. This applies to Kolab 16 as well, for at least the pytz* package. Found this signing key in one of the Kolab support forum posts.

rpm --import "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x830C2BCF446D5A45"

You’ll get a message about ClamAV being upset, for some reason the current setup-kolab script doesn’t create /var/log/clamav. Make it yourself, set the permissions, and restart clamd. bash mkdir /var/log/clamav chown clamupdate /var/log/clamav freshclam systemctl restart clamd@amavisd

After this, I chose to go with nginx over Apache mostly because I prefer nginx and think Apache’s configurations are frustrating to work with, but let’s just pretend it’s because nginx is more performant. I’ve been using it for a while and have been dumping all of my SSL parameters in to a separate configuration file so that I can keep my reasonably well secured (A rating on Qualsys) configuration from clogging up my configurations with it’s 25 or so lines for every server block I have. The nginx configuration guide is also pretty much accurate, minus a few things that I had to add myself. I didn’t have to manually adjust the Kolab WAP URL in kolab.conf like it suggests, and I added a block to provide roundcubemail on /roundcubemail as well as the root of the subdomain. I didn’t do any magic to make creating the php-fpm sockets any less annoying, I just copy and pasted them from the installation guide and adjusted the domain names accordingly. Beyond that, you just need to create the configuration files for nginx and install certs with certbot. I also go in to the CentOS default /etc/nginx/nginx.conf and delete the default server blocks as I’m replacing them in my separate kolab.conf file. Replace smash and smasherie with whatever is applicable to your setup. I use Cloudflare as my DNS provider, so I use the Certbot Cloudflare DNS plugin as well. Using Certbot with a DNS plugin makes your life easier, and I’d highly suggest it. I’ll be using the Cloudflare plugin options in my example, but just replace the Cloudflare related bits with whatever matches your setup.

yum install nginx php-fpm python2-certbot (and optionally python2-certbot-dns-cloudflare)
systemctl stop httpd
systemctl disable httpd
<nginx configuration steps, delete server blocks from nginx.conf, create conf.d/kolab.conf and default.d/ssl.conf>
certbot-2 certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini -d kolab.smasherie.com -d collabora.smasherie.com
openssl dhparam -out /etc/letsencrypt/dh2048.pem 2048
nginx -t
<fix any errors as needed>
systemctl enable nginx php-fpm
systemctl start nginx php-fpm

File: /etc/nginx/conf.d/kolab.conf

#
# Force HTTP Redirect
#
server {
    listen 80 default_server;
    server_name _; # catch requests to the direct server name as well as to the prettier name
    server_name_in_redirect off;
    rewrite ^ https://mail.smasherie.com$request_uri permanent; # enforce https redirect
}

#
# Full Kolab Stack
#
server {
    server_name kolab.smasherie.com;
    access_log /var/log/nginx/kolab.smasherie.com-access_log;
    error_log /var/log/nginx/kolab.smasherie.com-error_log;

    include /etc/nginx/default.d/ssl-smash.conf;

    # support roundcubemail secure urls
    rewrite "^/roundcubemail/[a-zA-Z0-9]{16}/(.*)" /roundcubemail/$1;

        # roundcube
    location /roundcubemail {
        alias /usr/share/roundcubemail/public_html;
        index index.php;

        location ~ \.php$ {
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
                fastcgi_split_path_info ^(.+.php)(/.*)$;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }


    # Start common Kolab config

    ##
    ## Chwala
    ##
    location /chwala {
        index index.php;
        alias /usr/share/chwala/public_html;

        client_max_body_size 30M; # set maximum upload size

        # enable php
        location ~ .php$ {
            include fastcgi_params;
            fastcgi_param HTTPS on;
            fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_chwala.sock;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            # Without this, PHPSESSION is replaced by webadmin-api X-Session-Token
            fastcgi_param PHP_VALUE "session.auto_start=0
                session.use_cookies=0";
            fastcgi_pass_header X-Session-Token;
        }
    }

    ##
    ## iRony
    ##
    location /iRony {
        alias  /usr/share/iRony/public_html/index.php;

        client_max_body_size 30M; # set maximum upload size

        # If Nginx was built with http_dav_module:
        dav_methods  PUT DELETE MKCOL COPY MOVE;
        # Required Nginx to be built with nginx-dav-ext-module:
        # dav_ext_methods PROPFIND OPTIONS;

        include fastcgi_params;
        # fastcgi_param DAVBROWSER 1;
        fastcgi_param HTTPS on;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_iRony.sock;
        fastcgi_param SCRIPT_FILENAME $request_filename;
    }
    location ~* /.well-known/(cal|card)dav {
        rewrite ^ /iRony/ permanent;
    }

    ##
    ## Kolab Webclient
    ##
    location / {
        index index.php;
        root /usr/share/roundcubemail/public_html;

        # support for csrf token
        rewrite "^/[a-zA-Z0-9]{16}/(.*)" /$1 break;

        # maximum upload size for mail attachments
        client_max_body_size 30M;

        # enable php
        location ~ .php$ {
            include fastcgi_params;
            fastcgi_param HTTPS on;
            fastcgi_split_path_info ^(.+.php)(/.*)$;
            fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_roundcubemail.sock;
            fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }

    ##
    ## Kolab Web Administration Panel (WAP) and API
    ##
    location /kolab-webadmin {
        index index.php;
        alias /usr/share/kolab-webadmin/public_html;
        try_files $uri $uri/ @kolab-wapapi;

        # enable php
        location ~ .php$ {
            include fastcgi_params;
            fastcgi_param HTTPS on;
            fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_kolab-webadmin.sock;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            # Without this, PHPSESSION is replaced by webadmin-api X-Session-Token
            fastcgi_param PHP_VALUE "session.auto_start=0
                session.use_cookies=0";
            fastcgi_pass_header X-Session-Token;
        }
    }
    # kolab-webadmin api
    location @kolab-wapapi {
        rewrite ^/kolab-webadmin/api/([^.]*).([^.]*)$ /kolab-webadmin/api/index.php?service=$1&method=$2;
    }

    ##
    ## Kolab syncroton ActiveSync
    ##
    location /Microsoft-Server-ActiveSync {
        alias  /usr/share/kolab-syncroton/index.php;

        client_max_body_size 30M; # maximum upload size for mail attachments

        include fastcgi_params;
        fastcgi_param HTTPS on;
        fastcgi_read_timeout 1200;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_kolab-syncroton.sock;
        fastcgi_param SCRIPT_FILENAME /usr/share/kolab-syncroton/index.php;
    }

    ##
    ## Kolab Free/Busy
    ##
    location /freebusy {
        alias  /usr/share/kolab-freebusy/public_html/index.php;

        include fastcgi_params;
        fastcgi_param HTTPS on;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php-fpm/kolab.smasherie.com_kolab-freebusy.sock;
        fastcgi_param SCRIPT_FILENAME /usr/share/kolab-freebusy/public_html/index.php;
    }
    # End common Kolab config
}

File: /etc/nginx/default.d/ssl-smash.conf

listen 443 ssl http2; # managed by Certbot
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/kolab.smasherie.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/kolab.smasherie.com/privkey.pem; # managed by Certbot
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/letsencrypt/dh2048.pem;

# intermediate configuration. tweak to your needs.
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;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

## verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /etc/letsencrypt/live/kolab.smasherie.com/chain.pem;

resolver 1.1.1.1;

At this point, everything should be up and running properly. You can find your login to /kolab-webadmin in /etc/kolab/kolab.conf directly under the [ldap] section. Since we’re an organization that pertains to video games, we aren’t interested in the usual first.last@domain.org. We chose to use handle@smasherie.com instead, and even if you don’t necessarily want to use that, the Recipient Policy will be enforced, and you may have a less silly reason to want to adjust your email address format. In the Kolab Web Interface, go under Settings -> Kolab User -> Attributes -> mail, and hit edit. From there, change the Value from “Generated (Read-Only)” to “Normal” and feel free to delete the “Default Value” field data and leave it blank. After this, you’ll need to make some edits to your Kolab configuration, comment out the relevant bits and add the first line.

daemon_rcpt_policy = False
#policy_uid = %(surname)s.lower()
#primary_mail = %(surname)s@%(domain)s
#secondary_mail = {
#       0: {
#       "{0}.{1}@{2}": "format('%(givenname)s'[0:1].capitalize(), '%(surname)s', '%(domain)s')"
#       },
#       1: {
#       "{0}@{1}": "format('%(uid)s', '%(domain)s')"
#       },
#       2: {
#       "{0}@{1}": "format('%(givenname)s.%(surname)s', '%(domain)s')"
#       }
#       }

This is about as much as I can think of that I ran in to while trying to set up a Kolab server. I’ll have another post regarding integrating Amazon SES, and then a final followup on deploying Collabora Online Development Edition alongside Kolab and using it with kolab-wopi to provide LibreOffice editing capabilities for your shared files. Also here’s to not making it a full year between blog posts.