This issue happens with Linux Azure Container Instances (ACI) after upgrading the app to .NET 8; the exact same code works targeting .NET 6 (i.e. if I only change <TargetFramework>
in the .csproj
and the publish profile).
I get the following exception when launching an ASP.NET Core 8 app in an ACI:
fail: Microsoft.Extensions.Hosting.Internal.Host[11]
Hosting failed to start
System.Net.Sockets.SocketException (13): Permission denied
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint)
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>c__DisplayClass28_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.AnyIPListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.EndpointsStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(ListenOptions[] listenOptions, AddressBindContext context, Func`2 useHttps, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token)
at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
Unhandled exception. System.Net.Sockets.SocketException (13): Permission denied
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint)
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>c__DisplayClass28_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.AnyIPListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.EndpointsStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(ListenOptions[] listenOptions, AddressBindContext context, Func`2 useHttps, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token)
at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Program.<Main>$(String[] args) in E:MyProject.WebProgram.cs:line 42
This happens if I try to make Kestrel listen on ports 80, 443, or both. I don’t get any more information with Trace
-level logging.
This is what I tried:
- Alternative configuration options for Kestrel, but it doesn’t matter how I configure this, be it in
appsettings.json
underKestrel__Endpoints
, theASPNETCORE_HTTP_PORTS
andASPNETCORE_HTTPS_PORTS
environment variables, or withUseKestrel()
. - Making Kestrel listen under the internal IP of the container, not any IP, but that yields the same result.
- Using different ports, like 8080 and 8443. That does work, but since I need this web app to be available under standard HTTP and HTTPS, 80 and 443 should be the ones publicly exposed. And I can’t do that while using a different port internally, since ACI doesn’t support port mapping.
- I’m aware of the default port having changed from 80 to 8080 with .NET 8, however, that should be addressed by setting the port to 80 explicitly as under the first point, nor should it affect port 443.
- ACI seems to reserve port 443 but neither should that affect port 80, nor should port 443 work with .NET 6. So, this is a .NET-related issue, not this port reservation.
- Using
--privileged
withaz container create
(as well as--allow-escalation
) but nothing changed.
I can’t reproduce this locally under Windows.
The container image is published self-contained.
Can somebody help me understand what I may be doing wrong? Is this perhaps an undocumented breaking change, in .NET 8 or how ACI supports .NET 8?
Cross-post from an aspnetcore
discussion since nobody replied there for a week.
Update: I cross-posted to Microsoft Q&A for visibility.
Update 2: I dug some more and found that actually this might be the breaking change relevant to this issue, not the default port change: New non-root ‘app’ user in Linux images. Also see the blogpost it links. And sure enough, with .NET 6 the container’s user is root
, and with .NET 8 it’s app
.
I tried
az container create `
--run-as-group 0 `
--run-as-user 0 `
...
to run the container as root but it still runs as app
. Adding USER root
in the Dockerfile doesn’t do anything either because the Dockerfile has no effect for Azure Container Instance, they use az container create
as a substitute. I also tried setting ContainerUser
with <ContainerUser>root</ContainerUser>
but no luck.
2
Answers
Found the solution. It was actually setting ContainerUser with
<ContainerUser>root</ContainerUser>
in the csproj. No other changes necessary.So, all in all, compared to the .NET 6 version, my app has the following changes to target .NET 8:
<TargetFramework>net8.0</TargetFramework>
in the csproj.<TargetFramework>net8.0</TargetFramework>
in the pubxml used byaz container create
.<ContainerUser>root</ContainerUser>
in the csproj, see below the context.Full section of the csproj:
I did try this before, but apparently, I messed up something during publishing, but now it's clear.
I also opened an issue to clarify this in the docs: https://github.com/dotnet/docs/issues/39082.
.NET 8 linux containers are not running under user "root" anymore. And because port 80 is a Privileged port, you can not make Kestrel listen on port 80.
I’m not familiar with Container Instances, but I think you can do port mapping as shown here. You can leave the default 8080 port for inside the container.