Context: Custom reverse proxy/API Gateway built in C# on top of IIS and IIS Rewrite-rules, making use of OWIN middleware.
Goal: I'd like the proxy to authenticate the incoming request first(this is easily configured in IIS). Having the identity available, an OWIN middleware should assert some things based on the authenticated user. Afterwards, however, the request should be sent to the backend API, which is also windows authenticated.
My attempted solution: Before sending the request further, the OWIN middleware should impersonate the authenticated identity, request a Kerberos ticket and put it to the Authorization header (as Negotiate XXXXX.....
).
Here's the code using C# standard libs:
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);
}
Here's an alternative implementation I tried, using 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);
}
Both implementations throw an exception, which boils down to the following Win32 error:
No credentials are available in the security package
Important info:
- the proxy is hosted in IIS, running under a service account (let's say
srv_ApiGateway
useAppPoolCredentials
in IIS is set toTrue
(otherwise IIS wouldn't be able to even authenticate the initial request using Kerberos)- the service account (
srv_ApiGateway
) is set up for delegation in AD toTrust this user for delegation to any service (Kerberos only)
- In both implementations, removing the impersonation and simply requesting a ticket as the service account works (just FYI)
- Both the proxy and the backend API have proper SPNs (direct requests are authenticated correctly using Kerberos).
WindowsIdentity.GetCurrent().ImpersonationLevel
inside theusing
returnsImpersonationLevel.Impersonation
(this should proabably beDelegation
)
Does anyone have any suggestions as to what might be causing this and how can I fix this double-hop reverse-proxy use-case? Any help is appreciated :)