docker container with user created, where on host - docker

I have a dockerfile with a user created so it is not running as root(best pratice)
FROM microsoft/dotnet:sdk 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 microsoft/dotnet:aspnetcore-runtime
RUN groupadd -g 1001 appuser && useradd -r -u 1001 -g appuser appuser
USER appuser
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "ConsoleApp32.dll"]
I build the image and run the container:
docker build -f Dockerfile1 -t myappimage .
docker run -d --name myapp myappimage
And then check it running:
ps aux | grep dotnet
21569 1001 0:00 dotnet ConsoleApp32.dll
So running as uid 1001.
I then check host for this user:
cut -d: -f1 /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
news
uucp
operator
man
postmaster
cron
ftp
sshd
at
squid
xfs
games
postgres
cyrus
vpopmail
ntp
smmsp
guest
nobody
dockremap
No sign of appuser. My understanding(which may be wrong) is we are using a shared Kernel and user should be in list.
I also looked up uid
getent passwd 1001
Which returned no result.
Can someone explain this, as I dont understand how a process is running on host as a uid of 1001 and there is no associated user

The user ID is shared with the host: it’s 1001. The name of that user comes from looking it up in the /etc/passwd file. Since the host and container have different filesystem spaces, they have different passwd files; the kernel doesn’t know anything about a user’s name.
The corresponding FAQ: it doesn’t matter if you have users named pat on both the host and in the container; if their numeric user IDs don’t match up, and you’re on Linux, and you’re trying to share content with docker run -v, one user won’t be able to access the other’s files.

Related

Why can I bind to port 80

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?

Building Docker image as non root user

New here, was wondering if someone had experience with building images as non root user?
I am building Kotlin project, (2 step build) and my goal is now to build it as non root user. Here is what my Dockerfile looks like. Any help would be appreciated:
# Build
FROM openjdk:11-jdk-slim as builder
# Compile application
WORKDIR /root
COPY . .
RUN ./gradlew build
FROM openjdk:11-jre-slim
# Add application
COPY --from=builder /root/build/libs/*.jar ./app.jar
# Set the build version
ARG build_version
ENV BUILD_VERSION=$build_version
COPY docker-entrypoint.sh /
RUN chmod 777 /docker-entrypoint.sh
CMD /docker-entrypoint.sh
In order to use Docker, you don't need to be a root user, you just need to be inside of the docker user group.
On Linux:
If there is not already a docker group, you can create one using the command sudo groupadd docker.
Add yourself and any other users you would like to be able to access docker to this group using the command sudo usermod -aG docker [username of user].
Relog, so that Linux can re-evaluate user groups.
If you are not trying to run the command as root, but rather want to run the container as non-root, you can use the following DOCKERFILE contents (insert after FROM but before anything else.)
# Add a new user "john" with user id 8877
RUN useradd -u 8877 john
# Change to non-root privilege
USER john

How to give docker container based on scratch access to /var/run/docker.sock

I would like to use the Docker socket on the host from Go code running inside a container based on scratch.
The Dockerfile looks something like this:
FROM golang:1.12.4-alpine3.9 as builder
RUN mkdir /user && \
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
echo 'nobody:x:65534:' > /user/group
RUN apk add --no-cache ca-certificates git
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM scratch as final
COPY --from=builder /user/group /user/passwd /etc/
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /src/app /app
COPY --chown=nobody:nobody data /.local
USER nobody:nobody
ENTRYPOINT ["/app"]
The docker service itself includes a mount for the /var/run/docker.sock
Output from docker service inspect:
"Mounts": [
{
"Type": "bind",
"Source": "/var/run/docker.sock",
"Target": "/var/run/docker.sock"
}
],
Things I've tried:
touch /var/run/docker.sock on the builder and COPY --chown=nobody:nobody --from=builder /var/run /var/run in final
Different user (I refuse to run as root. It's bad practice).
Adding nobody in final to the docker group.
EDIT:
Under this configuration I get the following error as nobody as a user does not have permission to access /var/run/docker.socket
Got permission denied while trying to connect to the Docker daemon
socket at unix:///var/run/docker.sock: Get
http://%2Fvar%2Frun%2Fdocker.sock/v1.25/services: dial unix
/var/run/docker.sock: connect: permission denied
To communicate with the docker daemon you either need to run the command as root (or sudo), or your user must be a member of the docker group.
In order to use it from a non-root user and without sudo, you will need to create the docker group inside the container and add your user to that group. NOTE: the docker group inside the container must have the same GID as the actual docker group on the host.

How to run .NET Core 2 application in Docker on Linux as non-root

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

Container output directory not visible in Host

I have Dockerfile as shown here.
A script in the entrypoint creates a directory and places few artifacts.
# from base image
FROM ......
RUN mkdir -p /home/myuser
RUN groupadd -g 999 myuser &&\
useradd -r -u 999 -g myuser myuser
ENV HOME=/home/myuser
ENV APP_HOME=/home/myuser/workspace
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
RUN chown -R myuser:myuser $APP_HOME
USER myuser
ENTRYPOINT ......
I start a container for the above image as shown here
sudo docker run -v ${WORKSPACE}/output:/home/myuser/workspace/output image
I could not get the artifacts in the host machine. ${WORKSPACE}/output created with permission drwxr_xr_x
What is the process to get the container files into the host machine?
Additional Info:
My host username is kit
container user is myuser
container works perfectly fine - at the time of creating output file it throws an error that Permission denied
I tried to give full permission drwxrwxrwx to ${WORKSPACE}/output. then i could see the output files.
The permission denied error is because you are running a container with uid 999, but trying to write to a host directory that is owned by uid 1000 and only configured to allow writes by the user. You can:
chmod the directory to allow anyone to write (not recommended, but quick and easy)
update your image to match the uid/gid of your user on the host
switch to using a named volume
use an entrypoint to align the container uid/gid to that of a volume mount before starting your app
I go into a bit more detail on these in my slides here. There are also some speaker notes in there (I believe either P or S will bring them up).

Resources