使用具有受限双跳和模拟的 SSO 的 Active Directory 中的 Kerberos 无法更新服务票证

Kerberos in Active Directory using SSO with constrained double-hop and impersonation fails to renew service ticket

深入研究已在
中讨论的 Kerberos 约束委派

我有一个在多个服务器上运行的分布式应用程序,并通过 Kerberos 令牌依赖 SSO 进行用户身份验证,并且一切都按照上述线程中的讨论进行了布局(并且正在运行)。简而言之,

  1. 用户连接到服务器 运行ning 上的服务 1
  2. Server1 会将用户的作业分派给 Server2,即 运行ning 服务主体帐户下的另一项服务
    一种。 Server1 请求(并获得)相关 SPN
    的票证 b.服务器 1 将票据传输到服务器 2
  3. Server2 解析该票证(即 AcquireCredentialsHandleAcceptSecurityContext),然后调用 QuerySecurityContextToken ,接着是 DuplicateHandle, CreateProcess,将重复句柄传递给子进程,并 然后子进程调用 ImpersonateLoggedOnUser
  4. Server2 上的子进程现在正在 运行ning 模拟原始用户并且是 能够以该用户身份访问某些 Server3
  5. 上的网络资源

所以,一切正常……直到不再正常。步骤2a中获取的服务票据的生命周期最长为10小时(假设AD设置为服务票据默认10小时有效期)。票证生命周期有时远少于 10 小时的原因是因为步骤 1 和步骤 2 之间可能存在明显的延迟,例如,用户可能在提交作业进行处理之前做了一整天的准备工作。因此,服务票据结束时间将从原始用户登录时间起 10 小时,即与用户连接时生成的 TGT 的结束时间相匹配。

在服务票过期前大约 5 分钟,Server2 似乎尝试更新这张票,并且一张新票确实出现在 klist 中,用于 Server2 上子进程的 LUID .然而,这张新票据似乎“格式错误”,此时客户端进程失去了访问网络资源的能力,就像被冒充的用户一样。具体来说,虽然客户端进程仍然持有对 Server3 资源的有效 Kerberos 服务票证,但当从 Server2 向 Server3 伸出时,它从 Kerberos 身份验证下降到 NTLM 身份验证,此时它被 Server3 拒绝。

这是失败时 Server2 上 LUID 运行ning 子进程的 klist 输出示例;

Current LogonId is 0:0x5777e  
Targeted LogonId is 0:0x3966b5

Cached Tickets: (5)

#0>     Client: user @ VNET.COM
        Server: MSSQLSvc/sql.vnet.com:1433 @ VNET.COM
        KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
        Start Time: 2/20/2020 22:21:02 (local)
        End Time:   2/21/2020 7:07:45 (local)
        Renew Time: 2/27/2020 21:07:45 (local)
        Session Key Type: RSADSI RC4-HMAC(NT)
        Cache Flags: 0
        Kdc Called: DC.Vnet.com

#1>     Client: user @ VNET.COM
        Server: ldap/DC.Vnet.com/Vnet.com @ VNET.COM
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize
        Start Time: 2/20/2020 22:21:02 (local)
        End Time:   2/21/2020 7:07:45 (local)
        Renew Time: 2/27/2020 21:07:45 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0
        Kdc Called: DC.Vnet.com

#2>     Client: user @ VNET.COM
        Server: svc @
        KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
        Ticket Flags 0xa10000 -> renewable pre_authent name_canonicalize
        Start Time: 2/20/2020 22:23:37 (local)
        End Time:   2/21/2020 7:07:45 (local)
        Renew Time: 2/27/2020 21:07:45 (local)
        Session Key Type: RSADSI RC4-HMAC(NT)
        Cache Flags: 0x4 -> S4U
        Kdc Called: DC.Vnet.com

#3>     Client: user @ VNET.COM
        Server: svc @
        KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
        Ticket Flags 0xa10000 -> renewable pre_authent name_canonicalize
        Start Time: 2/20/2020 22:23:37 (local)
        End Time:   2/21/2020 7:07:45 (local)
        Renew Time: 2/27/2020 21:07:45 (local)
        Session Key Type: RSADSI RC4-HMAC(NT)
        Cache Flags: 0x4 -> S4U
        Kdc Called: DC.Vnet.com

#4>     Client: user @ VNET.COM
        Server: SVCD/APP.Vnet.com @ VNET.COM
        KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
        Start Time: 2/20/2020 22:11:39 (local)
        End Time:   2/20/2020 22:26:39 (local)
        Renew Time: 0
        Session Key Type: RSADSI RC4-HMAC(NT)
        Cache Flags: 0x8 -> ASC
        Kdc Called:

在上面的清单中,工单#4 是在步骤 2a 中请求的原始服务工单; SPN 是“SVCD/APP.Vnet.com”,与此 SPN 关联的域帐户(以及 运行 宁服务器 2 服务进程)是“ svc”。此票证用于在 Server2 上创建用户的安全上下文,子进程模拟该安全上下文。工单 #0 和 #1 是在子进程尝试访问 LDAP 和 MS SQL 服务器(用于子进程需要执行的工作)后模拟后生成的,因此这确认所有约束委派设置都很好并且子进程按预期 运行ning 长达 10 小时。在某些情况下,工单 #0 和 #1 需要更新 - 它们已成功更新,并且该过程继续进行。

现在,问题是:ticktes #2 和 #3 是在 #4 结束前几分钟生成的,但对我来说奇怪的是 ServerRealm 部分,即“svc @ …”为空白。此外,在生成这些票证的那一刻,Server2 子进程停止能够打开新的 MS SQL 到 Server3 的连接(并且在 Server3 上,匿名登录拒绝事件被记录为具有匹配的时间戳)。我还应该提到,虽然在上面的示例中有两张相同的“Server: svc @”票,但在某些情况下,我看到 7、14,有时甚至 30 多张相同的票,例如那。所有这些都是同时发出的,就好像 Server2 疯狂地尝试从 KDC 获得它需要的东西,但失败了。

我的解释是 Server2 想要更新模拟中使用的服务票证,但没有这样做。

此时我不确定问题是否出在;
a) AD 设置(即,具有受约束的委派详细信息),或
b) 在“svc”域帐户的权限中(它具有 SetImpersonatePrivilege 但可能需要其他东西),或
c) 在Server2中父进程和子进程之间传递用户安全上下文的方式,或者
d) 完全不同的东西

到目前为止,在阅读和研究了大量时间之后,我已经尝试了 c) 的各种序列排列(例如使用 DuplicateTokenEx + CreateProcessAsUser 相反,为重复的句柄和标记等翻转各种标志,但我不知道我可以和应该用 a) 和 b) 做什么(如果有的话)。我尝试更改一些设置,但没有效果,但我避免使用“核心”选项,例如在 svc 帐户上设置 SeTcbPrivilege,因为这是不可接受的我的解决方案。

最后一个观察是票 #4 缺少“Kdc Called”和“更新时间”,但我的印象是这是通过 InitializeSecurityContext.

获得的不透明票证的正常现象

我需要更改什么才能使 Server2 上的子进程 运行 超过原来的 10 小时?

我通过 Farzan Mirheydari 提供的评论解决了这个问题,这可以简化为在 AD 中为 svc[=36= 的约束委派设置下设置 "Use any authentication protocol" 的要求] 帐户。这将设置 TrustedToAuthForDelegation (T2A4D) 标志,允许 "protocol transition"。

Microsoft article 中隐含了为什么需要这样做的原因。尽管我的情况与那篇文章中描述的布局不同,但看起来(据我所知)Kerberos 票证由 AcceptSecurityContext 在 Server2 上生成并由 Cache Flags: 0x8 -> ASC 标识是 "token...just duplicated for the second service session access."

因此,当此 ASC 令牌过期时,模拟 usersvc 帐户无法更新它,因为它是重复的,缺少KDC/renewal 信息。相反,它为自己获取 S4U2Self ticket,如 Cache Flags: 0x4 -> S4U 所示,以便继续模拟 user 和 运行 客户端进程。如果客户端进程不尝试创建到 Server3 的新 SQL 连接,这一切都很好。但是,如果现在 运行 在 S4U2Self 协议下的进程尝试使用 Kerberos 协议建立新的 SQL 连接,除非也允许协议转换,否则它将失败。

svc 设置 T2A4D 标志将导致 S4U 票据接收 forwardable 标志

#2> Client: user @ VNET.COM Server: svc @ KerbTicket Encryption Type: RSADSI RC4-HMAC(NT) Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize Start Time: 2/20/2020 22:23:37 (local) End Time: 2/21/2020 7:07:45 (local) Renew Time: 2/27/2020 21:07:45 (local) Session Key Type: RSADSI RC4-HMAC(NT) Cache Flags: 0x4 -> S4U Kdc Called: DC.Vnet.com

如前所述here,现在一切正常!