Friday 7 April 2017

High Availability with IREDMAIL Integrated with Active Directory




This is step by step guide for Centos 7. Server1 will be the Active node and Server2 will be the failover node. After failover when Server 1 comes back online all service will be restored to Server1.

A floating IP 10.21.202.110 will be used for the cluster config


To start with disable Firewall and Selinux on both servers and install prerequisites

#yum install vim wget bzip2 telnet pcs pacemaker corosync cman wget -y

Server 1
10.21.202.108
hostname: mail.example.com

Server 2
10.21.202.109
hostname: mail.example.com


Step 1: Make host file entries

Server1
$vim /etc/hosts
10.21.202.108   master1.example.com   master1
10.21.202.109   master2.example.com   master2
10.21.202.108   mail.example.com

Server2
$vim /etc/hosts
10.21.202.108   master1.example.com   master1
10.21.202.109   master2.example.com   master2
10.21.202.109   mail.example.com

Step 2: Install Mariadb On Both Servers. 

The mysql root password and other user password should be the same on both servers in order for HA to work with iRedMail/Roundcube.
#yum install mariadb-server -y



Step 3: Start Mariadb Multi-Master Replication Between The Two Servers

Insert the following configuration for replication. Create the file and paste the below

Server1:
#vim /etc/my.cnf.d/repl.cnf

[mysqld]
server-id               = 108
report_host             = master1
log_bin                 = /var/log/mariadb/mariadb-bin
log_bin_index           = /var/log/mariadb/mariadb-bin.index
relay_log               = /var/log/mariadb/relay-bin
relay_log_index         = /var/log/mariadb/relay-bin.index

Server2:
#vim /etc/my.cnf.d/repl.cnf

[mysqld]
server-id               = 102
report_host             = master2
log_bin                 = /var/log/mariadb/mariadb-bin
log_bin_index           = /var/log/mariadb/mariadb-bin.index
relay_log               = /var/log/mariadb/relay-bin
relay_log_index         = /var/log/mariadb/relay-bin.index

Restart Mariadb on both servers and run the following commands to initialize Mariadb

#systemctl restart mariadb.service
#systemctl enable mariadb.service
# mysql_secure_installation

Set the same root password on both servers. Now login to mysql and create the replication user.

Server1:
#mysql -u root -p<pass>
> create user 'replusr'@'%' identified by '<password>';
> grant replication slave on *.* to 'replusr';

To check Master status
>show master status;
Make a note of bin file name and position

Server2:
#mysql -u root -p<pass>
> create user 'replusr'@'%' identified by '<password>';
> grant replication slave on *.* to 'replusr';

To start replication on Server 2
>stop slave;
>change master to master_host='master1', master_user='replusr', master_password='<pass>’,
master_log_file='<bin file name>', master_log_pos=<pos>;
>start slave;

To check status use the following command
>show slave status;
Now check master status in Server2 and get bin file name and position
>show master status;

Server1
>stop slave;
>change master to master_host='master2', master_user='replusr', master_password='<pass>',
master_log_file='<bin file name>', master_log_pos=<pos>;
>start slave;
>show slave status;

Now both servers have Mariadb installed and multi-master replication is enabled.


Step 4: Install Remi And EPEL Repo On Both Servers

#yum install epel-release -y

#rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

Now enable remi repo for php5.5 on both servers
#vim /etc/yum.repos.d/remi.repo
-----------------------------------------------------------
[remi-php55]
name=Remi's PHP 5.5 RPM repository for Enterprise Linux 7 - $basearch
#baseurl=http://rpms.remirepo.net/enterprise/7/php55/$basearch/
#mirrorlist=https://rpms.remirepo.net/enterprise/7/php55/httpsmirror
mirrorlist=http://rpms.remirepo.net/enterprise/7/php55/mirror
# NOTICE: common dependencies are in "remi-safe"
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
--------------------------------------------------------------

Step 5: Install iRedMail With Openladap Backend On Server 1

Server 1:
#wget https://bitbucket.org/zhb/iredmail/downloads/iRedMail-0.9.6.tar.bz2
#tar xjvf iRedMail-0.9.6.tar.bz2
#cd iRedMail-0.9.6 && chmod +x iRedMail.sh
Note: Do not apply iRedMail's mysql settings













Step 6: Installing Calendar Plugin For RoundCube

#yum install composer -y

Comment this line in php.ini
#vim /etc/php.ini
;disable_functions=(.....)

#cd /tmp

#git clone https://git.kolab.org/diffusion/RPK/roundcubemail-plugins-kolab.git
 
#cd /tmp/roundcubemail-plugins-kolab/plugins
#mv calendar/ libcalendaring/ /var/www/roundcubemail/plugins/
#cd /var/www/roundcubemail/
#mv composer.json-dist composer.json

Now install dependancy for calendar plugin
#composer require "sabre/vobject" "~3.3.3"
Initialize the calendar database tables
# mysql -uroot -p<pass> roundcubemail < /var/www/roundcubemail/plugins/calendar/drivers/database/SQL/mysql.initial.sql

Enable the calendar plugin
#vim /var/www/roundcubemail/config/config.inc.php

Add 'calendar' to the list of active plugins:
$config['plugins'] = array((...)    'calendar',  );

Step 7: Installing Skins For Roundcube

Harry Skin
#mkdir /root/skins
#cd /root/skins
#git clone https://github.com/beliys/harry.git
#cd harry
#rm -rf .git

Barry Skin
#cd /root/skins
#wget http://indexnl.com/barry.zip
#unzip barry.zip

#mv harry barry /var/www/roundcubemail/skins/

Step 8: Restart And Enable All Services


systemctl enable amavisd.service
systemctl enable fail2ban.service
systemctl enable nginx.service
systemctl enable php-fpm.service
systemctl enable uwsgi.service
systemctl enable mariadb.service

systemctl restart amavisd.service
systemctl restart fail2ban.service
systemctl restart nginx.service
systemctl restart php-fpm.service
systemctl restart uwsgi.service
systemctl restart mariadb.service
systemctl restart iredapd

Step 9: Integrate iRedMail And Roundcube With Active Directory

Create an account "vmail" in AD with a strong password. Password should be set to never expire.

Disable unused iRedMail special settings:
postconf -e virtual_alias_maps=''
postconf -e sender_bcc_maps=''
postconf -e recipient_bcc_maps=''
postconf -e relay_domains=''
postconf -e relay_recipient_maps=''
postconf -e sender_dependent_relayhost_maps=''
Add your mail domain name in smtpd_sasl_local_domain and virtual_mailbox_domains:
postconf -e smtpd_sasl_local_domain='example.com'
postconf -e virtual_mailbox_domains='example.com'
Change transport maps setting:
postconf -e transport_maps='hash:/etc/postfix/transport'

Enable AD query. Note: We will create these 3 files later.
  • Verify SMTP senders
postconf -e smtpd_sender_login_maps='proxy:ldap:/etc/postfix/ad_sender_login_maps.cf'
  • Verify local mail users
postconf -e virtual_mailbox_maps='proxy:ldap:/etc/postfix/ad_virtual_mailbox_maps.cf'
  • Verify local mail lists/groups.
postconf -e virtual_alias_maps='proxy:ldap:/etc/postfix/ad_virtual_group_maps.cf'
  • Create/edit file: /etc/postfix/transport.
example.com dovecot
Notedovecot used here is a Postfix transport defined in /etc/postfix/master.cf, used to deliver received emails to local user mailboxes.
Run postmap so that postfix can read it:
# postmap hash:/etc/postfix/transport
  • Create file: /etc/postfix/ad_sender_login_maps.cf:
server_host     = ad.example.com
server_port     = 389
version         = 3
bind            = yes
start_tls       = no
bind_dn         = vmail
bind_pw         = password_of_vmail
search_base     = cn=users,dc=example,dc=com
scope           = sub
query_filter    = (&(userPrincipalName=%s)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
result_attribute= userPrincipalName
debuglevel      = 0
  • Create file: /etc/postfix/ad_virtual_mailbox_maps.cf:
server_host     = ad.example.com
server_port     = 389
version         = 3
bind            = yes
start_tls       = no
bind_dn         = vmail
bind_pw         = passwd_of_vmail
search_base     = cn=users,dc=example,dc=com
scope           = sub
query_filter    = (&(objectclass=person)(userPrincipalName=%s))
result_attribute= userPrincipalName
result_format   = %d/%u/Maildir/
debuglevel      = 0
Note: Here, we hard-code user's mailbox path in [domain]/[username]/Maildir/ format (result_format parameter). for example:example.com/postmaster/Maildir/.
  • Create file: /etc/postfix/ad_virtual_group_maps.cf:
server_host     = ad.example.com
server_port     = 389
version         = 3
bind            = yes
start_tls       = no
bind_dn         = vmail
bind_pw         = password_of_vmail
search_base     = cn=users,dc=example,dc=com
scope           = sub
query_filter    = (&(objectClass=group)(mail=%s))
special_result_attribute = member
leaf_result_attribute = mail
result_attribute= userPrincipalName
debuglevel      = 0
Note:
  • If your user have email address in both mail and userPrincipalName, you will get duplicate result. Comment out leaf_result_attribute line will fix it.
  • If your mail group account doesn't contain attribute mail and userPrincipalName, please try query_filter = (&(objectClass=group)(sAMAccountName=%u)) instead.
Also, we need to remove iRedAPD related settings in Postfix:
  1. Open Postfix config file /etc/postfix/main.cf
  2. Remove setting check_policy_service inet:127.0.0.1:7777.

Verify LDAP query with AD in Postfix

We can now use command line tool postmap to verify AD integration in postfix. Before testing, we have to create two testing mail accounts first:
  1. Create a mail user in AD. e.g. user@example.com.
  2. Create a mail group in AD. e.g. testgroup@example.com, then assign mail user user@example.com as group member.
  3. Query mail user account with below command:
# postmap -q user@example.com ldap:/etc/postfix/ad_virtual_mailbox_maps.cf
example.com/user/Maildir/
If nothing returned by the command, it means LDAP query doesn't get expected result. Please set debuglevel = 1 file /etc/postfix/ad_virtual_mailbox_maps.cf, then query again, it now will print detailed debug message. If you're not familiar with LDAP related info, please post the debug message in our online support forum to get help.
Verify sender login check:
# postmap -q user@example.com ldap:/etc/postfix/ad_sender_login_maps.cf
user@example.com
Verify mail group
# postmap -q testgroup@example.com ldap:/etc/postfix/ad_virtual_group_maps.cf
user@example.com
NOTEpostmap return nothing if:
  1. mail group doesn't exist
  2. mail group doesn't have any members

Remove iRedAPD integration in Postfix

iRedAPD relies on iRedMail LDAP scheme, so it's useless if you integrate iRedMail with Active Directory. We should remove the integration in Postfix to save some system resource.
To disable iRedAPD, please read tutorial: Manage iRedAPD.

Enable Active Directory integration in Dovecot

To query AD instead of local LDAP server, we have to modify Dovecot config file /etc/dovecot/dovecot-ldap.conf like below:
hosts           = ad.example.com:389
ldap_version    = 3
auth_bind       = yes
dn              = vmail
dnpass          = passwd_of_vmail
base            = cn=users,dc=example,dc=com
scope           = subtree
deref           = never
user_filter     = (&(userPrincipalName=%u)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
pass_filter     = (&(userPrincipalName=%u)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
pass_attrs      = userPassword=password
default_pass_scheme = CRYPT
user_attrs      = =home=/var/vmail/vmail1/%Ld/%Ln/Maildir/,=mail=maildir:/var/vmail/vmail1/%Ld/%Ln/Maildir/
Restart dovecot service to make it work.
Note: we don't have per-user quota limit here, you can set a hard-coded quota for all users in /etc/dovecot/dovecot.conf. For example:
plugin {
    [... omit other settings here ...]

    # Format: integer number + M/G/T (M -> MB, G -> GB, T -> TB).
    quota_rule = *:storage=1G
}
Now use command telnet to verify AD query after restarted Dovecot service:
# telnet localhost 143                     # <- Type this
* OK [...] Dovecot ready.

. login user@example.com password_of_user  # <- Type this. Do not miss the dot in the beginning
. OK [...] Logged in

^]                                         # <- Quit telnet with "Ctrl+]", then type 'quit'.
Note: Do NOT miss the dot character before login command. if it returns Logged in, then dovecot + AD works.


Step 10: Integrate Roundcube with AD for address book lookup

# vim /var/www/roundcubemail/config/config.inc.php

Delete all lines after the plugin config line and insert the below.
Modify the connection and user details as per the requirement

-----------------------------------------------------------------------------
#
# "sql" is personal address book stored in roundcube database.
# "global_ldap_abook" is the new LDAP address book for AD, we will create it below.
#
$config['autocomplete_addressbooks'] = array("sql", "global_ldap_abook");

# Enable setting below if Roundcube returns 'user@127.0.0.1' as email address
#$config['mail_domain'] = '%d';

#
# Global LDAP Address Book with AD.
#
$config['ldap_public']["global_ldap_abook"] = array(
    'name'          => 'Global Address Book',
    'hosts'         => array("ad.example.com"), // <- Set AD hostname or IP address here.
    'port'          => 389,
    'use_tls'       => false,   // <- Set to true if you want to use LDAP over TLS.
    'ldap_version'  => '3',
    'network_timeout' => 10,
    'user_specific' => false,

    'base_dn'       => "dc=example,dc=com", // <- Set base dn in AD
    'bind_dn'       => "vmail",             // <- bind dn
    'bind_pass'     => "<pass>", // <- bind password
    'writable'      => false,               // <- Do not allow mail user write data back to AD.
    'search_fields' => array('mail', 'cn', 'sAMAccountName', 'displayname', 'sn',
givenName'),

    // mapping of contact fields to directory attributes
    'fieldmap' => array(
        'name'        => 'cn',
        'surname'     => 'sn',
        'firstname'   => 'givenName',
        'title'       => 'title',
        'email'       => 'mail:*',
        'phone:work'  => 'telephoneNumber',
        'phone:mobile' => 'mobile',
        'phone:workfax' => 'facsimileTelephoneNumber',
        'street'      => 'street',
        'zipcode'     => 'postalCode',
        'locality'    => 'l',
        'department'  => 'departmentNumber',
        'notes'       => 'description',
        'photo'       => 'jpegPhoto',
    ),
    'sort'          => 'cn',
    'scope'         => 'sub',
    'filter'        => "(|(&(objectclass=group)(!(groupType:1.2.840.113556.1.4.803:=2147483648)))(&(objectclass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))))",
    'fuzzy_search'  => true,
    'vlv'           => false,   // Enable Virtual List View to more
                                // efficiently fetch paginated data
                                // (if server supports it)
    'sizelimit'     => '0',     // Enables you to limit the count of
                                // entries fetched. Setting this to 0
                                // means no limit.
    'timelimit'     => '0',     // Sets the number of seconds how long
                                // is spend on the search. Setting this
                                // to 0 means no limit.
    'referrals'     => false,   // Sets the LDAP_OPT_REFERRALS option.
                                // Mostly used in multi-domain Active
                                // Directory setups
);
-------------------------------------------------------------------------------------

Save and close the file

Step 11: Now Drop all databases created mysq iRedmail in Server 1



Step 12: Install iRedmail in server 2 with openLDAP backend



Note: copy config file from server 1 to server 2 before starting the installation. The config file contains the random db user passwords created for installation. They should be the same on both servers.

#scp /root/iRedMail-0.9.6/config root@server2:/root/iRedMail-0.9.6/config

Install iredmail with openldap backend and install the calendar plugin just like how we did for server1. Install the skins as well

Step 13: Install unision on both server. 

We will use unison to sync email, postfix  and dovecot config files

#yum install ocaml ocaml-camlp4-devel ctags ctags-etags -u
#cd ~
#wget http://www.seas.upenn.edu/~bcpierce/unison//download/releases/stable/unison-2.40.102.tar.gz
#tar xvfz unison-2.40.102.tar.gz
#cd unison-2.40.102
#make
#sudo cp -v unison /usr/local/sbin/
# sudo cp -v unison /bin/unison

Now enable shell access for vmail user and enable passwordless SSH for this user between the two servers. Emails are stored in /var/vmail/vmail1 directory and user "vmail" is the owner of the directory

On Server 1
Switch to user vmail and create the below crontab entry to sync mails across the two servers

#crontab -e
*/2 * * * *           /bin/unison -batch /var/vmail/vmail1 ssh://master2//var/vmail/vmail1

Now as root user
#crontab -e
0 5 * * *               /bin/unison -batch /etc/postfix ssh://master2//etc/postfix          
0 6 * * *               /bin/unison -batch /etc/dovecot ssh://master2//etc/dovecot

Step 14: Cluster configuration

On Server 1 and 2 set the same password for hacluster user

#passwd hacluster

Start PCSD service and enable it at startup
#systemctl start pcsd
#systemctl enable pcsd
#mkdir /etc/cluster

Server1

Now on server1 authorize the cluster nodes
#pcs cluster auth master1 master2

Now create the cluster and start the cluster
#pcs cluster setup --name MAILCLUSTER master1 master2
#pcs cluster start --all

Now disable STONITH and quorum as it is not required for a two node setup
#pcs property set stonith-enabled=false
#pcs property set no-quorum-policy=ignore

Now add the resources. We need a virtual IP 
#pcs resource create VirtualIP ocf:heartbeat:IPaddr2 ip=x.x.x.x cidr_netmask=24  op monitor interval=30s

Now we need a mail alert when ever failover happens
# pcs resource create MailTo ocf:heartbeat:MailTo email="admin@example.com" subject="MailServerFailOver" op monitor timeout="10" interval="10"

Now add postfix and dovecot as resources
# pcs resource create POSTFIX ocf:heartbeat:postfix op monitor interval=30s
# pcs resource create DOVECOT systemd:dovecot op monitor interval=30s

Now set a constraint so that they all run on the same server
# pcs constraint colocation set VirtualIP MailTo POSTFIX DOVECOT

Server1 is going to act as the active server and so we need to make sure that the services sticks to server1
# pcs constraint location VirtualIP prefers master1
# pcs constraint location MailTo prefers master1
# pcs constraint location DOVECOT prefers master1
# pcs constraint location POSTFIX prefers master1

Now stop and start the cluster
# pcs cluster stop --all
# pcs cluster start --all

Now we have iRedmail with High Availability


High Availability with IREDMAIL Integrated with Active Directory

This is step by step guide for Centos 7. Server1 will be the Active node and Server2 will be the failover node. After failover when ...