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)
benmo
没有帐号? 立即注册