I'm trying to run a net core 3 API as a windows service (i.e. not using IIS) with Http.Sys authenticating accounts on an active directory. I would like to have Basic auth enabled (have to support non .NET languages where developers have requested basic auth) but allow windows/kerberos where available.
I am most of the way there, in fact I basically have a service that does everything that I want, except for 1 thing - I only see NTLM being used instead of kerberos. I could potentially go to prod as it is but I'd like to understand why I can't get kerberos working. I believe the final piece is the SPN.
Create:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
services.AddHostedService<Worker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Basic | AuthenticationSchemes.Negotiate;
options.Authentication.AllowAnonymous = true;
options.UrlPrefixes.Add("https://*:20010/");
});
webBuilder.UseStartup<Startup>();
})
.UseWindowsService();
}
Startup class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.IgnoreNullValues = true;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Controller:
[ApiController]
[Route("test")]
[Authorize]
public class TestController : ControllerBase
{
[HttpGet("run")]
public ContentResult Run()
{
return new ContentResult
{
Content = $"Run Requested: {string.Join(",", this.User?.Identity?.Name, this.User?.Identity?.IsAuthenticated, this.User?.Identity?.AuthenticationType)}",
};
}
}
I have paid for a proper certificate which I setup on port 20010 with netsh in a ps1 script:
$guid = [guid]::NewGuid()
$certHash = "<CORRECT THUMBNAIL>"
$ip = "0.0.0.0" # This means all IP addresses
$port = "20010" # the default HTTPS port
"http add sslcert ipport=$($ip):$port certhash=$certHash appid={$guid}" | netsh
The Webserver:
- properly joined to the domain
- netbios name: mywebserver
- full name: mywebserver.ad.mydomain.com
The service:
- .net core 3.1
- running under domain account 'myserviceaccount'
The service account 'myserviceaccount' currently has admin privs on the web server, I think I can remove that if I grant permissions to the certificate directly to this account. Or perhaps to get this all working I have to run it as a system account? Prefer not to do this though.
The active directory:
- ad.mydomain.com
- server netbios name: domainserver
- server full name: domainserver.ad.mydomain.com
As is, this works fine with all browsers/OS and from all languages that are set to use the service. It challenges correctly, allows domain users through and shows the proper certificate. But as stated above, when I use various auth testers/fiddler etc - it only shows NTLM when accessing from windows clients.
I have tried to set the following SPNs on the web server:
setspn -S HTTP/mywebserver.ad.mydomain.com:20010 MYDOMAIN\mywebserver
setspn -S HTTPS/mywebserver.ad.mydomain.com:20010 MYDOMAIN\mywebserver
setspn -S HTTP/mywebserver.ad.mydomain.com:20010 MYDOMAIN\myserviceaccount
setspn -S HTTPS/mywebserver.ad.mydomain.com:20010 MYDOMAIN\myserviceaccount
I made sure to restart the service each time I tried a new SPN, and fully close down all browsers.
I don't know if these are correct (I'm assuming they're not since it falls back to NTLM!). I am not sure if I should be setting these on mywebserver or domainserver.
I have tried setting the scheme to AuthenticationSchemes.Kerberos only as a test, but when I do this I get no response from the server at all.
Can anyone help me get over the line? Thanks in advance!