Exchange 撤销后的连锁反应:一次 AD mail 属性变更引发的 Gerrit 登录事故
这不是 Gerrit 的 bug,而是设计如此。真正的冲突点是:Gerrit 里有我手动添加的邮箱AD 里mail是空的AD 里mail有值了Gerrit 登录时尝试“再添加一次同样的邮箱”于是 Gerrit 认为:“这个邮箱已经属于某个 account 了,我不能再给你建 external ID。Exchange 撤销之后,AD 某些原本被自动维护的属性,悄悄变成了“没人管”,而系统却还在继续依
公司内部不少系统都有邮箱通知功能。最近业务部门反馈:
部分同事收不到系统通知邮件了,收件人没他。
第一反应当然是到 Windows AD 域控上面查看用户属性,但很快就发现一个共性:
❗ 这些用户在 Windows AD 里的
这一下怀疑是不是因为 Exchange 邮箱服务器撤掉了
继续一对比就更明显了:
- Exchange 废弃 之前 创建的 AD 用户
mail属性都有值 - Exchange 废弃 之后 创建的 AD 用户
mail基本都是空的 - 后来虽然换了新的邮箱系统,但 邮箱地址本身和原 Exchange 是一致的
问题,基本就对上了。
一、先捋清楚:Windows AD、Exchange 和 mail 属性到底是什么关系
这个问题如果不把底层关系想清楚,后面会一直踩坑。
1️⃣ AD 负责“账号”,Exchange 负责“邮箱”
简单说一句话:
AD 是目录服务,Exchange 是“往 AD 里写邮箱属性”的系统
Windows AD 本身只管:
- 用户
- 组
- 权限
- 登录标识
而 邮箱这件事,本来不是 AD 的核心能力。
当你给一个 AD 用户“启用 Exchange 邮箱”时,Exchange 会帮你在 AD 用户对象上写一堆属性,比如:
mail
主 SMTP 邮箱地址proxyAddresses
所有邮箱地址(主的 / 别名 / 历史 X500)- 以及一堆 Exchange 自己用的 GUID / 标记属性
也就是说:
AD 里的
AD 自己并不会“自动生成一个靠谱的邮箱地址”。
2️⃣ Exchange 撤销之后,问题就来了
当 Exchange 被撤掉之后,会出现一个很尴尬但很常见的状态:
- AD 还在
- 用户还在
- 但 再也没有系统会自动帮你维护
mail属性
于是结果就是:
- 老用户(Exchange 时代创建的) →
mail还有值 - 新用户(Exchange 废弃后创建的) →
mail是空的
而偏偏 公司很多内部系统,默认都拿 AD 的 mail 当邮箱用。
这就直接导致了你看到的现象:
系统发不出邮件,但 AD 里用户“看起来又是正常的”
二、邮箱通知系统怎么补?两个方案
既然问题确认是 mail 属性为空,那解决方案其实就两种。
方案一:改业务系统(理想,但不现实)
让业务系统别再依赖 mail,改成用:
UserPrincipalName(UPN)
原因是:
- UPN 是 AD 创建账号时就带的
- 本身就长得像邮箱:
user@domain - 很多公司本来就是用它当登录名
但现实情况是:
系统太多,改不过来
方案二:我来兜底,批量补 AD 的 mail 属性
那就只能运维兜底了。
我做的事情很简单:
- 找出
mail为空 - 用
UserPrincipalName去填mail
PowerShell 一把梭:
Get-ADUser -Filter * -Properties mail,UserPrincipalName |
Where-Object { -not $_.mail -and $_.UserPrincipalName } |
ForEach-Object {
Set-ADUser $_ -Replace @{mail = $_.UserPrincipalName}
}
到这里为止:
- 系统邮箱通知修好了
- AD 里看起来也一切正常了
但没想到,下一波事故开始了。
三、Gerrit Web 登录突然炸了
没多久,同事反馈:
Gerrit Web 登录失败
看 Gerrit 日志,熟悉又刺眼的一段:
Email 'user@example.com' in use by another account
Email xxxxx already assigned to account 1000316
cannot create external ID gerrit:user with the same email ...
一开始的直觉判断是:
- 是不是邮箱配错用户了?
经过一系列排查后,发现不对。
四、为什么偏偏是 Gerrit 出问题?
1️⃣ Exchange 废弃后,我对 Gerrit 做过“人工干预”
在 Exchange 废弃之后,我发现一个很奇怪的现象:
Gerrit 里,很多新建的 LDAP 用户登录 是“没有邮箱”的
为了不影响使用,我之前一直是手动给用户补邮箱的:
ssh -p 29418 gerrit@服务器 \
gerrit set-account 账号 \
--add-email 邮箱地址 \
--preferred-email 邮箱地址
这样做,当时是完全正常的。
2️⃣ 现在为什么冲突了?
关键变化只有一个:
现在 AD 里,新用户突然都有
而 Gerrit 的 LDAP 登录逻辑是这样的(这是重点):
当用户通过 LDAP 登录时,Gerrit 会:
- 认证用户名 / 密码(这一步其实是成功的)
- 从 LDAP 读取用户属性(包括
mail) - 尝试把
mail写成 Gerrit 的 email external ID - 如果这个邮箱已经存在 → 直接拒绝登录
也就是说:
Gerrit 不允许:
同一个邮箱,被“再绑定一次”
哪怕这个邮箱本来就是本人。
五、那个 1000316,到底是谁?
中间我还走了一点弯路。
我一开始以为:
“是不是把邮箱配到别的账号上了?”
但 Gerrit 版本太老(2.x),
根本没有命令能直接 id -> 用户名 查询。
最后我是用一个“反向验证”的办法:
-
把登录失败的那个用户
-
先执行:
ssh -p 29418 gerrit@服务器 gerrit set-account 用户 --delete-email 邮箱 -
再登录 Gerrit Web
-
一看账号 ID ——
就是日志里那个 1000316
而且更有意思的是:
- 他登录后 邮箱是存在的
- 只是没有被标记为
preferred email
这一下就彻底坐实了判断。
六、问题的本质(总结原理)
这不是 Gerrit 的 bug,而是设计如此。
真正的冲突点是:
-
之前:
- Gerrit 里有我手动添加的邮箱
- AD 里
mail是空的
-
现在:
- AD 里
mail有值了 - Gerrit 登录时尝试“再添加一次同样的邮箱”
- AD 里
于是 Gerrit 认为:
“这个邮箱已经属于某个 account 了,
我不能再给你建 external ID。”
七、最终处理方式(目前可接受的解法)
对这类用户,我现在的固定操作流程是:
-
登录失败
-
管理员执行:
ssh -p 29418 gerrit@服务器 gerrit set-account 用户 --delete-email 邮箱 -
用户重新 LDAP 登录
-
再执行:
ssh -p 29418 gerrit@服务器 gerrit set-account 用户 --preferred-email 邮箱
只要做 一次,后面就正常了。
好在:
- 这类用户只存在于
Exchange 废弃 → AD mail 补齐 → Gerrit 曾被手动加邮箱
这个时间窗口 - 数量是越来越少的
八、现在还遗留两个问题
1️⃣ 新用户能不能自动有 mail 属性?
理想情况是:
- AD 新用户创建时
- 自动把
UserPrincipalName同步到mail
这样所有系统都省心。
这个可以通过:
- 创建流程规范
- 或 AD 自动化脚本
- 或 IDM / 同步工具解决
2️⃣ Gerrit 还要不要人工处理?
短期内:
- 仍然需要对“历史账号”人工介入一次
长期来看:
- 只要 AD 的
mail属性从一开始就是稳定、唯一的 - Gerrit 的 LDAP 登录是不会出问题的
最后
这次问题本质上不是某一个系统“出 bug”,而是:
Exchange 撤销之后,AD 某些原本被自动维护的属性,
悄悄变成了“没人管”,
而系统却还在继续依赖它。
更多推荐



所有评论(0)