公司内部不少系统都有邮箱通知功能。最近业务部门反馈:
部分同事收不到系统通知邮件了,收件人没他。

第一反应当然是到 Windows AD 域控上面查看用户属性,但很快就发现一个共性:

❗ 这些用户在 Windows AD 里的 mail 属性是空的

这一下怀疑是不是因为 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 里的 mail 属性,本质上是 Exchange 帮你维护的

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 里,新用户突然都有 mail 属性了(原本是空值)

而 Gerrit 的 LDAP 登录逻辑是这样的(这是重点):

当用户通过 LDAP 登录时,Gerrit 会:

  1. 认证用户名 / 密码(这一步其实是成功的)
  2. 从 LDAP 读取用户属性(包括 mail
  3. 尝试把 mail 写成 Gerrit 的 email external ID
  4. 如果这个邮箱已经存在 → 直接拒绝登录

也就是说:

Gerrit 不允许:
同一个邮箱,被“再绑定一次”

哪怕这个邮箱本来就是本人。


五、那个 1000316,到底是谁?

中间我还走了一点弯路。

我一开始以为:

“是不是把邮箱配到别的账号上了?”

但 Gerrit 版本太老(2.x),
根本没有命令能直接 id -> 用户名 查询

最后我是用一个“反向验证”的办法:

  1. 登录失败的那个用户

  2. 先执行:

    ssh -p 29418 gerrit@服务器 gerrit set-account 用户 --delete-email 邮箱
    
  3. 再登录 Gerrit Web

  4. 一看账号 ID ——
    就是日志里那个 1000316

而且更有意思的是:

  • 他登录后 邮箱是存在的
  • 只是没有被标记为 preferred email

这一下就彻底坐实了判断。


六、问题的本质(总结原理)

这不是 Gerrit 的 bug,而是设计如此。

真正的冲突点是:

  • 之前:

    • Gerrit 里有我手动添加的邮箱
    • AD 里 mail 是空的
  • 现在:

    • AD 里 mail 有值了
    • Gerrit 登录时尝试“再添加一次同样的邮箱”

于是 Gerrit 认为:

“这个邮箱已经属于某个 account 了,
我不能再给你建 external ID。”


七、最终处理方式(目前可接受的解法)

对这类用户,我现在的固定操作流程是:

  1. 登录失败

  2. 管理员执行:

    ssh -p 29418 gerrit@服务器 gerrit set-account 用户 --delete-email 邮箱
    
  3. 用户重新 LDAP 登录

  4. 再执行:

    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 某些原本被自动维护的属性,
悄悄变成了“没人管”,
而系统却还在继续依赖它。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐