上下文:在IIS和IIS重写规则之上使用C#构建的自定义反向代理/ API网关,利用OWIN中间件。
目标:我希望代理首先验证传入的请求(这在IIS中很容易配置)。拥有该身份后,OWIN中间件应根据经过身份验证的用户声明某些内容。但是,之后,请求应该发送到后端API,后端API也经过Windows身份验证。
我尝试的解决方案:在进一步发送请求之前,OWIN中间件应该模拟经过身份验证的身份,请求Kerberos票证并将其放入Authorization标头(如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错误:
安全包中没有可用的凭据
重要信息:
srv_ApiGateway
useAppPoolCredentials
设置为True
(否则IIS将无法使用Kerberos验证初始请求)srv_ApiGateway
)设置为AD到Trust this user for delegation to any service (Kerberos only)
的委派WindowsIdentity.GetCurrent().ImpersonationLevel
内的using
返回ImpersonationLevel.Impersonation
(这应该可能是Delegation
)有没有人对可能导致这种情况的原因提出任何建议?如何修复这种双跳反向代理用例?任何帮助表示赞赏:)
假冒级别没有设置为Delegation
的原因是由于票证无效。出于某种原因,即使域名列入白名单,Google Chrome也不会为该用户请求授权票证(根据these docs)。尝试使用IE然后使用Chrome工作,即使不是真正的解决方案。