上下文:在 IIS 和 IIS 重写规则之上用 C# 构建的自定义反向代理/API 网关,利用 OWIN 中间件。
目标:我希望代理首先对传入请求进行身份验证(这在 IIS 中很容易配置)。有了可用的身份,OWIN 中间件应该根据经过身份验证的用户声明一些事情。然而,之后,请求应该被发送到后端 API,这也是 windows 认证的。
我尝试的解决方案:在进一步发送请求之前,OWIN 中间件应该模拟经过身份验证的身份,请求 Kerberos 票证并将其放入 Authorization 标头(as Negotiate XXXXX.....
)。
这是使用 C# 标准库的代码:
var identity = (WindowsIdentity) context.Request.User.Identity;
using (var impersonation = identity.Impersonate())
{
var spn = "HTTP/" + backendHostname; // e.g. HTTP/myapi.mydomain.com
var tokenProvider = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation);
var token = tokenProvider.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;
var ticketBytes = token.GetRequest();
var ticket = Convert.ToBase64String(ticketBytes);
context.Request.Headers.Append("Negotiate", ticket);
}
这是我尝试的另一种实现,使用NSSPI:
var identity = (WindowsIdentity) context.Request.User.Identity;
using (var impersonation = identity.Impersonate())
{
var clientCredentials = new ClientCurrentCredential(PackageNames.Kerberos);
var client = new ClientContext(
clientCredentials,
"HTTP/" + backendHostname, // e.g. HTTP/myapi.mydomain.com
ContextAttrib.MutualAuth |
ContextAttrib.InitIdentify |
ContextAttrib.Confidentiality |
ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect |
ContextAttrib.Connection |
ContextAttrib.Delegate
);
var clientStatus = client.Init(null, out var tokenBytes);
var token = Convert.ToBase64String(tokenBytes);
context.Request.Headers.Append("Negotiate", token);
}
两种实现都抛出异常,归结为以下 Win32 错误:
安全包中没有可用的凭据
重要信息:
- 代理托管在 IIS 中,在服务帐户下运行(假设
srv_ApiGateway
useAppPoolCredentials
在 IIS 中设置为True
(否则 IIS 甚至无法使用 Kerberos 对初始请求进行身份验证)- 服务帐户 (
srv_ApiGateway
) 在 AD 中设置为委派给Trust this user for delegation to any service (Kerberos only)
- 在这两种实现中,删除模拟并在服务帐户工作时简单地请求票证(仅供参考)
- 代理和后端 API 都有适当的 SPN(直接请求使用 Kerberos 正确验证)。
WindowsIdentity.GetCurrent().ImpersonationLevel
在using
回报内ImpersonationLevel.Impersonation
(这应该是可能的Delegation
)
是否有人对可能导致此问题的原因以及如何解决此双跳反向代理用例有任何建议?任何帮助表示赞赏:)