为何Docker容器中非root用户可成功绑定小于1024的80端口?
我创建了一个使用非root用户的镜像,此前我一直认为进程无法绑定1024以下的端口(该结论来自各类教程以及StackOverflow上的大量相关问题)。但在测试时,我基于模板创建了简单的ASP.NET Core Web API,并通过VS Code生成了Dockerfile:即便VS Code的逻辑也符合上述认知,若指定5000端口生成Dockerfile会创建非root用户,若选择80端口则默认构建使用root用户的镜像。
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 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"]
随后我执行如下命令构建并运行镜像:
docker build . -t test docker run --rm -it -p 5000:80 test
镜像运行完全正常,执行top命令查看后发现dotnet进程是以appuser身份运行的。
请问绑定1024以下端口需要root权限的限制已经失效了吗?该变化是来自新版本Docker的改动,还是与.NET相关?
根据docker run官方文档,Docker默认会添加多项Linux capabilities,其中NET_BIND_SERVICE的作用是:
将套接字绑定到互联网域特权端口(端口号小于1024)。
但我通过--cap-drop=all参数删除所有capabilities后,程序依然可以正常运行。
本次测试覆盖的环境如下:
- Docker Desktop 3.5.2 (Mac OS / Windows 10)
- Linux Docker 20.10.7
解答
特权端口绑定需要特殊权限的限制并没有失效,该现象既不是Docker的通用改动,也和.NET运行时本身的功能无关,核心原因是你使用的微软官方ASP.NET Core基础镜像做了特殊配置:
- 从.NET Core 3.0开始,微软发布的官方Linux版.NET镜像已经默认通过
setcap命令给dotnet可执行文件附加了CAP_NET_BIND_SERVICE权限。该权限是绑定在二进制文件属性上的,只要执行这个文件,进程就会自动获得绑定特权端口的能力,和容器启动时配置的进程权限、运行用户是不是root都没有关系。这也是你哪怕启动容器时指定--cap-drop=all删除所有进程权限,程序依然能正常绑定80端口的原因。 - 你可以进入运行中的容器执行
getcap /usr/bin/dotnet验证,返回结果会显示/usr/bin/dotnet = cap_net_bind_service+ep,就是该配置生效的直接证据。 - 补充说明:部分新的Linux发行版会修改内核参数
net.ipv4.ip_unprivileged_port_start,将默认的1024改为更低的值甚至0,相当于全局放开特权端口限制,但该场景下你的问题核心还是镜像内置的dotnet二进制权限配置。
你可以通过原生Ubuntu镜像做对比验证:使用非root用户安装Nginx尝试绑定80端口,默认会抛出权限不足的错误,即可验证特权端口限制本身仍然生效。
内容的提问来源于stack exchange,提问作者Exploding Kitten

