Windows AD PowerShell(七):DNS、SPN 与 Kerberos 排障

3648 字
18 分钟
Windows AD PowerShell(七):DNS、SPN 与 Kerberos 排障

这是 Windows AD PowerShell 学习记录的第七节,主要整理 AD 的 SRV 记录、SPN 注册、Kerberos 票据和服务账户认证故障。

本节通过 LDAP 只读核对了实验环境中的 DNS 与 SPN:_ldap、_kerberos 和 _gc SRV 记录都指向两台 DC;全域共有 86 个唯一 SPN,当前没有发现跨对象重复 SPN;示例账户 svc_testweb 和 HTTP/testweb.qy.net 尚不存在。添加和删除 SPN 的命令只作教学展示,没有在 VM 上执行。

七、DNS、SPN 与 Kerberos 排障#

7.1 DNS 与 SPN 分别解决什么问题#

服务认证大致经过以下链路:

DNS / SRV
↓ 找到域控、KDC 或目标主机
SPN
↓ 把“服务名称”映射到运行服务的 AD 账户
KDC
↓ 为该 SPN 签发服务票据
客户端票据缓存
↓ 把 Kerberos 票据交给服务
服务账户密钥
↓ 解密票据并完成认证

DNS 解决“服务器在哪里”,SPN 解决“这个服务属于哪个安全主体”。DNS 正常并不代表 SPN 正确;SPN 正确也不代表客户端一定使用 Kerberos。

一个典型 SPN:

HTTP/testweb.qy.net
│ └────────────── 服务实例名称
└─────────────────── 服务类别

常见格式:

<服务类别>/<主机名>[:端口][/附加服务名]

例如:

SPN常见用途
HOST/server.qy.netWindows 主机类服务
HTTP/web.qy.netIIS、HTTP.sys、Web 服务
MSSQLSvc/sql.qy.net:1433SQL Server
CIFS/fileserver.qy.netSMB 文件服务
LDAP/dc01.qy.netLDAP
WSMAN/server.qy.netWinRM

SPN 应注册在“实际运行服务的账户”上:

  • Local System、Network Service 等计算机身份运行:通常注册到计算机账户。
  • 域用户运行:注册到对应域用户。
  • gMSA 运行:注册到对应 gMSA。
  • 集群、负载均衡或别名访问:注册的名称必须与客户端实际访问名称匹配。

7.2 查询 AD 的 SRV 记录#

使用 nslookup:

Terminal window
nslookup -type=SRV _ldap._tcp.qy.net
_ldap._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 389
svr hostname = QYNET-CORE01.qy.net
_ldap._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 389
svr hostname = QYNET-CORE02.qy.net

查询 Kerberos KDC:

Terminal window
nslookup -type=SRV _kerberos._tcp.qy.net
_kerberos._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 88
svr hostname = QYNET-CORE01.qy.net
_kerberos._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 88
svr hostname = QYNET-CORE02.qy.net

查询全局编录:

Terminal window
nslookup -type=SRV _gc._tcp.qy.net
_gc._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 3268
svr hostname = QYNET-CORE01.qy.net
_gc._tcp.qy.net SRV service location:
priority = 0
weight = 100
port = 3268
svr hostname = QYNET-CORE02.qy.net

当前环境实际记录如下:

查询名称目标端口PriorityWeight
_ldap._tcp.qy.netQYNET-CORE01、QYNET-CORE023890100
_kerberos._tcp.qy.netQYNET-CORE01、QYNET-CORE02880100
_gc._tcp.qy.netQYNET-CORE01、QYNET-CORE0232680100

更严格的 DC 定位查询:

Terminal window
nslookup -type=SRV _ldap._tcp.dc._msdcs.qy.net

使用 PowerShell 可以获得结构化对象:

Terminal window
Resolve-DnsName -Type SRV "_ldap._tcp.qy.net" |
Where-Object Type -eq "SRV" |
Select-Object Name,NameTarget,Port,Priority,Weight
Name NameTarget Port Priority Weight
---- ---------- ---- -------- ------
_ldap._tcp.qy.net QYNET-CORE01.qy.net 389 0 100
_ldap._tcp.qy.net QYNET-CORE02.qy.net 389 0 100

Priority 越小越优先;Priority 相同的记录会结合 Weight 选择。权重相同不等于客户端严格轮流使用,DC Locator 还会考虑站点、缓存和响应情况。

7.3 按站点定位域控制器#

客户端通常应优先寻找本 AD Site 的 DC:

Terminal window
nltest /dsgetsite
QYNET-CORE
nltest /dsgetdc:qy.net /force
DC: \\QYNET-CORE01.qy.net
Address: \\192.168.254.10
Dom Guid: ca2f1ead-decd-4f2b-9f8a-aab329a6c090
Dom Name: qy.net
Forest Name: qy.net
Dc Site Name: QYNET-CORE
Our Site Name: QYNET-CORE
Flags: GC DS LDAP KDC TIMESERV WRITABLE DNS_DC DNS_DOMAIN DNS_FOREST
The command completed successfully

查询站点专用 SRV:

Terminal window
Resolve-DnsName -Type SRV "_ldap._tcp.QYNET-CORE._sites.qy.net"
Resolve-DnsName -Type SRV "_kerberos._tcp.QYNET-CORE._sites.qy.net"

如果客户端总是联系远端 DC,应检查:

  1. 客户端 IP 是否属于正确的 AD Subnet。
  2. Subnet 是否关联正确 Site。
  3. 站点专用 SRV 是否存在。
  4. 客户端是否使用 AD DNS。
  5. DC Locator 缓存是否需要刷新。
Terminal window
nltest /dsgetsite
nltest /dclist:qy.net
klist query_bind

7.4 使用 DCDiag 检查 DNS#

本篇不重复展开 DCDiag 的所有参数,只保留 DNS 入口:

Terminal window
dcdiag /s:QYNET-CORE01 /test:DNS /DnsBasic /v
dcdiag /e /test:DNS /DnsRecordRegistration /v

DCDiag 的运行范围、DNS 子测试和检查顺序已在第六篇:DCDiag 域控健康检查说明。

如果 SRV 缺失,先检查 Netlogon 和 DNS:

Terminal window
Get-Service Netlogon,DNS |
Select-Object Name,Status,StartType
Get-DnsClientServerAddress -AddressFamily IPv4
Resolve-DnsName -Type SRV "_ldap._tcp.dc._msdcs.qy.net"

确认确实需要重新注册后再执行:

Terminal window
ipconfig /registerdns
nltest /dsregdns
Restart-Service Netlogon

这些命令会改变 DNS 注册或服务状态,不应在未确认根因时反复执行。

7.5 查询账户已经注册的 SPN#

使用 setspn -L 可以列出指定账户上的全部 SPN:

Terminal window
# 查询计算机账户上的 SPN
setspn -L QYNET-CORE01
Registered ServicePrincipalNames for CN=QYNET-CORE01,OU=Domain Controllers,DC=qy,DC=net:
TERMSRV/QYNET-CORE01
TERMSRV/QYNET-CORE01.qy.net
WSMAN/QYNET-CORE01
WSMAN/QYNET-CORE01.qy.net
RestrictedKrbHost/QYNET-CORE01
RestrictedKrbHost/QYNET-CORE01.qy.net
HOST/QYNET-CORE01
HOST/QYNET-CORE01.qy.net
GC/QYNET-CORE01.qy.net/qy.net
ldap/QYNET-CORE01
ldap/QYNET-CORE01.qy.net
DNS/QYNET-CORE01.qy.net

上面只节选了便于理解的项目。域控制器还会拥有基于 GUID、DomainDnsZones、ForestDnsZones 和 DFSR 的 SPN,不应因为名称看起来陌生就随意删除。

域用户运行服务时,应查询对应用户:

Terminal window
setspn -L QY\svc_testweb

当前实验环境中并不存在 svc_testweb,因此真正注册前应先确认服务究竟以哪个身份运行:

Terminal window
Get-ADUser -Identity svc_testweb
Get-ADServiceAccount -Identity svc_testweb
Get-CimInstance Win32_Service -ComputerName TESTWEB |
Where-Object Name -eq "W3SVC" |
Select-Object Name,StartName,State

StartName 才是判断服务运行身份的重要依据。把 SPN 注册到管理员账户、部署人员账户或错误的计算机账户上,即使名称正确,也可能导致 Kerberos 失败。

7.6 反向查询 SPN 与检查重复项#

已知 SPN 时,使用 -Q 反查它属于哪个账户:

Terminal window
setspn -Q HTTP/testweb.qy.net
Checking domain DC=qy,DC=net
No such SPN found.

当前实验环境中没有这个 SPN。多域林中应增加 -F,从整片林查询:

Terminal window
setspn -F -Q HTTP/testweb.qy.net
Checking forest DC=qy,DC=net
No such SPN found.

检查全林是否存在重复 SPN:

Terminal window
setspn -X -F
Processing entry 0
found 0 group of duplicate SPNs.

LDAP 只读核对结果也未发现重复项:当前有 7 个对象持有 SPN,共计 86 个唯一 SPN。

也可以使用 ActiveDirectory 模块生成重复项报告:

Terminal window
Get-ADObject `
-LDAPFilter "(servicePrincipalName=*)" `
-Properties ServicePrincipalName |
ForEach-Object {
foreach ($spn in $_.ServicePrincipalName) {
[pscustomobject]@{
SPN = $spn
DistinguishedName = $_.DistinguishedName
}
}
} |
Group-Object SPN |
Where-Object Count -gt 1 |
ForEach-Object Group
# 没有重复 SPN 时无输出

setspn -X -F 适合快速检查整片林;PowerShell 版本便于导出对象 DN、制作报告和进一步筛选。

重复 SPN 意味着同一个服务名称同时属于多个安全主体,KDC 无法可靠确定应该使用哪个账户的密钥。不要通过“随便删除其中一个”来碰运气,应先确认服务实际运行身份、客户端访问名称和旧对象是否仍在使用。

7.7 安全添加与删除 SPN#

假设 Web 服务以域账户 QY\svc_testweb 运行,客户端通过 https://testweb.qy.net 访问。注册前依次确认账户、名称和现有所有者:

Terminal window
Get-ADUser -Identity svc_testweb
Resolve-DnsName testweb.qy.net
setspn -F -Q HTTP/testweb.qy.net

确认账户已存在、DNS 指向正确,而且该 SPN 尚未被占用后,使用 -S 添加:

Terminal window
setspn -U -S HTTP/testweb.qy.net QY\svc_testweb
Checking domain DC=qy,DC=net
Registering ServicePrincipalNames for CN=svc_testweb,OU=Service Accounts,DC=qy,DC=net
HTTP/testweb.qy.net
Updated object

这段输出用于展示成功格式;当前实验环境没有该账户,因此没有实际执行。

参数含义:

参数含义
-S检查重复后再添加 SPN,发现冲突就拒绝写入
-A直接添加,不做重复检查,不建议使用
-U将目标按用户账户处理
-C将目标按计算机账户处理
-Q查询某个 SPN 的所有者
-L列出某个账户拥有的 SPN
-D从指定账户删除 SPN
-F在整片林中执行查询或查重
-X查找重复 SPN

添加后应从正反两个方向验证:

Terminal window
setspn -L QY\svc_testweb
setspn -F -Q HTTP/testweb.qy.net

如果确认注册错误,从明确的账户删除:

Terminal window
setspn -D HTTP/testweb.qy.net QY\svc_testweb
Unregistering ServicePrincipalNames for CN=svc_testweb,OU=Service Accounts,DC=qy,DC=net
HTTP/testweb.qy.net
Updated object

删除后再次执行 setspn -F -Q 验证。删除 SPN 会影响正在使用它的 Kerberos 客户端,不能只凭重复检查结果直接操作生产环境。

始终优先使用 setspn -S,不要使用 setspn -A。 -S 会先检查重复项,能在写入前阻止最常见的人为故障。

7.8 检查客户端 Kerberos 票据#

DNS 和 SPN 都正确后,在实际发起访问的客户端检查票据:

Terminal window
# 查看当前登录会话中的 Kerberos 票据
klist
Current LogonId is 0:0x4a9f1
Cached Tickets: (2)
#0> Client: qianyue @ QY.NET
Server: krbtgt/QY.NET @ QY.NET
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
#1> Client: qianyue @ QY.NET
Server: HTTP/testweb.qy.net @ QY.NET
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96

只看票据授予票据(TGT):

Terminal window
klist tgt

主动请求某个服务票据:

Terminal window
klist get HTTP/testweb.qy.net
Current LogonId is 0:0x4a9f1
A ticket to HTTP/testweb.qy.net has been retrieved successfully.

如果 klist get 失败,错误会直接指向 KDC、SPN、账户或加密类型问题。若请求成功但访问仍失败,应继续检查服务身份、服务端解密票据的密钥以及应用自身配置。

查看 DC Locator 当前绑定:

Terminal window
klist query_bind
Current LogonId is 0:0x4a9f1
QY.NET : QYNET-CORE01.qy.net

清空当前登录会话票据后重新测试:

Terminal window
klist purge
Current LogonId is 0:0x4a9f1
Deleting all tickets:
Ticket(s) purged!

klist purge 会清除当前登录会话中的 Kerberos 票据,可能使已打开的网络资源暂时失效;它只适合在修复配置后验证缓存影响,并不是修复 SPN 的办法。

客户端应使用与 SPN 匹配的名称访问服务。例如 SPN 是 HTTP/testweb.qy.net,却使用 IP 地址或另一个别名访问时,通常不会请求到这个 SPN 的票据,并可能回退到 NTLM。

7.9 从事件日志定位 Kerberos 错误#

在域控制器的安全日志中,常见事件如下:

事件 ID含义排查重点
4768请求 Kerberos TGT用户身份、预身份验证、KDC 与加密类型
4769请求 Kerberos 服务票据 TGS目标 SPN、服务账户、票据加密类型与失败码
4771Kerberos 预身份验证失败密码错误、时间偏差、证书或预身份验证问题
4776DC 验证 NTLM 凭据判断应用是否没有使用 Kerberos 而回退到 NTLM

查询最近两小时的相关事件:

Terminal window
$startTime = (Get-Date).AddHours(-2)
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4768,4769,4771,4776
StartTime = $startTime
} |
Select-Object TimeCreated,Id,MachineName,Message

安全日志通常需要管理员权限,而且只有启用了相应审核策略才会产生所需事件。输出过长时先筛选目标账户或服务名:

Terminal window
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4769
StartTime = $startTime
} |
Where-Object Message -Match "HTTP/testweb.qy.net|svc_testweb" |
Select-Object TimeCreated,Id,Message

常见 Kerberos 状态码:

状态码名称常见含义
0x6KDC_ERR_C_PRINCIPAL_UNKNOWNKDC 找不到客户端主体
0x7KDC_ERR_S_PRINCIPAL_UNKNOWN找不到目标服务主体,常见于 SPN 缺失
0x8KDC_ERR_PRINCIPAL_NOT_UNIQUE服务主体不唯一,优先检查重复 SPN
0x18KDC_ERR_PREAUTH_FAILED预身份验证失败,常见于密码或密钥不匹配
0x25KRB_AP_ERR_SKEW客户端、服务端与 DC 的时间偏差过大
0x29KRB_AP_ERR_MODIFIED服务无法用当前身份的密钥解密票据,常见于 SPN 绑错账户或密码不一致

状态码必须结合事件 ID、目标 SPN、客户端地址和服务运行身份一起判断,不能只看一行错误文本。

7.10 Kerberos 服务认证失败的排查顺序#

下面这条链路能覆盖大部分“服务账户认证失败”的工单。

第一步:确认客户端解析到正确地址#

Terminal window
Resolve-DnsName testweb.qy.net
Test-NetConnection testweb.qy.net -Port 443

如果访问名称解析错误,后续 SPN 检查即使正确也没有意义。

第二步:确认 KDC 的 SRV 记录#

Terminal window
Resolve-DnsName -Type SRV "_kerberos._tcp.qy.net"
nltest /dsgetdc:qy.net /KDC /force

第三步:确认客户端实际请求的 SPN#

浏览器访问 https://testweb.qy.net 时,通常对应 HTTP/testweb.qy.net。端口、别名、代理、负载均衡入口和应用协议都可能改变实际需要的 SPN,不能只根据服务器计算机名猜测。

Terminal window
klist get HTTP/testweb.qy.net

第四步:确认 SPN 位于正确账户且没有重复#

Terminal window
setspn -F -Q HTTP/testweb.qy.net
setspn -L QY\svc_testweb
setspn -X -F

第五步:确认服务实际运行身份#

Terminal window
Get-CimInstance Win32_Service -ComputerName TESTWEB |
Select-Object Name,StartName,State

IIS 应同时检查应用程序池身份;SQL Server 应检查实例服务账户;计划任务和 Windows 服务也可能使用不同账户。

第六步:检查票据与认证协议#

Terminal window
klist
klist get HTTP/testweb.qy.net

如果客户端访问成功但缓存里始终没有目标服务票据,应怀疑应用正在使用 NTLM、匿名认证或其他认证方式。

第七步:检查 KDC 与服务端日志#

重点查看 DC 的 4768、4769、4771,以及是否出现 4776 NTLM 验证;服务端再结合 System、Security 和应用自身日志确认。

7.11 三个典型故障案例#

案例一:网页能打开,但实际使用的是 NTLM#

现象:域用户访问 Web 站点成功,但无法使用 Kerberos 委派,klist 中没有对应的 HTTP 票据。

检查:

Terminal window
setspn -F -Q HTTP/testweb.qy.net
klist get HTTP/testweb.qy.net

常见原因包括 SPN 未注册、用户使用 IP 地址访问、访问了未注册的别名、浏览器未把站点视为允许集成认证的区域,或应用根本没有启用 Windows 身份验证。

案例二:注册 SPN 时提示重复#

现象:setspn -S 拒绝写入,或 KDC 返回主体不唯一。

Terminal window
setspn -F -Q HTTP/testweb.qy.net
setspn -X -F

先确认查询到的每个账户是否仍承载服务,再从错误账户删除。若直接删除当前生产服务使用的 SPN,原本正常的 Kerberos 会立即失效。

案例三:出现 KRB_AP_ERR_MODIFIED#

现象:客户端能取得服务票据,但服务无法接受票据。

排查重点:

  1. SPN 是否注册到了错误账户。
  2. 服务当前运行身份是否刚刚更换。
  3. 服务账户密码是否在部分节点上没有同步更新。
  4. 集群或负载均衡各节点是否使用相同的服务身份。
  5. 是否存在同名旧计算机对象或错误 DNS 指向。
Terminal window
Resolve-DnsName testweb.qy.net
setspn -F -Q HTTP/testweb.qy.net
Get-CimInstance Win32_Service -ComputerName TESTWEB |
Select-Object Name,StartName,State

KRB_AP_ERR_MODIFIED 不等于“票据被人篡改”。在日常运维中,它更常表示收到票据的服务没有对应账户密钥,通常要沿着“DNS 指向—SPN 所有者—服务身份”排查。

7.12 推荐检查清单#

  1. 客户端只使用 AD DNS,并能正确解析服务名称。
  2. _ldap、_kerberos、_gc 以及站点专用 SRV 记录正常。
  3. 客户端定位到正确 Site 和可用 DC/KDC。
  4. 明确客户端实际访问的 FQDN、别名、端口和服务类别。
  5. 使用 setspn -F -Q 确认 SPN 的唯一所有者。
  6. 使用 setspn -L 确认目标账户上的完整 SPN。
  7. 核对服务实际运行身份,不要只看配置文档。
  8. 使用 klist get 验证 KDC 能否签发目标服务票据。
  9. 检查 4768、4769、4771;必要时用 4776 判断是否回退到 NTLM。
  10. 修改后重新获取票据并复测,不把清缓存当成配置修复。

先确认名称解析,再确认 SPN 唯一且属于正确账户,最后检查票据和服务密钥。DNS、SPN、票据三者必须同时正确,Kerberos 才能稳定工作。

参考资料#


上一篇:Windows AD PowerShell(六):DCDiag 域控健康检查

下一篇:Windows AD PowerShell(八):Kerberos 与票据

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Windows AD PowerShell(七):DNS、SPN 与 Kerberos 排障
https://springtsuki.xucjno.com/posts/springtsuki/notes/windowsad/powershell/921-dns/
作者
SpringTsuki
发布于
2026-09-21
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
SpringTsuki
你好,我是浅月。
公告
浅月的开发小窝!欢迎你来到这里!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
56
分类
9
标签
42
总字数
188,888
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录