I've created an image with non-root user and always thought that you can't bind to ports < 1024 (I saw it in different guides and a lot of questions on StackOverflow). But for testing, I've create simple ASP.NET Core Web API (from template) and generated Dockerfile by VS Code (even VS Code thinks the same, if you generate Dockerfile for 5000 port it creates non-root user, but if you select 80 port then it creates image with root user).
Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:5.0-focal AS base
WORKDIR /app
EXPOSE 80
ENV ASPNETCORE_URLS=http://+:80
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
RUN addgroup --gid 8765 appgroup
RUN adduser -u 5678 --gid 8765 --disabled-password --gecos "" appuser && chown -R appuser:appgroup /app
USER appuser:appgroup
FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS build
WORKDIR /src
COPY ["webapi.csproj", "./"]
RUN dotnet restore "webapi.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "webapi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "webapi.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "webapi.dll"]
Then build and run it:
docker build . -t test
docker run --rm -it -p 5000:80 test
And it works perfectly, then I check result of top command and it outputs that dotnet runs as appuser.
Is this restriction (root access to bind < 1024 ports) not valid anymore? Is it changed by newer versions of Docker or is it related to .NET?
According to docker run documentation, Docker adds several Linux capabilities (by default). One of them is NET_BIND_SERVICE, which allows:
Bind a socket to internet domain privileged ports (port numbers less than 1024).
But if I drop all capabilities by --cap-drop=all, it still works.
Tested on:
Docker Desktop 3.5.2 (Mac OS / Windows 10)
Linux Docker 20.10.7
It seems that since Docker 20.03.0 you can now bind to any port even while dropping all capabilities.
See the answer here In my Docker container, why can I still bind the port 1 without `NET_BIND_SERVICE` capability?
Related
I'm trying to run simplest asp.net core 3.1 WebApplication inside docker as simple as possible but, docker does not binding host port.
Host: Windows 10
Container target: Linux
Docker version: 20.10.7
Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
EXPOSE 80
EXPOSE 443
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY . .
RUN dotnet restore "WebApplication.csproj"
RUN dotnet build "WebApplication.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication.dll"]
Docker build command (ok)
docker build -t webapplication .
Docker run command (ok)
docker run webapplication -p 5000:80 -p 5001:443
When I try to access website on my localhost (http://localhost:5000) I'm getting This site can’t be reached. Running the docker ps returned ports information without host mapping port
As #marzelin correctly pointed out, you need to add the flag right after run.
I have a .Net 3.1 API running as a Docker container (WSL2+ Linux Containers) on Windows 10. I am unable to access the API on any other ports except 5000 :
When i run the container as
docker run -d -p 8060:7110 golide/payapi:0.1.0
and try a GET in Postman to http://localhost:7110/weatherforecast I get "Unable to Connect" exception.
But when i run the container as
docker run -d -p 5000:5000 golide/payapi:0.1.0
and try a GET in Postman to http://localhost:5000/weatherforecast I get the API response.
What am I missing regarding how Kestrel port binding works ?
My Dockerfile looks as follows :
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:3.1
WORKDIR /app
ENV ASPNETCORE_URLS http://+:5000
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "PaymentsAPI.dll"]
What additional configuration should I do (if any) in order to access the API on any other port ?
For the port (-p) option of docker run, the format looks like <host-port>:<container-port>. Since you used a value of 8060:7110 and attempted to access localhost:7110 on the host, you're targeting the wrong port. You should be targeting 8060 from the host, or swap the position of the ports if that's what you need.
Then you'd also want to have ENV ASPNETCORE_URLS http://+:<container-port> set in your Dockerfile as well.
When I run docker with Asp.net Core locally everything is ok. But when I use the Ubuntu virtual machine in cloud.google, I kind of start the container and everything works. But I don't know how to open the site. Login via External ip doesn't open anything. I just have "Can't access the site" Although everything opens locally. Am I missing any settings?
Dockerfile:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
Command: sudo docker run -d -p 8000:80 gamehomm/webapplication1
I got it!
docker run -d -p 80:80/tcp gamehomm/webapplication1
Then I changed VM instance details
Firewalls
Allow HTTP traffic
Allow HTTPS traffic
I'm trying to configure my docker container so it's possible to ssh into it (the container will be run on Azure). I managed to create an image that enables user to ssh into a container created from that image, the Dockerfile looks like that (it's not mine, I found it on the internet):
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
EXPOSE 2222
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
COPY sshd_config /etc/ssh
RUN echo 'root:Docker' | chpasswd
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed 's#session\s*required\s*pam_loginuid.so#session optional pam_loginuid.so#g' -i /etc/pam.d/sshd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
CMD ["/usr/sbin/sshd", "-D"]
I'm using mcr.microsoft.com/dotnet/core/sdk:2.2-stretch because it's what I need later on to run the application.
Having the Dockerfile above, I run docker build . -t ssh. I can confirm that it's possible to ssh into a container created from ssh image with following instructions:
docker run -d -p 0.0.0.0:2222:22 --name ssh ssh
ssh root#localhost -p 2222
My application's Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["Application.WebAPI/Application.WebAPI.csproj", "Application.WebAPI/"]
COPY ["Processing.Dependency/Processing.Dependency.csproj", "Processing.Dependency/"]
COPY ["Processing.QueryHandling/Processing.QueryHandling.csproj", "Processing.QueryHandling/"]
COPY ["Model.ViewModels/Model.ViewModels.csproj", "Model.ViewModels/"]
COPY ["Core.Infrastructure/Core.Infrastructure.csproj", "Core.Infrastructure/"]
COPY ["Model.Values/Model.Values.csproj", "Model.Values/"]
COPY ["Sql.Business/Sql.Business.csproj", "Sql.Business/"]
COPY ["Model.Events/Model.Events.csproj", "Model.Events/"]
COPY ["Model.Messages/Model.Messages.csproj", "Model.Messages/"]
COPY ["Model.Commands/Model.Commands.csproj", "Model.Commands/"]
COPY ["Sql.Common/Sql.Common.csproj", "Sql.Common/"]
COPY ["Model.Business/Model.Business.csproj", "Model.Business/"]
COPY ["Processing.MessageBus/Processing.MessageBus.csproj", "Processing.MessageBus/"]
COPY [".Processing.CommandHandling/Processing.CommandHandling.csproj", "Processing.CommandHandling/"]
COPY ["Processing.EventHandling/Processing.EventHandling.csproj", "Processing.EventHandling/"]
COPY ["Sql.System/Sql.System.csproj", "Sql.System/"]
COPY ["Application.Common/Application.Common.csproj", "Application.Common/"]
RUN dotnet restore "Application.WebAPI/Application.WebAPI.csproj"
COPY . .
WORKDIR "/src/Application.WebAPI"
RUN dotnet build "Application.WebAPI.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Application.WebAPI.csproj" -c Release -o /app
FROM ssh AS final
WORKDIR /app
EXPOSE 80
EXPOSE 443
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Application.WebApi.dll"]
As you can see I'm using ssh image as a base image in the final stage. Even though I was able to sshe into the container created from ssh image, I'm unable to ssh into a container created from the latter Dockerfile. Here is the docker-compose.yml I'm using in order to ease starting the container:
version: '3.7'
services:
application.webapi:
image: application.webapi
container_name: webapi
ports:
- "0.0.0.0:5000:80"
- "0.0.0.0:2222:22"
build:
context: .
dockerfile: Application.WebAPI/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=docker
When I run docker exec -it webapi bashand execute service ssh status, I'm getting [FAIL] sshd is not running ... failed! - but when I do service ssh start and try to ssh into that container, it works. Unfortunately this approach is not acceptable, ssh daemon should launch itself on startup.
I tried using cron and other stuff available on debian but it's a slim version and systemd is not available there - I'm also not fond of installing hundreds of things on slim versions.
Do you have any ideas what could be wrong here?
You have conflicting startup command definitions in your final image. Note that CMD does not simply run a command in your image, it defines the startup command, and has a complex interaction with ENTRYPOINT (in short: if both are present, CMD just supplies extra arguments to ENTRYPOINT).
You can see the table of possibilities in the Dockerfile documentation: https://docs.docker.com/engine/reference/builder/. In addition, there's a bonus complication when you mix and match CMD and ENTRYPOINT in different layers:
Note: If CMD is defined from the base image, setting ENTRYPOINT will reset CMD to an empty value. In this scenario, CMD must be defined in the current image to have a value.
As far as I know, you can't get what you want just by layering images. You will need to create a startup script in your final image that both runs sshd -D and then runs dotnet Application.WebApi.dll.
I'm successfully running a simple dotnet core 2.1 web API application in docker but want to run it under a custom account instead of under root as this is supposedly best practice.
I can add an account and change to that account, but then Kestral throws an error on startup.
I've searched the web repeatedly and can't find any solutions.
Here's the Docker file.
FROM sel-docker.artifactory.metro.ad.selinc.com/microsoft/dotnet:2.1.500-sdk-
alpine3.7 AS build-env
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# build runtime image
FROM sel-docker.artifactory.metro.ad.selinc.com/microsoft/dotnet:2.1.6-
aspnetcore-runtime-alpine3.7
# Create a group and user
RUN addgroup -S -g 1000 customgroup \
&& adduser -S -u 1000 -G customgroup -s /bin/sh customuser
WORKDIR /app
RUN mkdir -p /local/
COPY --from=build-env /app/out .
RUN chown customuser:customgroup /local
RUN chown customuser:customgroup /app
# Tell docker that all future commands should run as the appuser user
USER 1000
ENTRYPOINT ["dotnet", "ConfigApi.dll"]
And here is the Kestral error when I run the resultant image.
crit: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to start Kestrel.
System.Net.Sockets.SocketException (13): Permission denied
...
Has anyone solved this?
In linux, binding to a port less than 1024 requires the user to be superuser. You can just use the default port 5000 and then publish to port 80 on your host (if you don't have any reverse proxy).
Because this gets so much traffic, I'm adding the fully detailed code that you need to get this done.
# Create a group and user so we are not running our container and application as root and thus user 0 which is a security issue.
RUN addgroup --system --gid 1000 customgroup \
&& adduser --system --uid 1000 --ingroup customgroup --shell /bin/sh customuser
# Serve on port 8080, we cannot serve on port 80 with a custom user that is not root.
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
# Tell docker that all future commands should run as the appuser user, must use the user number
USER 1000
Has anyone got this working. I have tired this as well with using port 5000 and still haven't been able to get this working with a custom user
To enable ASP.NET core to bind to a higher port, I set this environment variable in my dockerfile
ENV ASPNETCORE_URLS=http://*:8080
Sources: https://github.com/dotnet/aspnetcore/issues/4699#issuecomment-454818058