deadheaddeveloper
deadheaddeveloper

Reputation: 55

How can i get a .NET 8 minimal API in a Windows container within Docker Desktop working with gMSA?

Our organization is new to the container game (I know, I know) and we are heavily invested in the Microsoft ecosystem. We use Active Directory and not Azure Active Directory. We are tasked with creating a series of microservices using .NET 8. I believe we need to utilize gMSA to allow our APIs in Windows containers to use AD (https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts). Does anyone know if we need to do anything else to test out one of those Windows containers (nano server image) running on our local machines through Docker Desktop and if so what do we need to setup? Thank you!

We are in the research phase trying to deal with potential issues so we are trying to get the boiler plate minimal API up and running. We have the below code setup for Windows authentication. The container is running in Docker Desktop but we get a popup for login credentials every time we attempt to access the API such as https://localhost:53300/weatherforecast. Swagger will come up on https://localhost:53300/swagger but if you try to execute the the GET endpoint we naturally get the login credentials as well. No matter how many times we enter our creds we seem to be in an endless loop with the login popup asking for them again. We have logging enabled so we see that it's a 401 unauthorized response and see things like this in our log and we don't have gMSA set up for this but our research seems to indicate we need to do that but are curious if we'll need to do more for it to work in Docker Desktop in our dev environments:

dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[39]
      Connection id "0HN0JF74G3G36" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[1]
      Connection id "0HN0JF74G3G36" started.
dbug: Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware[3]
      Connection 0HN0JF74G3G36 established using the following protocol: Tls12
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 GET https://localhost:53300/weatherforecast - - -
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
      1 candidate(s) found for the request path '/weatherforecast'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint 'HTTP: GET /weatherforecast'
dbug: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[15]
      Static files was skipped as the request already matched an endpoint.
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[12]
      Negotiate is not supported with HTTP/2.
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[9]
      AuthenticationScheme: Negotiate was not authenticated.
dbug: Microsoft.AspNetCore.Authorization.AuthorizationMiddleware[0]
      Policy authentication schemes  did not succeed
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed. These requirements were not met:
      DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[6]
      Challenged 401 Negotiate.
info: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[12]
      AuthenticationScheme: Negotiate was challenged.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 GET https://localhost:53300/weatherforecast - 401 0 - 8.0066ms
using Microsoft.AspNetCore.Authentication.Negotiate;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
   .AddNegotiate();
builder.Services.AddAuthorization(opt => opt.FallbackPolicy = opt.DefaultPolicy);

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", (HttpContext context) =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.UseAuthentication();
app.UseAuthorization();
app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Upvotes: 1

Views: 148

Answers (0)

Related Questions