User Sync
Introduction
iRedMail brings already a very nice interface for management. The system is highly integrated and it includes an OpenLDAP server. Hence I already have a centralized OpnLDAP server I had the challenge to bring them somehow together. One way would have been to point iRedMail to the existing OpenLDAP server. The other way was to sync these two systems.
I decided to sync them, because I didn't want to bring any side effects or problems to this integrated system.
First I tried the syncrepl protocol that is provided by OpenLDAP. Syncrepl wouldn't do the job hence it synchronizes two OpenLDAP servers to the last bit, but I only need the mail users to be synced. So finally I decided to to do the sync with LDAP tools and bash scripting.
The Script
#!/bin/bash
#
# Sync users from another LDAP
#
# run in iRedMail-1.6.8/tools
#
. ../conf/global
. ../conf/core
#
# LDAP from which users will be synced (Source LDAP) fill in your data
#
urlAAI="ldap://openldap.simmy.ch:389"
basednAAI="dc=simmy,dc=ch"
binddnAAI="cn=binduser,ou=Users,dc=simmy,dc=ch"
bindpwAAI="cfhpqU@ZyJ9ZCrmDFqo4"
#
# iRedMail LDAP (target LDAP) fill in your data
#
urlMAIL="ldap://mail.simmy.ch:389"
basednMAIL="o=domains,dc=simmy,dc=ch"
#binddnMAIL="cn=vmail,dc=simmy,dc=ch"
#bindpwMAIL="SlwsYG3JUM2X2j5vyhLZ7MQx6Ozq5lQZ"
binddnMAIL="cn=Manager,dc=simmy,dc=ch"
bindpwMAIL="La4knk4Emg59U4hQ6zQorqlA0KmzrSKr"
#
# common variables from iRedMail setup
#
STORAGE_BASE_DIRECTORY="/var/vmail/vmail1"
STORAGE_BASE="$(dirname ${STORAGE_BASE_DIRECTORY})"
STORAGE_NODE="$(basename ${STORAGE_BASE_DIRECTORY})"
#
# Search filter for groups on source LDAP server
s_filter="(&(|(objectclass=inetOrgPerson))(|(memberof= cn=hMail_Users,ou=Groups,dc=simmy,dc=ch)))"
#
# get users uid from Source LDAP (I here have parameter which distinguish
# different types of users(hrEduPersonPrimaryAffiliation=djelatnik) for employees in Source LDAP
#
ldapsearch -H ${urlAAI} -x -D "${binddnAAI}" -w "${bindpwAAI}" -b "${basednAAI}" "${s_filter}" "uid" > AAI.ldif
#
# process .ldif output so in one line is only users uid
#
# example: AAI.ldif contains ONLY users uid each in newline
# user1
# user2
# etc...
#
sed '/#/d' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
sed '/search:/d' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
sed '/result:/d' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
sed '/dn:/d' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
sed 's/uid: //' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
sed '/^$/d' < AAI.ldif > newAAI.ldif; mv newAAI.ldif AAI.ldif
#
# add users to array (each line in another member of array) and delete temporary file
#
declare -a AAIkorisnici
let i=0
while IFS=$'\n' read -r line_data; do
AAIkorisnici[i]="${line_data}"
((++i))
done < AAI.ldif
rm -f AAI.ldif
#
# PROCESSING USERS
#
let i=0
while (( ${#AAIkorisnici[@]} > i )); do
#
# check if user already in Target LDAP (already have mailbox)
#
checkuserMAIL=$(ldapsearch -x -H ${urlMAIL} -b "${basednMAIL}" -D "${binddnMAIL}" -w "${bindpwMAIL}" uid=${AAIkorisnici[i]} | grep uid: | awk '{print $1}')
if [ "${checkuserMAIL}" = 'uid:' ];
then
#
# user exist on Target LDAP ... so I will only synchronize password beetwen two LDAPs
#
printf "User ${AAIkorisnici[i]} already exists on mail server\n";
printf "Synchronize mail password on openldap.simmy.cha: ${AAIkorisnici[i]}\n";
ldapsearch -H ${urlAAI} -x -D "${binddnAAI}" -w "${bindpwAAI}" -b "${basednAAI}" uid=${AAIkorisnici[i]} "(hrEduPersonPrimaryAffiliation=djelatnik)" "uid" "userPassword" | perl -MMIME::Base64 -MEncode=decode -n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode("UTF-8",decode_base64($1))/eg;binmode(STDOUT, ":utf8");print' > userAAIpwtoMAILpw.ldif
#
# put user data in variables and delete temporary file
#
userUID=$(grep uid: userAAIpwtoMAILpw.ldif | awk '{print $2}')
userPASSWORD=$(grep userPassword: userAAIpwtoMAILpw.ldif | awk '{print $2}')
userMAIL=${userUID}@hmail.simmy.org
rm -f userAAIpwtoMAILpw.ldif
ldapmodify -x -H ${urlMAIL} -D "${binddnMAIL}" -w "${bindpwMAIL}" <<EOF
dn: mail=${userMAIL},ou=Users,domainName=hmail.simmy.org,${basednMAIL}
changetype: modify
replace: userPassword
userPassword: ${userPASSWORD}
EOF
else
#
# user does not exist on Target LDAP so it will be added
#
printf "User does not exist on Target LDAP: ${AAIkorisnici[i]}\n";
printf "User will be added to mail server: ${AAIkorisnici[i]}\n";
#
# get user data ftom Source LDAP (in my case i need: givenName, sn, userPassword, uid)
#
ldapsearch -H ${urlAAI} -x -D "${binddnAAI}" -w "${bindpwAAI}" -b "${basednAAI}" uid=${AAIkorisnici[i]} "(hrEduPersonPrimaryAffiliation=djelatnik)" "givenName" "sn" "userPassword" "uid"| perl -MMIME::Base64 -MEncode=decode -n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode("UTF-8",decode_base64($1))/eg;binmode(STDOUT, ":utf8");print' > userAAItoMAIL.ldif
#
# put user data in variables and delete temporary file
#
userUID=$(grep uid: userAAItoMAIL.ldif | awk '{print $2}')
userGIVENNAME=$(grep givenName: userAAItoMAIL.ldif | awk '{print $2}')
userSN=$(grep sn: userAAItoMAIL.ldif | awk '{print $2}')
userPASSWORD=$(grep userPassword: userAAItoMAIL.ldif | awk '{print $2}')
userMAIL=${userUID}@hmail.simmy.org
#maildir="$( hash_domain "hmail.simmy.org")/$( hash_maildir ${userUID} )"
maildir="/var/vmail/vmail1/hmail.simmy.org/$( hash_maildir ${userUID} )"
#/var/vmail/vmail1/hmail.simmy.org/n/e/o/neo-2024.03.29.18.25.30/
rm -f userAAItoMAIL.ldif
#
# and finaly create user on mailserver....
#
ldapadd -x -H ${urlMAIL} -D "${binddnMAIL}" -w "${bindpwMAIL}" <<EOF
dn: mail=${userMAIL},ou=Users,domainName=hmail.simmy.org,${basednMAIL}
objectClass: inetOrgPerson
objectClass: shadowAccount
objectClass: amavisAccount
objectClass: mailUser
objectClass: top
accountStatus: active
storageBaseDirectory: ${STORAGE_BASE}
homeDirectory: ${STORAGE_BASE_DIRECTORY}/${maildir}
mailMessageStore: ${STORAGE_NODE}/${maildir}
mail: ${userMAIL}
mailQuota: 1048576000
userPassword: ${userPASSWORD}
cn: ${userSN} ${userGIVENNAME}
sn: ${userSN}
givenName: ${userGIVENNAME}
uid: ${userUID}
shadowLastChange: 0
amavisLocal: TRUE
enabledService: internal
enabledService: doveadm
enabledService: lib-storage
enabledService: mail
enabledService: pop3
enabledService: pop3secured
enabledService: imap
enabledService: imapsecured
enabledService: managesieve
enabledService: managesievesecured
enabledService: sieve
enabledService: sievesecured
enabledService: smtp
enabledService: smtpsecured
enabledService: deliver
enabledService: lda
enabledService: forward
enabledService: senderbcc
enabledService: recipientbcc
enabledService: shadowaddress
enabledService: displayedInGlobalAddressBook
EOF
fi
((++i))
done
The script is located in /root/iRedMail-1.6.8/tools and must be run from there. I basically found this script in an iRedMail forum.
First the scripts filters all users from cn=hMail_Users,ou=Groups,dc=simmy,dc=ch on the source LDAP server. Then the script checks if a user exists on iRedMail LDAP and if not, it creates the user. Otherwise only the password will be updated.
Useful links
https://www.openldap.org/doc/admin22/syncrepl.html
ToDo
switch to SSL/TLS
https://stackoverflow.com/questions/9468137/why-doesnt-ldapsearch-over-ssl-tls-work
Add a cronjob