Run SDK image as nonroot (UID 65532) for Docker Scout compliance

- apko/sdk.yaml: run-as 65532, create /home/nonroot and /app owned by nonroot
- sdk.Dockerfile: USER 65532, set HOME and DOTNET_CLI_HOME to /home/nonroot
- README/DOCKERHUB: update examples to use COPY --chown=nonroot and /app/publish

dotnet restore writes NuGet cache to $DOTNET_CLI_HOME/.nuget/packages
which is now /home/nonroot/.nuget/packages — no root needed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mathias Beaulieu-Duncan 2026-02-03 15:39:51 -05:00
parent fa20f6f0ea
commit 74bb1f6070
4 changed files with 40 additions and 26 deletions

View File

@ -46,9 +46,9 @@ Minimal .NET Docker images for production and CI/CD. Built on [Wolfi](https://wo
ARG BUILDPLATFORM
FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build
WORKDIR /source
COPY . .
WORKDIR /source/MyApp.Api
WORKDIR /app/source
COPY --chown=nonroot . .
WORKDIR /app/source/MyApp.Api
ARG TARGETARCH
RUN case "$TARGETARCH" in \
@ -56,11 +56,11 @@ RUN case "$TARGETARCH" in \
arm64) ARCH=arm64 ;; \
*) ARCH=$TARGETARCH ;; \
esac && \
dotnet publish -a $ARCH --self-contained false -o /app
dotnet publish -a $ARCH --self-contained false -o /app/publish
FROM svrnty/dotnet:runtime-invariant-10 AS final
WORKDIR /app
COPY --from=build /app .
COPY --from=build /app/publish .
USER 65532
EXPOSE 8080
ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"]
@ -72,8 +72,8 @@ ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"]
ARG BUILDPLATFORM
FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-lts AS build
WORKDIR /source
COPY . .
WORKDIR /app/source
COPY --chown=nonroot . .
ARG TARGETARCH
RUN case "$TARGETARCH" in \
@ -81,11 +81,11 @@ RUN case "$TARGETARCH" in \
arm64) ARCH=arm64 ;; \
*) ARCH=$TARGETARCH ;; \
esac && \
dotnet publish MyWorker -a $ARCH --self-contained false -o /app
dotnet publish MyWorker -a $ARCH --self-contained false -o /app/publish
FROM svrnty/dotnet:runtime-invariant-lts AS final
WORKDIR /app
COPY --from=build /app .
COPY --from=build /app/publish .
USER 65532
ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyWorker.dll"]
```

View File

@ -20,11 +20,11 @@ Minimal .NET Docker images for production and CI/CD. Built on [Wolfi](https://wo
|---------|----------|-------|-------------------|------|
| <a href="https://hub.docker.com/r/svrnty/dotnet/tags?name=runtime-" target="_blank">**runtime**</a> | ASP.NET Core runtime | No | Yes | 65532 (nonroot) |
| <a href="https://hub.docker.com/r/svrnty/dotnet/tags?name=runtime-invariant-" target="_blank">**runtime-invariant**</a> | ASP.NET Core runtime | No | No (invariant mode) | 65532 (nonroot) |
| <a href="https://hub.docker.com/r/svrnty/dotnet/tags?name=sdk-" target="_blank">**sdk**</a> | .NET SDK + bash, git, curl | Yes (bash) | Yes | root |
| <a href="https://hub.docker.com/r/svrnty/dotnet/tags?name=sdk-" target="_blank">**sdk**</a> | .NET SDK + bash, git, curl | Yes (bash) | Yes | 65532 (nonroot) |
- **runtime** - Full globalization support (ICU + tzdata). Use this for apps that need locale-aware formatting, time zones, or culture-specific behavior.
- **runtime-invariant** - No ICU or tzdata. Smallest image size. Use this for APIs that only need UTC and ordinal string comparison.
- **sdk** - Everything needed to build .NET apps. Runs as root so `dotnet restore` can write to global caches.
- **sdk** - Everything needed to build .NET apps. Uses `DOTNET_CLI_HOME=/home/nonroot` for NuGet cache — no root required. Use `COPY --chown=nonroot` to make source files writable.
## Why Wolfi?
@ -51,9 +51,9 @@ Minimal .NET Docker images for production and CI/CD. Built on [Wolfi](https://wo
ARG BUILDPLATFORM
FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build
WORKDIR /source
COPY . .
WORKDIR /source/MyApp.Api
WORKDIR /app/source
COPY --chown=nonroot . .
WORKDIR /app/source/MyApp.Api
ARG TARGETARCH
RUN case "$TARGETARCH" in \
@ -61,11 +61,11 @@ RUN case "$TARGETARCH" in \
arm64) ARCH=arm64 ;; \
*) ARCH=$TARGETARCH ;; \
esac && \
dotnet publish -a $ARCH --self-contained false -o /app
dotnet publish -a $ARCH --self-contained false -o /app/publish
FROM svrnty/dotnet:runtime-invariant-10 AS final
WORKDIR /app
COPY --from=build /app .
COPY --from=build /app/publish .
USER 65532
EXPOSE 8080
ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"]
@ -77,8 +77,8 @@ ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"]
ARG BUILDPLATFORM
FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-lts AS build
WORKDIR /source
COPY . .
WORKDIR /app/source
COPY --chown=nonroot . .
ARG TARGETARCH
RUN case "$TARGETARCH" in \
@ -86,11 +86,11 @@ RUN case "$TARGETARCH" in \
arm64) ARCH=arm64 ;; \
*) ARCH=$TARGETARCH ;; \
esac && \
dotnet publish MyApp.sln -a $ARCH --self-contained false -o /app
dotnet publish MyApp.sln -a $ARCH --self-contained false -o /app/publish
FROM svrnty/dotnet:runtime-lts AS final
WORKDIR /app
COPY --from=build /app .
COPY --from=build /app/publish .
USER 65532
EXPOSE 8080
ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.dll"]
@ -102,8 +102,8 @@ ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.dll"]
ARG BUILDPLATFORM
FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build
WORKDIR /source
COPY . .
WORKDIR /app/source
COPY --chown=nonroot . .
ARG TARGETARCH
RUN case "$TARGETARCH" in \
@ -111,11 +111,11 @@ RUN case "$TARGETARCH" in \
arm64) ARCH=arm64 ;; \
*) ARCH=$TARGETARCH ;; \
esac && \
dotnet publish MyWorker -a $ARCH --self-contained false -o /app
dotnet publish MyWorker -a $ARCH --self-contained false -o /app/publish
FROM svrnty/dotnet:runtime-invariant-10 AS final
WORKDIR /app
COPY --from=build /app .
COPY --from=build /app/publish .
USER 65532
ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyWorker.dll"]
```

View File

@ -25,7 +25,19 @@ accounts:
- username: nonroot
uid: 65532
gid: 65532
run-as: 0
run-as: 65532
paths:
- path: /home/nonroot
type: directory
uid: 65532
gid: 65532
permissions: 0o755
- path: /app
type: directory
uid: 65532
gid: 65532
permissions: 0o755
archs:
- x86_64

View File

@ -6,5 +6,7 @@ ENV DOTNET_ROOT=/usr/share/dotnet
ENV PATH="/usr/share/dotnet:${PATH}"
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV HOME=/home/nonroot
ENV DOTNET_CLI_HOME=/home/nonroot
WORKDIR /app
USER 0
USER 65532