Exchange迁移至iRedMail(LDAP)
文档导航
- Exchange导出用户列表与通讯组
- 数据处理与导入LDAP
- 同步用户历史邮件
- 登录验证
- 修改DNS解析
- 其他需求
Exchange导出用户列表与通讯组
- 导出Exchange用户列表
依次打开'Exchange Management Console' --> 'Microfoft Exchange On-Permiss(example.com)' --> 'Recipient Configuration'-->'Mailbox'
右键'Export list...',选择本地磁盘,保存为'user.txt'
- 导出Exchange通讯组列表
这里,我们不选择EMC中导出,因为单独的groups我们并没有用,需要与用户有对应关系的一个列表,so,这里我们使用Powershell导出这个关系
打开powershell,依次执行如下操作
# 添加Exchange命令行管理程序(根据自己的版本执行下面命令) # Exchange 2007 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin # Exchange 2010 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 # Exchange 2013 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn # 也可以用*代替 Add-PSSnapin Microsoft.Exchange*
# 保存下面脚本,使用powershell执行,完成后会在 # D:\exchange_contact下生成以通讯组命名的文件,文件内包含邮件用户 param ( # 定义日志输出路径 $temppath="D:\exchange_contact" ) #判断日志路径是否存在,如果不存在则建立目录 if(!(Test-Path $temppath)) { #建立日志路径 New-Item -Path $temppath -ItemType "directory" } $groupNum=get-distributiongroup #将所有通讯组输出到一个变量 #变更命令执行路径 Set-Location $temppath #循环通讯组 foreach($groupnow in $groupNum) { # 输出通讯组成员,并且只输出名字 $groupmember=get-distributiongroupmember -identity $groupnow.name|select name #定义输出的通讯组成员导出的文件 $tmppath=$temppath+"\"+$groupnow.PrimarySmtpAddress #将通讯组成员导出为csv 文件 $groupmember |Export-Csv -Path $tmppath -encoding utf8 }
数据处理与导入LDAP
这里我们需要张大'iRedMail'安装程序tools目录下提供的脚本'create_mail_user_OpenLDAP.py'脚本来导入用户,
在这之前,我们要先处理之前得到的文本数据,呈现出以下格式来给这个脚本处理
domain name, username, password, [common name], [quota_in_bytes], [groups]
将之前的'user.txt'与'exchange_contact'打包上传到你iRedMail所在的邮件服务器
[root@mail hkmail]# ls exchange_contact userlist.sh user.txt
- 写一个脚本处理user.txt与exchange_contact,生成需要的csv
bash userlist.sh >> exchange_mail.csv
userlist.sh脚本内容如下:
#!/bin/bash # 生成的密码格式为username123(不包括@和后面一截) # 默认配额4G while read line; do display_name=`echo $line | awk -F'"' '{print $2}'` mail_address=`echo $line | awk '{print $NF}'` user_name=`echo $mail_address | awk -F'@' '{print $1}'` domain_name=`echo $mail_address | awk -F'@' '{print $2}'` default_pass="${user_name}123" memberofgroups=`grep -i "$display_name" -R exchange_contact/ | \ awk -F'[/:]' '{print $2}' | sort -n | uniq | tr '\n' ':' | sed 's/:$//'` echo "$domain_name, $user_name, $default_pass, $display_name,\ 4294967296, $memberofgroups" done<user.txt
查看生成的csv是否正确
[root@mail hkmail]# tail -n 5 exchange_mail.csv example.com, Yan.Ho, Yan.Ho123, Yan Ho, 4294967296, fna@example.com example.com, Yvonne.yan, Yvonne.yan123, Yvonne yan, 4294967296, sale@example.com example.com, long.zhang, long.zhang123, Zhang Long, 4294967296, example.com, Zhang.Yu, Zhang.Yu123, Zhang Yu, 4294967296, example.com, Zheng.Honglian, Zheng.Honglian123, Zheng Honglian, 4294967296,
- 添加虚拟域(域都没有怎么导入?iredmail后台添加)
- 生成通讯组(这里使用一个脚本来完成工作)
# 提取通讯组列表到文件 [root@mail ldap]# ll exchange_contact/* | awk '{print $NF}' >> groups.txt # 执行脚本添加通讯组 [root@mail ldap]# ./managerGroups.sh -f groups.txt -a 添加组: Message@example.com [ OK ] 添加组: mail@example.com [ OK ] 添加组: STXGRMSupportGroup@example.com [ OK ] 添加组: Support.stcdc.lehman@example.com [ OK ] 添加组: szucp@example.com [ OK ] 添加组: VTCNSRSSupportGroup@example.com [ OK ] 添加组: WarehousePROSupportGroup@example.com [ OK ] 添加组: WhelanEmail@example.com [ OK ] 成功数: 8, 失败数: 0
脚本如下(自行修改ldap密码和访问权限,使用: bash managerGroups.sh -h):
#!/bin/bash ############################################# # 添加或者删除某个组 # Anthor: 本末丶 # filename: managerGroups.sh ############################################# . /etc/rc.d/init.d/functions # 修改成你的配置 Policy='domain' rootdn="cn=manager,dc=iredmail,dc=org" basedn='o=domains,dc=iredmail,dc=org' ldapass="you_pass" # 默认参数 times=$(date +%s) tmpfile="./.tmp.${0##*/}.${times}.ldif" ldapsearch="/usr/bin/ldapsearch" ldapadd="/usr/bin/ldapadd" ldapdelete="/usr/bin/ldapdelete" args="$*" ### Function Begin ### # 帮助 usage() { echo -e " ldap通讯组管理: Usage: $0 [-g|-f] [-a|-d] -g 要新增的组名 -f 组名列表文件, -a 添加组 -d 删除组 Example: 1.新增组it@example.com \e[32m$0 -g it@example.com -a\e[0m 2.删除组it@comero.com \e[32m$0 -g it@example.com -d\e[0m 3.批量新增组 \e[32m$0 -f grouplist.txt -a\e[0m " exit 1 } # 执行添加操作 ldapAdd() { mail="$1" name=`echo ${mail} | awk -F"@" '{print $1}'` domain=`echo ${mail} | awk -F"@" '{print $2}'` options="mail=${mail},ou=Groups,domainName=${domain},${basedn}" value=`${ldapsearch} -LL -H ldap://127.0.0.1 -x -D "${rootdn}" -w ${ldapass} \ -b "${options}" -t mail=${mail} mail 2>/dev/nul | egrep -v "version|^$"` if [[ -z ${value} ]];then cat > ${tmpfile} <<-EOF dn: mail=${mail},ou=Groups,domainName=${domain},${basedn} accountStatus: active mail: ${mail} objectClass: mailList objectClass: top enabledService: mail enabledService: deliver enabledService: displayedInGlobalAddressBook accessPolicy: ${Policy} description: ${name} EOF # 执行ldapadd导入新数据 ${ldapadd} -x -D ${rootdn} -w ${ldapass} -f ${tmpfile} >/dev/null 2>&1 if [ $? -eq 0 ];then success;echo -e $"添加组: \e[33m${mail}\e[0m" # 成功后删除临时文件 rm -f ${tmpfile} return 0 else failure;echo -e $"\e[31m添加组: ${mail}\e[0m" return 1 fi else failure;echo -e $"\e[31m已存在组: ${mail}\e[0m" return 1 fi } ldapDelete() { mail="$1" name=`echo ${mail} | awk -F"@" '{print $1}'` domain=`echo ${mail} | awk -F"@" '{print $2}'` options="mail=${mail},ou=Groups,domainName=${domain},${basedn}" value=`${ldapsearch} -LL -H ldap://127.0.0.1 -x -D "${rootdn}" -w ${ldapass} \ -b "${options}" -t mail=${mail} mail 2>/dev/nul | egrep -v "version|^$"` if [[ ! -z ${value} ]]; then ${ldapdelete} -x -D "${rootdn}" -w ${ldapass} "${options}" if [ $? -eq 0 ];then success;echo -e $"删除组: \e[33m${mail}\e[0m" return 0 else failure;echo -e $"\e[31m添加组: ${mail}\e[0m" return 1 fi else failure;echo -e "\e[31m未查询到相关组: ${mail}\e[0m" return 1 fi } groupAction() { succ=0 fail=0 echo "" # 判断操作的是文件还是单个用户 if $flag;then while read group; do ${action} $group if [ $? -eq 0 ];then let succ+=1 else let fail+=1 fi done<${groups} else ${action} $groups if [ $? -eq 0 ];then let succ+=1 else let fail+=1 fi fi echo -e "成功数: \e[33m${succ}\e[0m, 失败数: \e[31m${fail}\e[0m\n" } ### Function End ### # 参数为空打印帮助 if [ ${#args} -lt 1 ];then usage fi # 获取选项参数 while getopts :f:g:ad opt; do case $opt in g) groups=$OPTARG flag=false ;; f) groups=$OPTARG flag=true ;; a) action="ldapAdd" msg="新增" ;; d) action="ldapDelete" msg="删除" ;; \?|-h|--help) usage ;; esac done # 参数判断执行 if [ ! -z $groups ] then if [ ! -z $action ];then groupAction else usage fi else usage fi
- 准备就绪,开始导入ldap
找到张大的脚本'create_mail_user_OpenLDAP.py',先修改下符合自己的需求
cd /opt/soft/iRedMail-0.9.5-1/tools/ vim create_mail_user_OpenLDAP.py
修改如下内容:
# LDAP server address. LDAP_URI = 'ldap://127.0.0.1:389' # LDAP base dn. BASEDN = 'o=domains,dc=iredmail,dc=org' # Bind dn/password BINDDN = 'cn=Manager,dc=iredmail,dc=org' BINDPW = 'you_pass' # 我这里只支持SSHA DEFAULT_PASSWORD_SCHEME = 'SSHA' # 212行左右,我的组包含的域名后缀,修改下 # Get group list. if groups.strip() != '': groups = groups.strip().split(':') for i in range(len(groups)): #groups[i] = groups[i] + '@' + domain groups[i] = groups[i]
- 执行脚本生成ldif
[root@mail tools]# python create_mail_user_OpenLDAP.py exchange_mail.csv < INFO > Remove exist file: exchange_mail.csv.ldif < INFO > User data are stored in /opt/soft/iRedMail-0.9.5-1/tools/hkmail.csv.ldif, you can verify it before importing it. < INFO > You can import it with below command: ldapadd -x -D cn=Manager,dc=iredmail,dc=org -W -f /opt/soft/iRedMail-0.9.5-1/tools/exchange_mail.csv.ldif
- 导入数据
ldapadd -x -D cn=Manager,dc=iredmail,dc=org -W -f exchange_mail.csv.ldif -c
同步用户历史邮件(imapsync)
由于Exchange用户的密码我们不可知,所以需要一个账户有权限访问所有用户的邮件数据,
执行如下操作:
- Exchange中新建账户'pengjk@example.com'
- 授权改用户对所有用户完全访问权限
# powershell中执行 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 # 注:下面是一条命令,生成PDF会把下面截断,我分开2行了,注意自己贴上 Get-Mailbox -ResultSize unlimited | Add-MailboxPermission -User #接下面 pengjk -AccessRights fullaccess -InheritanceType all
输出如下
PS D:\> & '.\full acccess permission.ps1' Identity User AccessRights IsInherited Deny -------- ---- ------------ ----------- ---- EXAMPLE.COM/Us... EHK\pengjk {FullAccess} False False EXAMPLE.COM/EH... EHK\pengjk {FullAccess} False False EXAMPLE.COM/EH... EHK\pengjk {FullAccess} False False EXAMPLE.COM/EH... EHK\pengjk {FullAccess} False False EXAMPLE.COM/Us... EHK\pengjk {FullAccess} False False EXAMPLE.COM/EH... EHK\pengjk {FullAccess} False False
- iRedMail主机上安装imapsync
yum -y install imapsync
- 执行同步
同步一个账户的命令
imapsync --host1 imap.example.com --user1 test01@example.com \ --authuser1 pengjk@example.com --password1 you_pass \ --host2 imap.example02.com --user2 test02@example.com --password2 test02123
为了批量同步,我们使用一个脚本(mail_sync.sh)
#!/bin/bash source_mail='imap.example.com' source_admin='pengjk@example.com' source_admin_pass='you_pass' dest_mail='imap.example.com' pass_code='123' while read source_user do dest_user=${source_user} dest_user_pass=`echo ${dest_user} | \ awk -v code=${pass_code} -F'@' '{print $1code}'` imapsync -usecache --host1 ${source_mail} --user1 ${source_user} \ --authuser1 ${source_admin} --password1 ${source_admin_pass} \ --host2 ${dest_mail} --user2 ${dest_user} --password2 ${dest_user_pass} \ >/dev/null done<mail_user.txt
后台同步
# 用之前Exchange导出的用户列表生成一个同步的列表 awk '{print $NF}' user.txt >> mail_user.txt # 同步比较慢,我们放后台同步(确保对端可连) nohup ./mail_sync.sh &
登录验证(略)
自建DNS验证吧
修改DNS解析(略)
其他需求
导入的用户,每个用户的密码随机生成,迁移前,向每个用户发送一份邮件,邮件内容包括: 新的邮件服务器地址,用户名,随机密码
来,说做就做,
文本如下
[root@mail hkmail]# tail -n3 mailpass.txt abuse@example.com admin.azure@example.com admin.strawberrynet@example.com
- 利用脚本处理文本修改密码,同时会生成一个“账户,密码”的关系表
[root@mail hkmail]# bash userPassword.sh -f mailpass.txt 修改用户密码: abuse@example.com(OHjxaF391z) [ OK ] 修改用户密码: admin.azure@example.com(TOOk1Y4Nn9) [ OK ] 修改用户密码: admin.strawberrynet@example.com(kLjiSECO8d) [ OK ] ... ... 成功用户数:79, 失败用户数: 0 生成列表文件: ./mailpass.txt.1487231757.csv
脚本userPassword.sh如下(使用方法: ./userPassword.sh -h)
#!/bin/bash ############################################# # 用户密码管理 # Anthor: 本末丶 ############################################# . /etc/rc.d/init.d/functions # 参数 times=$(date +%s) tmpfile="./.tmp.${0##*/}.${times}.ldif" rootdn="cn=manager,dc=iredmail,dc=org" ldapass="qaz123" ldapcmd="/usr/bin/ldapsearch" args="$*" usage() { echo -e " 用户密码管理: Usage: $0 [-u|-f] [-p] -u 用户邮件名称,不指定'-p'则生成随机密码 -f 用户邮件列表文件,每行一个用户(随机密码),或用户+密码(指定密码) -p 修改指定密码 Example: 1.用户pengjk@example.com修改密码为随机密码 \e[32m$0 -u pengjk@example.com\e[0m 2.用户pengjk@example.com修改密码为'abc123456' \e[32m$0 -u pengjk@example.com -p abc123456\e[0m 3.批量修改用户密码,自动生成密码的话,会生成一个列表文件名+时间戳.csv文件 \e[32m$0 -f mail_user.txt\e[0m " exit 1 } modifyTmpFile() { # 修改临时文件 tmpfile=$1 changeto=$2 passwd=`encryptPass ${changeto}` sed -i "/^dn:/a\changetype: modify\nreplace: \ userPassword\nuserPassword: ${passwd}\n" ${tmpfile} ldapmodify -h localhost -p 389 -D ${rootdn} -w ${ldapass} -c -f ${tmpfile} \ >/dev/null 2>&1 return $? } # 加密密码 encryptPass() { pass=$1 doveadm pw -s 'ssha' -p "$pass" } # 随机密码 generate_passwd() { tr -cd '[:alnum:]' < /dev/urandom | fold -w10 | head -n1 } changeUser() { suc=0 fail=0 # 判断用户是否存在,并导出用户dn if $files;then while read user changeto do # 未设置密码就生成随机密码 if [ -z $changeto ];then changeto=`generate_passwd` # 标记用于否写入关系列表文件 flag=1 fi value=`${ldapcmd} -LL -H ldap://127.0.0.1 -x -D "${rootdn}" \ -w ${ldapass} -b "o=Domains,dc=iredmail,dc=org" -t mail=${user} dn | \ egrep -v "version|^$" | sed -re '/dn/N;s/\n\s+//'` if [ ! -z ${value} ];then echo ${value} > ${tmpfile} # 将账号密码对应关系写入文件 [ ! -z $flag ] && echo "${user}, ${changeto}" >> \ ./${listfile}.${times}.csv modifyTmpFile ${tmpfile} ${changeto} if [ $? -eq 0 ];then success;echo -e \ $"修改用户密码: \e[33m${user}(${changeto})\e[0m \n" let suc+=1 # 删除临时文件 rm -f ${tmpfile} else failure;echo -e "失败用户: \e[31m${user}\e[0m" let fail+=1 fi else failure;echo -e $"不存在用户: \e[31m${user}\e[0m" let fail+=1 fi done<${listfile} echo -e "成功用户数: \e[33m${suc}\e[0m, 失败用户数: \e[33m${fail}\e[0m\n" [ ! -z $flag ] && echo -e \ "生成列表文件: \e[32m./${listfile}.${times}.csv\e[0m" else [ -z $changeto ] && changeto=`generate_passwd` value=`${ldapcmd} -LL -H ldap://127.0.0.1 -x -D "${rootdn}" \ -w ${ldapass} -b "o=Domains,dc=iredmail,dc=org" -t mail=${mailuser} dn | \ egrep -v "version|^$" | sed -re '/dn/N;s/\n\s+//'` if [[ ! -z ${value} ]];then echo ${value} > ${tmpfile} modifyTmpFile ${tmpfile} ${changeto} res=$? if [ $res -eq 0 ];then success;echo -e \ $"修改用户密码: \e[33m${mailuser}(${changeto})\e[0m \n" let suc+=1 # 删除临时文件 rm -f ${tmpfile} else failure;echo -e $"失败用户: \e[31m${mailuser}\e[0m" let fail+=1 fi else failure;echo -e $"不存在用户: \e[31m${mailuser}\e[0m" let fail+=1 fi fi } # get args if [ ${#args} -lt 1 ];then usage fi while getopts :u:f:p: opt; do case $opt in u) mailuser=$OPTARG files=false ;; f) listfile=$OPTARG files=true ;; p) changeto=$OPTARG ;; \?|-h|--help) usage ;; esac done # Run func if [[ "$mailuser"x != ""x ]];then changeUser elif [ "$listfile"x != ""x ];then changeUser else usage fi
- 修改完密码后,我们需要发送一份邮件给迁移前的账户
这里使用上一步生成的'mailpass.txt.1487231757.csv'文件,用python读取这个文本发送就好了
# python2.7,python3下正常,py2.6自行修改代码 [root@mail hkmail]# python3.5 mail_to_user.py mailpass.txt.1487231757.csv [Info] Sent abuse@example.com OK [Info] Sent admin.azure@example.com OK [Info] Sent admin.strawberrynet@example.com OK ... ... Total: 79
mail_to_user.py脚本内容如下
# -- encoding: utf8 -- from email.header import Header from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders import smtplib import sys # 处理的文件 passfile = sys.argv[1] # 发送方邮件的地址 mail_from = 'mailadmin@163.com' smtpserver = 'smtp.163.com' password = 'you_pass' # 新邮件服务器地址 mail_domain = 'mail.example.com' # 附件选择 attach_file = False file_name = '邮件系统说明.zip' file_path = '/home/pengjk' # debug mail_debug = 0 def mailSent(message,mail_to): msg = MIMEMultipart() msg['From'] = ('邮件管理员<%s>' % mail_from) msg['To'] = mail_to msg['Subject'] = Header(subject, 'utf-8').encode() msg.attach(MIMEText(message, 'html', 'utf-8')) if attach_file: with open('%s/%s' % (file_path, file_name), 'rb') as f: mime = MIMEBase('application', 'zip', filename=file_name) mime.add_header('Content-Disposition', 'attachment', filename=file_name) mime.add_header('Content-ID', '<0>') mime.add_header('X-Attachment-Id', '0') mime.set_payload(f.read()) encoders.encode_base64(mime) msg.attach(mime) try: server = smtplib.SMTP(smtpserver, 25) server.set_debuglevel(mail_debug) server.login(mail_from, password) server.sendmail(mail_from, mail_to, msg.as_string()) server.quit() print('[Info] Sent %s OK' % mail_to) except Exception as e: print('[Error]: %s' % e) if __name__ == '__main__': total = 0 subject = '新邮件服务器信息(注意保存邮件!)' f = open(passfile, 'r') for line in f.readlines(): mail_line = line.split(',') mail_user = mail_line[0] mail_pass = mail_line[1] message = ''' <html><body> <p><b>新邮件服务器登陆地址:</b> <a href="https://%s">%s</a></p> <ul> <li><b>默认账号:</b> <font color="#FF0000">%s</font></li> <li><b>默认密码:</b> <font color="#FF0000">%s</font></li> </ul> </body></html> ''' % (mail_domain, mail_domain, mail_user, mail_pass) mailSent(message,mail_user) total += 1 f.close() print('Total: %s' %total)
没有帐号? 立即注册