你的连接是通的,登录也被接受,但所有人都以为会用 Kerberos,SQL Server 却在用 NTLM。这个结果不是 VPN 的缺陷,因为隧道只提供路径;决定方案的是客户端请求的名称、目录中注册的主体,以及客户端实际能拿到的票据。三个条件必须同时成立,而会话本身会告诉你哪一个退让了。在任何人修改服务主体名称之前,先读这段。

会话会指出你实际得到的方案

身份验证方案属于连接,而不属于服务器或隧道,所以检查必须在你在意的那个会话里执行。用产生报告的那个客户端、工具和账户执行下面的查询,并把目标名称和传输方式记在结果旁边。

SELECT session_id, auth_scheme, net_transport, client_net_address, login_name
FROM sys.dm_exec_connections
WHERE session_id = @@SPID;

在数据库主机上本地 SSMS 窗口里查到的一行,描述的是完全不同的连接,因此查询必须从客户端一侧执行。保存输出时带上时间戳,因为下一个读到它的人会问它是什么时候取的。当多个应用程序共用一个端点时,对每一个都取样,因为两个应用程序之间的数据源差异往往就是全部故事。

条件一:名称与服务主体名称是否一致?

Kerberos 认证的是具名服务,所以客户端会向目录请求某一个特定主体的票据。对 TCP 提供程序来说,有文档记载的形式是 MSSQLSvc/,后面跟完全限定的主机名,默认实例还要跟端口。命名实例注册的是 MSSQLSvc/db01.corp.example:SQLPROD,其中后缀是实例名而不是数字。通过别名、在别处解析的 CNAME 或裸 IP 地址访问侦听器,都会改变所请求的名称;而找不到匹配主体的请求没有票据可以返回。

这就是为什么同一个应用在一个网络上能工作,在另一个网络上却回退:DNS 应答、别名记录或 hosts 条目改变了客户端使用的名称。在改动注册之前,先把你请求的名称与目录里实际保存的内容做比较。

条件二:SPN 是否在正确的账户上?

SPN 必须属于运行 SQL 服务的账户,而不是客户端的账户,也不能意外地属于某个计算机对象。当服务账户拥有写入权限时,SQL Server 可以在启动时注册自己的 SPN,这很方便,也解释了服务账户变更之后为什么会出现重复项。

向目录查询你预期的名称,然后列出该账户实际拥有的内容。是哪个对象持有这条记录,比名称是否存在森林中某处更重要。不一致意味着登录从来都不是问题所在,而 Microsoft 的 SPN 指南记载了这些格式,以及域管理员必须创建该条目的情形。

setspn -Q MSSQLSvc/db01.corp.example:1433
setspn -L CORP\svc-sql01

再添加一条记录不是安全的实验。重复或冲突的注册可能弄坏原本正常的客户端,所以改动前要检查所有权,改动后要再核查一次。

条件三:客户端到底能不能拿到票据?

如果客户端无法到达域控制器,或者没有可用的凭据上下文,那么正确的 SPN 也没有用。交换需要经由 VPN 访问 KDC 和 DNS,而只路由 SQL 端口的隧道恰好会在这一步失败。可达性必须从同一台机器、同一个账户去测,而不是从它们前面的防火墙去测。

检查机器上的票据缓存,以及运行应用程序的那个账户下的缓存。从提升权限的窗口里看到的整机列表,说明不了以另一个用户身份运行的服务;而对那个用户来说,缓存为空是一个发现,而不是不方便。

klist
klist -li 0x3e7

然后决定回退允许做什么。如果策略要求 Kerberos,就必须明确拒绝 NTLM,因为静默回退会把配置错误变成一条违反要求的可用连接。如果某些应用仍然接受 NTLM,就点名列出它们,并且不要再把每一个 NTLM 会话都当成事件。

更换启动器只是一个变量,不是修复

用另一套凭据启动 SSMS,可能治好意外的出站身份,这确实是一类问题。但它并不能证明目标名称、注册或票据路径的任何事情,也不会改变服务器接受了什么。

让比较保持受控:同一个端点、同一个提供程序、已知的执行上下文,然后是一条你检查过方案的新连接。分别确认被接受的登录及其数据库权限,因为访问和身份验证失败的原因并不相同。

当要求 Kerberos 时,记录第一条路径为什么回退,施加一处修正,并从同一个客户端重新执行查询。更换启动器之后查询成功,是访问的证据,但还不是身份验证要求已满足的证据。

相关文章