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.net | Windows 主机类服务 |
HTTP/web.qy.net | IIS、HTTP.sys、Web 服务 |
MSSQLSvc/sql.qy.net:1433 | SQL Server |
CIFS/fileserver.qy.net | SMB 文件服务 |
LDAP/dc01.qy.net | LDAP |
WSMAN/server.qy.net | WinRM |
SPN 应注册在“实际运行服务的账户”上:
- Local System、Network Service 等计算机身份运行:通常注册到计算机账户。
- 域用户运行:注册到对应域用户。
- gMSA 运行:注册到对应 gMSA。
- 集群、负载均衡或别名访问:注册的名称必须与客户端实际访问名称匹配。
7.2 查询 AD 的 SRV 记录
使用 nslookup:
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:
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查询全局编录:
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当前环境实际记录如下:
| 查询名称 | 目标 | 端口 | Priority | Weight |
|---|---|---|---|---|
_ldap._tcp.qy.net | QYNET-CORE01、QYNET-CORE02 | 389 | 0 | 100 |
_kerberos._tcp.qy.net | QYNET-CORE01、QYNET-CORE02 | 88 | 0 | 100 |
_gc._tcp.qy.net | QYNET-CORE01、QYNET-CORE02 | 3268 | 0 | 100 |
更严格的 DC 定位查询:
nslookup -type=SRV _ldap._tcp.dc._msdcs.qy.net使用 PowerShell 可以获得结构化对象:
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 100Priority 越小越优先;Priority 相同的记录会结合 Weight 选择。权重相同不等于客户端严格轮流使用,DC Locator 还会考虑站点、缓存和响应情况。
7.3 按站点定位域控制器
客户端通常应优先寻找本 AD Site 的 DC:
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-COREOur Site Name: QYNET-CORE Flags: GC DS LDAP KDC TIMESERV WRITABLE DNS_DC DNS_DOMAIN DNS_FORESTThe command completed successfully查询站点专用 SRV:
Resolve-DnsName -Type SRV "_ldap._tcp.QYNET-CORE._sites.qy.net"
Resolve-DnsName -Type SRV "_kerberos._tcp.QYNET-CORE._sites.qy.net"如果客户端总是联系远端 DC,应检查:
- 客户端 IP 是否属于正确的 AD Subnet。
- Subnet 是否关联正确 Site。
- 站点专用 SRV 是否存在。
- 客户端是否使用 AD DNS。
- DC Locator 缓存是否需要刷新。
nltest /dsgetsitenltest /dclist:qy.netklist query_bind7.4 使用 DCDiag 检查 DNS
本篇不重复展开 DCDiag 的所有参数,只保留 DNS 入口:
dcdiag /s:QYNET-CORE01 /test:DNS /DnsBasic /v
dcdiag /e /test:DNS /DnsRecordRegistration /vDCDiag 的运行范围、DNS 子测试和检查顺序已在第六篇:DCDiag 域控健康检查说明。
如果 SRV 缺失,先检查 Netlogon 和 DNS:
Get-Service Netlogon,DNS | Select-Object Name,Status,StartType
Get-DnsClientServerAddress -AddressFamily IPv4
Resolve-DnsName -Type SRV "_ldap._tcp.dc._msdcs.qy.net"确认确实需要重新注册后再执行:
ipconfig /registerdnsnltest /dsregdnsRestart-Service Netlogon这些命令会改变 DNS 注册或服务状态,不应在未确认根因时反复执行。
7.5 查询账户已经注册的 SPN
使用 setspn -L 可以列出指定账户上的全部 SPN:
# 查询计算机账户上的 SPNsetspn -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,不应因为名称看起来陌生就随意删除。
域用户运行服务时,应查询对应用户:
setspn -L QY\svc_testweb当前实验环境中并不存在 svc_testweb,因此真正注册前应先确认服务究竟以哪个身份运行:
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,StateStartName 才是判断服务运行身份的重要依据。把 SPN 注册到管理员账户、部署人员账户或错误的计算机账户上,即使名称正确,也可能导致 Kerberos 失败。
7.6 反向查询 SPN 与检查重复项
已知 SPN 时,使用 -Q 反查它属于哪个账户:
setspn -Q HTTP/testweb.qy.net
Checking domain DC=qy,DC=netNo such SPN found.当前实验环境中没有这个 SPN。多域林中应增加 -F,从整片林查询:
setspn -F -Q HTTP/testweb.qy.net
Checking forest DC=qy,DC=netNo such SPN found.检查全林是否存在重复 SPN:
setspn -X -F
Processing entry 0found 0 group of duplicate SPNs.LDAP 只读核对结果也未发现重复项:当前有 7 个对象持有 SPN,共计 86 个唯一 SPN。
也可以使用 ActiveDirectory 模块生成重复项报告:
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 访问。注册前依次确认账户、名称和现有所有者:
Get-ADUser -Identity svc_testweb
Resolve-DnsName testweb.qy.net
setspn -F -Q HTTP/testweb.qy.net确认账户已存在、DNS 指向正确,而且该 SPN 尚未被占用后,使用 -S 添加:
setspn -U -S HTTP/testweb.qy.net QY\svc_testweb
Checking domain DC=qy,DC=netRegistering ServicePrincipalNames for CN=svc_testweb,OU=Service Accounts,DC=qy,DC=net HTTP/testweb.qy.netUpdated object这段输出用于展示成功格式;当前实验环境没有该账户,因此没有实际执行。
参数含义:
| 参数 | 含义 |
|---|---|
-S | 检查重复后再添加 SPN,发现冲突就拒绝写入 |
-A | 直接添加,不做重复检查,不建议使用 |
-U | 将目标按用户账户处理 |
-C | 将目标按计算机账户处理 |
-Q | 查询某个 SPN 的所有者 |
-L | 列出某个账户拥有的 SPN |
-D | 从指定账户删除 SPN |
-F | 在整片林中执行查询或查重 |
-X | 查找重复 SPN |
添加后应从正反两个方向验证:
setspn -L QY\svc_testweb
setspn -F -Q HTTP/testweb.qy.net如果确认注册错误,从明确的账户删除:
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.netUpdated object删除后再次执行 setspn -F -Q 验证。删除 SPN 会影响正在使用它的 Kerberos 客户端,不能只凭重复检查结果直接操作生产环境。
始终优先使用
setspn -S,不要使用setspn -A。-S会先检查重复项,能在写入前阻止最常见的人为故障。
7.8 检查客户端 Kerberos 票据
DNS 和 SPN 都正确后,在实际发起访问的客户端检查票据:
# 查看当前登录会话中的 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):
klist tgt主动请求某个服务票据:
klist get HTTP/testweb.qy.net
Current LogonId is 0:0x4a9f1A ticket to HTTP/testweb.qy.net has been retrieved successfully.如果 klist get 失败,错误会直接指向 KDC、SPN、账户或加密类型问题。若请求成功但访问仍失败,应继续检查服务身份、服务端解密票据的密钥以及应用自身配置。
查看 DC Locator 当前绑定:
klist query_bind
Current LogonId is 0:0x4a9f1QY.NET : QYNET-CORE01.qy.net清空当前登录会话票据后重新测试:
klist purge
Current LogonId is 0:0x4a9f1Deleting 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、服务账户、票据加密类型与失败码 |
| 4771 | Kerberos 预身份验证失败 | 密码错误、时间偏差、证书或预身份验证问题 |
| 4776 | DC 验证 NTLM 凭据 | 判断应用是否没有使用 Kerberos 而回退到 NTLM |
查询最近两小时的相关事件:
$startTime = (Get-Date).AddHours(-2)
Get-WinEvent -FilterHashtable @{ LogName = "Security" Id = 4768,4769,4771,4776 StartTime = $startTime} | Select-Object TimeCreated,Id,MachineName,Message安全日志通常需要管理员权限,而且只有启用了相应审核策略才会产生所需事件。输出过长时先筛选目标账户或服务名:
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 状态码:
| 状态码 | 名称 | 常见含义 |
|---|---|---|
0x6 | KDC_ERR_C_PRINCIPAL_UNKNOWN | KDC 找不到客户端主体 |
0x7 | KDC_ERR_S_PRINCIPAL_UNKNOWN | 找不到目标服务主体,常见于 SPN 缺失 |
0x8 | KDC_ERR_PRINCIPAL_NOT_UNIQUE | 服务主体不唯一,优先检查重复 SPN |
0x18 | KDC_ERR_PREAUTH_FAILED | 预身份验证失败,常见于密码或密钥不匹配 |
0x25 | KRB_AP_ERR_SKEW | 客户端、服务端与 DC 的时间偏差过大 |
0x29 | KRB_AP_ERR_MODIFIED | 服务无法用当前身份的密钥解密票据,常见于 SPN 绑错账户或密码不一致 |
状态码必须结合事件 ID、目标 SPN、客户端地址和服务运行身份一起判断,不能只看一行错误文本。
7.10 Kerberos 服务认证失败的排查顺序
下面这条链路能覆盖大部分“服务账户认证失败”的工单。
第一步:确认客户端解析到正确地址
Resolve-DnsName testweb.qy.net
Test-NetConnection testweb.qy.net -Port 443如果访问名称解析错误,后续 SPN 检查即使正确也没有意义。
第二步:确认 KDC 的 SRV 记录
Resolve-DnsName -Type SRV "_kerberos._tcp.qy.net"
nltest /dsgetdc:qy.net /KDC /force第三步:确认客户端实际请求的 SPN
浏览器访问 https://testweb.qy.net 时,通常对应 HTTP/testweb.qy.net。端口、别名、代理、负载均衡入口和应用协议都可能改变实际需要的 SPN,不能只根据服务器计算机名猜测。
klist get HTTP/testweb.qy.net第四步:确认 SPN 位于正确账户且没有重复
setspn -F -Q HTTP/testweb.qy.net
setspn -L QY\svc_testweb
setspn -X -F第五步:确认服务实际运行身份
Get-CimInstance Win32_Service -ComputerName TESTWEB | Select-Object Name,StartName,StateIIS 应同时检查应用程序池身份;SQL Server 应检查实例服务账户;计划任务和 Windows 服务也可能使用不同账户。
第六步:检查票据与认证协议
klist
klist get HTTP/testweb.qy.net如果客户端访问成功但缓存里始终没有目标服务票据,应怀疑应用正在使用 NTLM、匿名认证或其他认证方式。
第七步:检查 KDC 与服务端日志
重点查看 DC 的 4768、4769、4771,以及是否出现 4776 NTLM 验证;服务端再结合 System、Security 和应用自身日志确认。
7.11 三个典型故障案例
案例一:网页能打开,但实际使用的是 NTLM
现象:域用户访问 Web 站点成功,但无法使用 Kerberos 委派,klist 中没有对应的 HTTP 票据。
检查:
setspn -F -Q HTTP/testweb.qy.net
klist get HTTP/testweb.qy.net常见原因包括 SPN 未注册、用户使用 IP 地址访问、访问了未注册的别名、浏览器未把站点视为允许集成认证的区域,或应用根本没有启用 Windows 身份验证。
案例二:注册 SPN 时提示重复
现象:setspn -S 拒绝写入,或 KDC 返回主体不唯一。
setspn -F -Q HTTP/testweb.qy.net
setspn -X -F先确认查询到的每个账户是否仍承载服务,再从错误账户删除。若直接删除当前生产服务使用的 SPN,原本正常的 Kerberos 会立即失效。
案例三:出现 KRB_AP_ERR_MODIFIED
现象:客户端能取得服务票据,但服务无法接受票据。
排查重点:
- SPN 是否注册到了错误账户。
- 服务当前运行身份是否刚刚更换。
- 服务账户密码是否在部分节点上没有同步更新。
- 集群或负载均衡各节点是否使用相同的服务身份。
- 是否存在同名旧计算机对象或错误 DNS 指向。
Resolve-DnsName testweb.qy.net
setspn -F -Q HTTP/testweb.qy.net
Get-CimInstance Win32_Service -ComputerName TESTWEB | Select-Object Name,StartName,StateKRB_AP_ERR_MODIFIED 不等于“票据被人篡改”。在日常运维中,它更常表示收到票据的服务没有对应账户密钥,通常要沿着“DNS 指向—SPN 所有者—服务身份”排查。
7.12 推荐检查清单
- 客户端只使用 AD DNS,并能正确解析服务名称。
_ldap、_kerberos、_gc以及站点专用 SRV 记录正常。- 客户端定位到正确 Site 和可用 DC/KDC。
- 明确客户端实际访问的 FQDN、别名、端口和服务类别。
- 使用
setspn -F -Q确认 SPN 的唯一所有者。 - 使用
setspn -L确认目标账户上的完整 SPN。 - 核对服务实际运行身份,不要只看配置文档。
- 使用
klist get验证 KDC 能否签发目标服务票据。 - 检查 4768、4769、4771;必要时用 4776 判断是否回退到 NTLM。
- 修改后重新获取票据并复测,不把清缓存当成配置修复。
先确认名称解析,再确认 SPN 唯一且属于正确账户,最后检查票据和服务密钥。DNS、SPN、票据三者必须同时正确,Kerberos 才能稳定工作。
参考资料
- Setspn 命令参考
- 如何配置服务主体名称(SPN)
- 验证 Active Directory 的 SRV DNS 记录
- Klist 命令参考
- 第六篇:DCDiag 域控健康检查
- 第五篇:复制健康与 Repadmin
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!













