Installation of a SOGo Debian Server for Groupware use

By dose | December 18, 2016
Under: Uncategorized

Motivation

Since the release of Microsoft Outlook 2016, it is possible to connect clients via the ActiveSync protocol, mostly known for its use on mobile devices to synchronize Calender, contacts and e-mail. So the complicated MAPI protocol (i.e. offered by OpenChange project) isn’t necessarily needed anymore.
So my goal was to set up a Linux server that offers ActiveSync protocol interfaces as well as the “classic” IMAP, Caldav and Carddav interfaces for integration with e-mail clients such as Thunderbird. For this SOGo seems to be a very good solution, because it offers you to use the standard Unix daemons in your desired configuration and is just a component that you can easily plug in to offer access to all components with above mentioned protocols which makes the server very easy to maintain without the need to use new, unfamiliar interfaces to manage it like partly with other groupware servers.

So I describe some simple setup for SOGo 2 (because I like its webinterface) on a Debian Jessie Server for handling mail on one domain in the combination Dovecot IMAP server and Postfix MTA. It is a mix of stuff taken from several tutorials available on the Internet.

Installation

1) Install SOGo 2

echo deb http://packages.inverse.ca/SOGo/nightly/2/debian/ jessie jessie >>/etc/apt/sources.list
apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4
apt-get update
apt-get install sogo mysql-server sope4.9-gdl1-mysql memcached

2) Fix some packages

sed -i "s/SHOWWARNING=true/SWOWWARNING=false/" /etc/tmpreaper.conf # suppress tmpreaper warnings
sed -i "s/127.0.0.1/localhost/" /etc/memcached.conf # Fix IPv6 errors
/etc/init.d/mysql restart 
/etc/init.d/memcached restart

3) Install MySQL and create user table

Unforutnately, SOGo doesn’t support PAM authentication for whatever reason. The most common solution in the tutorials is to use a LDAP server, which is of course also possible, but for me, it is harder to maintain due to my lack of LDAP-Skills. Therefore I decided to maintain a MySQL user database which will also be used by Dovecot for authentication and which I can add new shell users to with a simple shell script presented lateron. I prefer to have my mailbox users in /etc/passwd with a disabled login shell so that they automatically have a systemwide account, get their e-mails stored and delivered to their home-directories. That’s why I chose this method:

mysql -u root -p mysql
    CREATE DATABASE `sogo` CHARACTER SET='utf8';
    CREATE USER 'sogo'@'localhost' IDENTIFIED BY 'sogopasswd';
    GRANT ALL PRIVILEGES ON `sogo`.* TO 'sogo'@'localhost' WITH GRANT OPTION;
    USE sogo;
    CREATE TABLE sogo_users (c_uid VARCHAR(60) PRIMARY KEY, c_name VARCHAR(60), 
c_password VARCHAR(32), c_cn VARCHAR(128), mail VARCHAR(128), 
home VARCHAR(255) NOT NULL, uid INTEGER NOT NULL, gid INTEGER NOT NULL);
    FLUSH PRIVILEGES;
    quit

4) Install and configure dovecot IMAP-Server

apt-get install dovecot dovecot-sieve dovecot-lmtpd dovecot-managesieved dovecot-sql dovecot-mysql

You can also install dovecot-pop3d if needed.

/etc/dovecot/conf.d/10-auth.conf
#!include auth-system.conf.ext
!include auth-sql.conf.ext
/etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
/etc/dovecot/conf.d/10-master.conf
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}
/etc/dovecot/conf.d/15-lda.conf
lda_mailbox_autocreate = yes
/etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp {
  postmaster_address = postmaster@domain.com
  mail_plugins = $mail_plugins sieve
}
/etc/dovecot/conf.d/20-managesieve.conf
service managesieve-login {
  inet_listener sieve {
    port = 4190
    address = 127.0.0.1
  }
}
/etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}

userdb {
  driver = prefetch
}

userdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
/etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = host=localhost dbname=sogo user=sogo password=sogopasswd
default_pass_scheme = MD5
user_query = \
  SELECT home, uid, gid \
  FROM sogo_users WHERE c_uid = '%n'
password_query = \
  SELECT c_uid AS user, c_password AS password, \
    home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \
  FROM sogo_users WHERE c_uid = '%u'

4) Install the Postfix MTA

I assume that you have 2 DNS set up pointing to your mailserver: mail.domain.com and imap.domain.com, this also facilitates automatic mail server detection in most Mail applications.

apt-get install postfix postfix-policyd-spf-python
/etc/postfix/main.cf
myhostname = mail.domain.com
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

home_mailbox = Maildir/
mailbox_transport = lmtp:unix:private/dovecot-lmtp

# Restrictions
policy-spf_time_limit = 3600s
smtpd_helo_required = yes
smtpd_recipient_restrictions = permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    reject_unauth_pipelining,
    reject_non_fqdn_recipient,
    check_policy_service unix:private/policy-spf

smtpd_sender_restrictions = permit_mynetworks,
    permit_sasl_authenticated,
    reject_unknown_hostname,
    reject_unknown_recipient_domain,
    reject_unknown_sender_domain

smtpd_client_restrictions = permit_mynetworks,
    permit_sasl_authenticated
/etc/postfix/master.cf
# SPF
policy-spf  unix  -       n       n       -       -       spawn
     user=nobody argv=/usr/bin/policyd-spf

5) Install Apache 2 webserver

apt-get install apache2 sogo-activesync
a2enmod proxy
a2enmod proxy_http
a2enmod headers
a2enmod rewrite
a2enconf SOGo

6) configure Microsoft ActiveSync

Please read here carefully for related documentation.

apt-get install sogo-activesync

Uncomment and modify:

/etc/apache2/conf-available/SOGo.conf
ProxyPass /Microsoft-Server-ActiveSync \
	http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
	retry=60 connectiontimeout=60 timeout=660

7) Install Let’s encrypt SSL-certificates for your site

As self-signed certificates are always needed to be added to a trust list on the client side which makes setup for the user harder and we now have the possibility to get free SSL-certificates by the Let’s encrypt project, it is a good idea to use them instead. The downside of this may be the expiration time (3 months), but as renewal process can be automated with a cron-job, it’s not such a big issue.

/etc/apache2/sites-available/000-default.conf
ServerAlias mail.domain.com
ServerAlias imap.domain.com
echo "deb http://ftp.debian.org/debian jessie-backports main" >>/etc/apt/sources.list
apt-get update
apt-get install python-certbot-apache -t jessie-backports
letsencrypt --apache -d mail.domain.com -d imap.domain.com

Change TLS parameters in Postfix:

/etc/postfix/main.cf
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.domain.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.domain.com/privkey.pem

 

/etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_cert = imap.domain.com/fullchain.pem
ssl_key  = imap.domain.com/privkey.pem

Now we need to create a renewal-Cronjob for the requested certificates:

/etc/cron.weekly/letsencrypt
#!/bin/sh
letsencrypt renew
result=$(find /etc/letsencrypt/live/ -type l -mtime -1 )
if [ -n "$result" ]; then
  /etc/init.d/apache2 restart
  /etc/init.d/postfix restart
  /etc/init.d/dovecot restart
fi
chmod a+x /etc/cron.weekly/letsencrypt

8) Install DKIM

If you want to increase your e-mail reputation, it is a good idea to set up SPF and DKIM records. For DKIM, you can use opendkim daemon. This step is optional and requires full access to your DNS server.
This was shameslessly copied from here

apt-get install opendkim opendkim-tools
mkdir /etc/opendkim
mkdir /etc/opendkim/keys
mkdir /var/spool/postfix/var/run/opendkim
/etc/opendkim.conf
# OpenDKIM agiert als Mail Filter (= Milter) in den
# Modi signer (s) und verifier (v) und verwendet eine
# Socket-Datei zur Kommunikation (alternativ: lokaler Port)
Mode                    sv
# Socket                  local:/var/run/opendkim/opendkim.sock
# Socket                inet:12345@localhost
Socket                  local:/var/spool/postfix/var/run/opendkim/opendkim.sock

# OpenDKIM verwendet diesen Benutzer bzw.
# diese Gruppe
UserID                  postfix:postfix
UMask                   002
PidFile                 /var/run/opendkim/opendkim.pid

# OpenDKIM bei Problemen neustarten,
# aber max. 10 mal pro Stunde
AutoRestart             yes
AutoRestartRate         10/1h

# Logging (wenn alles funktioniert eventuell reduzieren)
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes

# Verfahren, wie Header und Body durch
# OpenDKIM verarbeitet werden sollen.
Canonicalization        relaxed/simple

# interne Mails nicht mit OpenDKIM verarbeiten
ExternalIgnoreList      refile:/etc/opendkim/trusted
InternalHosts           refile:/etc/opendkim/trusted

# welche Verschlüsselungs-Keys sollen für welche
# Domains verwendet werden
# (refile: für Dateien mit regulären Ausdrücke)
SigningTable            refile:/etc/opendkim/signing.table
KeyTable                /etc/opendkim/key.table

# diesen Signatur-Algorithmus verwenden
SignatureAlgorithm      rsa-sha256

# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others) between the signer
# and the verifier.  From is oversigned by default in the Debian pacakge
# because it is often the identity key used by reputation systems and thus
# somewhat security sensitive.
OversignHeaders         From
/etc/opendkim/trusted
127.0.0.1
::1
localhost
hostname
domain.com
/etc/opendkim/signing.table
# for E-Mails from xxx@domain.com use the key 'domain' for signing
*@domain.com domain
/etc/opendkim/key.table
# The key 'domain' is located in /etc/opendkim/keys/domain.private
domain domain.com:201611:/etc/opendkim/keys/domain.private
cd /etc/opendkim
opendkim-genkey -d domain.com -b 2048 -r -s 201611
mv 201611.private keys/domain.private
mv 201611.txt     keys/domain.txt
chown -R postfix:postfix /etc/opendkim
chmod -R go-rwx /etc/opendkim/keys
/etc/postfix/main.cf
milter_protocol = 6
milter_default_action = accept
smtpd_milters = unix:/var/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/var/run/opendkim/opendkim.sock

Then add the record from /etc/opendkim/keys/domain.txt to your DNS server.

9) Deploy some antivirus and antispam scripts for the MTA

It is a good idea to have some virus filtering and antispam on a Mailserver obiously. Now there are a lot of filtering solutions out there which can accomplish this, but I prefer not to rely on another piece of big software, so I just use a simple shellscript to do some simple mail filtering according to my needs. I’m using Clamav as an antivirus-filter, spamassassin as a spam-filter and some custom .zip file parsing using munpack which certainly can be improved.

apt-get install clamdscan spamassassin clamav-daemon mpack zip
mkdir /var/spool/spam
chmod 777 /var/spool/spam
mkdir /var/spool/virus
chmod 777 /var/spool/virus
/etc/spamassassin/local.cf
rewrite_header Subject *****SPAM*****
report_contact your_email@address.com
/etc/default/spamassassin
ENABLED=1
/etc/init.d/spamassassin start
mkdir /var/lib/dovecot/sieve
chmod 777 /var/lib/dovecot/sieve/

Now we want to move spam into the Junk folder automatically on the server side:

/var/lib/dovecot/sieve/global.sieve
require "fileinto";
if header :contains "X-Spam-Flag" "YES" {
    fileinto "Junk";
}
/etc/dovecot/conf.d/90-sieve.conf
plugin {
  sieve_before=/var/lib/dovecot/sieve/global.sieve
}

Now create the mail-scanner script that can be modified according to your needs:

/etc/postfix/mail-scanner.sh
#!/bin/sh

EX_OK=0
EX_BOUNCE=69
EX_DEFER=75

SENDMAIL="/usr/sbin/sendmail -G -i"

# prepare for scanning
INPUT=`mktemp /tmp/mail-scanner.XXXXXXXX`
OUTPUT=`mktemp /tmp/mail-scanner.XXXXXXXX`
if [ "$?" != 0 ]; then
    logger -s -p mail.warning -t scanner "Unable to create temporary files, deferring"
    exit $EX_DEFER
fi
trap "rm -f $INPUT $OUTPUT" EXIT TERM
cat >$INPUT

# check for viruses
/usr/bin/clamdscan --quiet - <$INPUT
return="$?"
if [ "$return" = 1 ]; then
    TARGET=/var/spool/virus/`basename $INPUT`
    logger -p mail.info "ClamAV found virus, quarantined as $TARGET"
    cp $INPUT $TARGET || { echo Cannot copy possible-virus mail; exit $EX_DEFER; };
    exit $EX_OK
elif [ "$return" != 0 ]; then
    logger -s -p mail.warning -t scanner "Temporary ClamAV failure $return, deferring"
    exit $EX_DEFER
fi

# check for undesired file extensions
MTMPDIR=`mktemp -d /tmp/mailattXXXXXXX`
BADEXT='[.](bat|cmd|com|cpl|exe|pif|msi|vb|vbs|js|jse|wsf)$'
munpack -C $MTMPDIR -q <$INPUT >/dev/null 2>&1
for i in $( ls $MTMPDIR | egrep -i '[.]zip' )
do
if [ $( unzip -l "${MTMPDIR}/${i}" | tail -n +4 |head -n -2 | egrep -i "${BADEXT}" | wc -l ) -gt 0 ]; then
    HASBADEXT=1
    break
fi
done
if [ $( ls $MTMPDIR | egrep -i "${BADEXT}" | wc -l ) -gt 0 ]; then
    HASBADEXT=1
fi

rm -r $MTMPDIR
if [ "$HASBADEXT" = 1 ]; then
    TARGET=/var/spool/virus/`basename $INPUT`
    logger -p mail.info "Found potential virus with suspicious extension, quarantined as $TARGET"
    cp $INPUT $TARGET || { echo Cannot copy possible-virus mail; exit $EX_DEFER; };
    exit $EX_OK
fi

# check for spam
/usr/bin/spamc -E <$INPUT >$OUTPUT
return="$?"
if [ "$return" = 1 ]; then
    # Activate this if you want to filter it out instead of just marking und filing into Junk
    #TARGET=/var/spool/spam/`basename $INPUT`
    logger -p mail.info "SpamAssassin found spam"
    #cp $INPUT $TARGET || { echo Cannot copy possible-spam mail; exit $EX_DEFER; };
    #exit $EX_OK
elif [ "$return" != 0 ]; then
    logger -s -p mail.warning -t scanner "Temporary SpamAssassin failure $return, delivering"
    # 1) deliver original mail
    OUTPUT=$INPUT
    # 2) or defer instead of delivering:
    # exit $EX_DEFER
fi

# deliver
$SENDMAIL "$@" <$OUTPUT
exit $?
chmod 755 /etc/postfix/mail-scanner.sh

Now register mail-scanner with postfix by appending content_filter option to smtpd line and adding the filter definition:

/etc/postfix/master.cf
smtp      inet  n       -       n       -       -       smtpd
  -o content_filter=scanner:dummy

scanner    unix  -       n       n       -       4       pipe
  flags=Rq user=nobody null_sender=
  argv=/etc/postfix/mail-scanner.sh -f ${sender} -- ${recipient}

10) Configure SOGo

/etc/sogo/sogo.conf
  /* Database configuration (mysql:// or postgresql://) */
  SOGoProfileURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_user_profile";
  OCSFolderInfoURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_folder_info";
  OCSSessionsFolderURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_sessions_folder";
  /* Mail */
  SOGoIMAPServer = localhost;
  SOGoSieveServer = sieve://127.0.0.1:4190;
  SOGoSMTPServer = 127.0.0.1;
  SOGoMailDomain = domain.com;
  SOGoMailingMechanism = smtp;
  /* Notifications */
  SOGoAppointmentSendEMailNotifications = YES;
  SOGoACLsSendEMailNotifications = NO;
  SOGoFoldersSendEMailNotifications = NO;

  /* Authentication */
  SOGoPasswordChangeEnabled = YES;

  /* SQL authentication example */
  /*  These database columns MUST be present in the view/table:
   *    c_uid - will be used for authentication -  it's the username or username@domain.tld)
   *    c_name - which can be identical to c_uid -  will be used to uniquely identify entries
   *    c_password - password of the user, plain-text, md5 or sha encoded for now
   *    c_cn - the user's common name - such as "John Doe"
   *    mail - the user's mail address
   *  See the installation guide for more details
   */
  SOGoUserSources = (
      {
        type = sql;
        id = users;
        viewURL = "mysql://sogo:sogopasswd@127.0.0.1:3306/sogo/sogo_users";
        canAuthenticate = YES;
        isAddressBook = YES;
        displayName = "Benutzer";
        userPasswordAlgorithm = md5;
      }
    );
  /* Web Interface */
  SOGoVacationEnabled = YES;
  SOGoSieveScriptsEnabled = YES;
  SOGoMailAuxiliaryUserAccountsEnabled = YES;

  /* General */
  SOGoLanguage = German;
  SOGoTimeZone = Europe/Vienna;
  SOGoSuperUsernames = (youradminacct);

  /* Activesync */
  SOGoMaximumPingInterval = 300;

If you read the ACtiveSync tuning link, you know that you need to maybe increase the number of active instances for handling requests according to the number of your users by setting PREFORK= value in /etc/default/sogo

Now finally you need to add your admin user you also specified above in SOGoSuperUsernames with SOGo. For every new user, you can add the user like a normal shell user with

adduser [username]

and maybe disable login shell in /etc/passwd. Then use the following script to register your new user with the MySQL DB (reenter user’s password when prompted):

cr_user.sh
#!/bin/bash
if [ -z $1 ]; then
  echo $0 \[Username\]
  exit
fi

ID=$1
C_UID=`id -u $ID`
if [ $? -ne 0 ]; then
  echo User $ID does not exist
  exit
fi
C_GID=`id -g $ID`
C_CN=`getent passwd $ID  | cut -d ':' -f 5 | cut -d"," -f1`
C_MAIL=$ID@`cat /etc/mailname`
C_HOME=`eval echo ~$ID`
read -sp "Password: " C_PASSWORD
echo "INSERT INTO sogo_users(c_uid,c_name,c_cn,c_password,mail,home,uid,gid) VALUES 
('$ID','$ID','$C_CN',MD5('$C_PASSWORD'),'$C_MAIL','$C_HOME',$C_UID,$C_GID) 
ON DUPLICATE KEY UPDATE c_uid='$ID',c_name='$ID',c_cn='$C_CN',c_password=MD5('$C_PASSWORD'),
mail='$C_MAIL',home='$C_HOME',uid=$C_UID,gid=$C_GID;" | mysql -u sogo --password=sogopasswd sogo

Finally you can add SOGo backup script to your daily backups (you need to adjust the file):

cp /usr/share/doc/sogo/sogo-backup.sh /usr/sbin/sogo-backup
vi /usr/sbin/sogo-backup

Finally restart all the stuff

3 comments | Add One

Comments

  1. sogonewbie - 01/5/2017 at 20:00

    perfect tutorial!
    thanks for your effort

  2. Peter Freimann - 01/26/2017 at 10:10

    nice tutorial – just have gone through all steps on a fresh jessie installation …

    please here are some small fixes :

    4)
    apt-get install dovecot-core dovecot-pop3d dovecot-imapd dovecot-sieve dovecot-lmtpd dovecot-managesieved dovecot-mysql

    6)
    the modified file should be
    /etc/sogo/sogo.conf

    7)
    letsencrypt gives unicodeerror … simple fix was to delete the non unicode char at offset 147 of /usr/lib/pymodules/python2.7/rpl-1.5.5.egg-info

  3. Chico - 03/31/2017 at 09:42

    thank you for the article…i have been searching for the same for quiet sometime..
    I am stuck at the point of creating sogo users using the script.. when I ran the script in echos Username and exits

Trackbacks

Leave a Comment

Name:

E-Mail :

Subscribe :
Website :

Comments :