[英]ASP.NET Core kestrel windows authentication in docker identifies wrong user
I'm using docker (windows nanoserver), traefik, asp.net core 3.1 and windows authentication/negotiate.我正在使用 docker(windows nanoserver)、traefik、asp.net core 3.1 和 windows 身份验证/协商。 If a user connects to the container, he or she logs in and their user is bound to the httpcontext.
如果用户连接到容器,他或她登录并且他们的用户被绑定到 httpcontext。 If another user connects afte that, the first user is still bound to the context.
如果之后有另一个用户连接,第一个用户仍然绑定到上下文。
This is my setup in code:这是我在代码中的设置:
docker-compose.yml docker-compose.yml
version: "3.7"
services:
webapplication:
image: <repository>/webapplication:8-1
networks:
webapplication-network:
aliases:
- webapplication
traefik:
aliases:
- traefik-webapplication
credential_spec:
file: webapp({GMSA_ACCOUNT})_credential.json
secrets:
- source: config_secrets
target: C:/app/appsettings.json
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 10
window: 30s
labels:
- applicationname=({APPLICATIONNAME})
- "traefik.enable=true"
- "traefik.http.routers.({APPLICATIONNAME})-webapplication.rule=Host(`({APPLICATIONNAME})-webapplication.({DOMAIN})`)"
- "traefik.http.routers.({APPLICATIONNAME})-webapplication.service=({APPLICATIONNAME})-webapplication"
- "traefik.http.routers.({APPLICATIONNAME})-webapplication.entrypoints=https"
- "traefik.http.routers.({APPLICATIONNAME})-webapplication.tls=true"
- "traefik.http.services.({APPLICATIONNAME})-webapplication.loadbalancer.server.scheme=http"
- "traefik.http.services.({APPLICATIONNAME})-webapplication.loadbalancer.server.port=80"
# redirect http to https
- traefik.http.middlewares.({APPLICATIONNAME})-webapplication-unsecure-redirect-secure.redirectscheme.scheme=https
- traefik.http.routers.({APPLICATIONNAME})-webapplication-unsecure.middlewares=({APPLICATIONNAME})-webapplication-unsecure-redirect-secure
- traefik.http.routers.({APPLICATIONNAME})-webapplication-unsecure.rule=Host(`({APPLICATIONNAME})-webapplication.({DOMAIN})`)
- traefik.http.routers.({APPLICATIONNAME})-webapplication-unsecure.entrypoints=http
# Attempt to make Windows authentication work through TCP
- "traefik.tcp.routers.({APPLICATIONNAME})-webapplication.rule=HostSNI(`({APPLICATIONNAME})-webapplication.({DOMAIN})`)"
- "traefik.tcp.routers.({APPLICATIONNAME})-webapplication.service=({APPLICATIONNAME})-webapplication"
- "traefik.tcp.routers.({APPLICATIONNAME})-webapplication.tls=true"
- "traefik.tcp.services.({APPLICATIONNAME})-webapplication.loadbalancer.server.port=80"
- "traefik.http.services.({APPLICATIONNAME})-webapplication.loadbalancer.sticky=true"
Dockerfile webapplication-windows-netcore-base Dockerfile webapplication-windows-netcore-base
# escape=`
FROM mcr.microsoft.com/windows/servercore:ltsc2019 AS installer
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# Retrieve .NET Core Runtime
RUN $dotnet_version = '3.1.3'; `
Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.azureedge.net/dotnet/Runtime/$dotnet_version/dotnet-runtime-$dotnet_version-win-x64.zip; `
$dotnet_sha512 = '62a18838664afd6f08cdb9a90a96a67626743aab1f0de0065eadfd7d1df31681c90f96744ccb5b7e40834c9e3c4952125c8c83625867c500692050cdd113ff50'; `
if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive dotnet.zip -DestinationPath dotnet; `
Remove-Item -Force dotnet.zip; `
# Install ASP.NET Core Runtime
$aspnetcore_version = '3.1.3'; `
Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$aspnetcore_version/aspnetcore-runtime-$aspnetcore_version-win-x64.zip; `
$aspnetcore_sha512 = '6d8a21a7420db9091fc05613ef0a923c7b86cc995360c9f0133d632020e042db0efac0343ee6516a28feb2c1dd004b33b74bfdcc1a687efdefa9db1a486c1ca2'; `
if ((Get-FileHash aspnetcore.zip -Algorithm sha512).Hash -ne $aspnetcore_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive aspnetcore.zip -DestinationPath dotnet -Force; `
Remove-Item -Force aspnetcore.zip
# Runtime image
FROM mcr.microsoft.com/windows/servercore:ltsc2019
ENV `
# Configure web servers to bind to port 80 when present
ASPNETCORE_URLS=http://+:80 `
# Enable detection of running in a container
DOTNET_RUNNING_IN_CONTAINER=true
WORKDIR C:\app
USER ContainerAdministrator
RUN setx /M PATH "%PATH%;C:\Program Files\dotnet" && `
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 -f
COPY --from=installer ["/dotnet", "/Program Files/dotnet"]
Dockerfile webapplication Dockerfile 网络应用程序
# escape=`
##### Runtime #####
ARG baseImageVersie
ARG buildNumber
ARG release
FROM <repository>/webapplication-build:$release-$buildNumber AS buildtools
FROM webapplication-windows-netcore-base:$baseImageVersie
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENTRYPOINT ["dotnet", "webapplication.dll"]
COPY --from=buildtools C:/publish .
Startup.cs looks like this Startup.cs 看起来像这样
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(o =>
{
o.Filters.Add(typeof(GlobalExceptionFilter));
});
services
.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate(o => o.Validate());
var appsettings = configuration.Get<Appsettings>();
services.AddCors(options =>
{
options.AddPolicy("cors",
builder =>
{
builder.WithOrigins(appsettings.Origins)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Webapplication",
Version = "v1" ,
});
c.ExampleFilters();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
});
services.AddSwaggerExamplesFromAssemblyOf<AuthorizeModelExample>();
}
// ReSharper disable once UnusedMember.Global
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors("cors");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization()
.RequireCors("cors");
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Webapplication V1");
});
}
}
UserController用户控制器
[Produces("application/json")]
[ApiController]
[Route("api/connect/[controller]")]
public class UserController : ControllerBase
{
[HttpGet]
public ActionResult Get()
{
return Ok(User?.Identity?.Name)
}
}
What happens - User 1 logs in (using incognito mode) by filling in his credentials - User 1 calls /api/connect/user and it returns "user 1" - User 2 opens webapplication and does not have to provide any credentials - User 2 calls /api/connect/user and it still returns "user 1"会发生什么- 用户 1 通过填写他的凭据登录(使用隐身模式) - 用户 1 调用 /api/connect/user 并返回“用户 1” - 用户 2 打开 Web 应用程序并且不必提供任何凭据 - 用户 2调用 /api/connect/user仍然返回“user 1”
Question: How do i prevent my asp.net core application returning the wrong windows authentication user?问题:如何防止我的 asp.net 核心应用程序返回错误的 windows 身份验证用户?
I managed to make it work using traefik TLS passthrough.我设法使用 traefik TLS passthrough 使其工作。 So I had to change the application to serve https itself instead of having traefik do SSL termination.
所以我不得不改变应用程序来服务 https 本身,而不是让 traefik 做 SSL 终止。 My application's compose file now looks like this:
我的应用程序的撰写文件现在如下所示:
docker-compose.yml docker-compose.yml
version: "3.7"
services:
webapplication:
image: <repository>/webapplication:8-1
environment:
- ASPNETCORE_Kestrel__Certificates__Default__Path=wildcard_certificate.pfx
networks:
webapplication-network:
aliases:
- webapplication
traefik:
aliases:
- traefik-webapplication
credential_spec:
file: webapp({GMSA_ACCOUNT})_credential.json
secrets:
- source: config_secrets
target: C:/app/appsettings.json
- source: wildcard_certificate_pfx
target: c:\certificates\wildcard_certificate.pfx
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 10
window: 30s
labels:
- applicatienaam=({APPLICATIENAAM})
- "traefik.enable=true"
- "traefik.http.routers.({APPLICATIENAAM})-webapplication.rule=Host(`({APPLICATIENAAM})-webapplication.({DOMAIN})`)"
- "traefik.http.routers.({APPLICATIENAAM})-webapplication.entrypoints=https"
- "traefik.http.routers.({APPLICATIENAAM})-webapplication.tls=true"
- "traefik.http.services.({APPLICATIENAAM})-webapplication.loadbalancer.server.scheme=https"
- "traefik.http.services.({APPLICATIENAAM})-webapplication.loadbalancer.server.port=443"
# Windows authentication works through TCP
# TLS passtrough, because otherwise windows authentication won't support multiple users
- "traefik.tcp.routers.({APPLICATIENAAM})-webapplication.tls=true"
- "traefik.tcp.routers.({APPLICATIENAAM})-webapplication.tls.options=default"
- "traefik.tcp.routers.({APPLICATIENAAM})-webapplication.tls.passthrough=true"
- "traefik.tcp.routers.({APPLICATIENAAM})-webapplication.rule=HostSNI(`({APPLICATIENAAM})-webapplication.({DOMAIN})`)"
- "traefik.tcp.routers.({APPLICATIENAAM})-webapplication.entrypoints=https"
- "traefik.tcp.services.({APPLICATIENAAM})-webapplication.loadbalancer.server.port=443"
I think the first user that logs in is bound to the traefik connection, so all next users will use the first users session, but I don't know for sure.我认为第一个登录的用户绑定到traefik连接,所以所有下一个用户将使用第一个用户session,但我不确定。 All I know is that the solution above prevents mixing up user sessions.
我所知道的是,上面的解决方案可以防止混淆用户会话。
I also had to make a change to my dockerfile to make serving https from the container work, because of a WindowsCryptographicException bug .由于WindowsCryptographicException 错误,我还必须更改我的 dockerfile 以使从容器工作的 https 工作。
# escape=`
##### Runtime #####
ARG baseImageVersie
ARG buildNumber
ARG release
FROM <repository>/webapplication-build:$release-$buildNumber AS buildtools
FROM webapplication-windows-netcore-base:$baseImageVersie
ENV DOTNET_RUNNING_IN_CONTAINER=true
# The copy is done, because wildcard_certificate.pfx is put into the container using docker secrets, which makes it a symlink.
# Reading a certificate as a symlink is not supported at this moment: https://stackoverflow.com/q/43955181/1608705
# After doing a copy, the copied version is not a symlink anymore.
ENTRYPOINT (IF EXIST "c:\certificates\wildcard_certificate.pfx" (copy c:\certificates\wildcard_certificate.pfx c:\app\wildcard_certificate.pfx)) && dotnet webapplication.dll
COPY --from=buildtools C:/publish .
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.