Windows-Containers icon indicating copy to clipboard operation
Windows-Containers copied to clipboard

Kerberos broken in recent nanoserver images

Open avin3sh opened this issue 5 months ago • 1 comments

Describe the bug It looks like nanoserver-ltsc2022 images released in April 2024 and later have broken Kerberos.

To Reproduce First, create a simple ASP.NET Core project AspNetKerbHello.

Create AspNetKerbHello.csproj with the following content:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Negotiate" Version="8.0.8" />
  </ItemGroup>

</Project>

Now, create Program.cs with the following content - we are enabling negotiate authentication here:

using Microsoft.AspNetCore.Authentication.Negotiate;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
    .AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = options.DefaultPolicy;
});

var app = builder.Build();

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

app.MapGet("/hello", (HttpContext context) =>
{
    var userName = context.User.Identity?.Name ?? "Anonymous";
    var authType = context.User.Identity?.AuthenticationType ?? "None";
    return Results.Ok($"Hello, {userName}! - via {authType}");
});

app.Run();

Now build the above project, using dotnet build command.

Now, inside the build output directory where AspNetKerbHello.dll is present, create a Dockerfile with the following content. We are purposefully pulling .NET runtime 8.0.4 because it uses nanoserver image from April 2024 - i.e. mcr.microsoft.com/windows/nanoserver:ltsc2022-KB5036909 -- but this works with even the latest nanoserver image:

FROM mcr.microsoft.com/dotnet/aspnet:8.0.4-nanoserver-ltsc2022
WORKDIR /app
EXPOSE 80
ENV ASPNETCORE_URLS=http://+:80

COPY . .

USER ContainerUser
ENTRYPOINT ["dotnet", "AspNetKerbHello.dll"]

Now build this docker image, let's assume it's tagged aspnetkerbhello:v1:

docker build -t aspnetkerbhello:v1 .

Now run this image in Kubernetes as a gMSA that owns a SPN

Now, access the Kubernetes SVC URI - assuming the SPN owned by the gMSA is http/<FQDN> where FQDN is Kubernetes SVC domain name. In the below example SPN is http/my-svc.my-namespace.svc.cluster.local:

Invoke-WebRequest -UseBasicParsing -UseDefaultCredentials -AllowUnencryptedAuthentication "http://my-svc.my-namespace.svc.cluster.local/hello"

Assuming NTLM is disabled and authentication is happening over Kerberos, you will get HTTP 500 error with an exception containing the following error callstack in the Kubernetes pod logs:

fail: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[5]
      An exception occurred while processing the authentication request.
      System.Net.InternalException: Exception of type 'System.Net.InternalException' was thrown. -1073741428
         at System.Net.SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(SECURITY_STATUS win32SecurityStatus, Boolean attachException)
         at System.Net.NegotiateAuthenticationPal.WindowsNegotiateAuthenticationPal.AcceptSecurityContext(SafeFreeCredentials credentialsHandle, SafeDeleteContext& securityContext, ContextFlags requestedContextFlags, ReadOnlySpan`1 incomingBlob, ChannelBinding channelBinding, Byte[]& resultBlob, Int32& resultBlobLength, ContextFlags& contextFlags)
         at System.Net.NegotiateAuthenticationPal.WindowsNegotiateAuthenticationPal.GetOutgoingBlob(ReadOnlySpan`1 incomingBlob, NegotiateAuthenticationStatusCode& statusCode)
         at System.Net.Security.NegotiateAuthentication.GetOutgoingBlob(ReadOnlySpan`1 incomingBlob, NegotiateAuthenticationStatusCode& statusCode)
         at System.Net.Security.NegotiateAuthentication.GetOutgoingBlob(String incomingBlob, NegotiateAuthenticationStatusCode& statusCode)
         at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateState.GetOutgoingBlob(String incomingBlob, BlobErrorType& status, Exception& error)
         at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()

If you made a mistake somewhere, you will instead get authenticated over NTLM and won't see the error - rather a 200 OK page with the following message:

"Hello, contoso\\username! - via NTLM"

The issue only occurs when using Kerberos, not NTLM.

Now, all of the above can be also done without involving Kubernetes with a docker container running with gMSA cred-spec file but it's bit tricky - you need to first enable Kerberos on loopback using DisableLoopbackCheck registry key, and then use [System.Net.AuthenticationManager]::CustomTargetNameDictionary to map localhost to the SPN owned by the gMSA; and even then you might endup with NTLM. So I recommend using Kubernetes for testing the above.

You do not need multiple replicas. Even just a single pod runs into the above error.

I know the issue isn't because of any change in the .NET runtime version because I have created a custom .NET runtime image with the exact same runtime version (8.0.4) but older (March 2024) nanoserver base image and I couldn't repro the issue - suggesting the issue is in the nanoserver base image.

Expected behavior

Kerberos should work with nanoserver images

Configuration:

  • Edition: Windows Server 2022 with September 2024 build
  • Base Image being used: nanoserver-ltsc2022
  • Container engine: docker and containerd both
  • Container Engine version: docker 26 / containerd 1.6.31

Additional context

Per https://github.com/dotnet/runtime/discussions/105567#discussion-6980650 the error System.Net.InternalException: Exception of type 'System.Net.InternalException' was thrown. -1073741428 translates to ERROR_TRUSTED_DOMAIN_FAILURE or The trust relationship between the primary domain and the trusted domain failed, so I suspect this is likely related to the other gMSA issue https://github.com/microsoft/Windows-Containers/issues/405

avin3sh avatar Sep 15 '24 20:09 avin3sh