How can a non-root Docker container use ICMP with a scratch image?

0
2
Asked By MellowCedar47 On

I'm deploying Gatus on ECS using the EC2 launch type. It monitors various domains and paths, including checks that rely on ICMP. To improve the image's security, I changed the runtime to use a numeric non-root user and a scratch base image. After deploying locally and to ECS, all ICMP checks failed.

I initially suspected that the missing /etc/passwd file or the non-root user was preventing access to NET_RAW. I added NET_RAW to the task definition, but that did not resolve the issue. Using Alpine for the runtime seems like one option, although I'd prefer to keep the image as small as possible.

Is there a way to keep using scratch while allowing Gatus to perform ICMP checks? Here is the relevant Dockerfile:

FROM golang:alpine AS builder
RUN apk --update add ca-certificates
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod tidy
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -trimpath -ldflags="-s -w" -o gatus .

FROM scratch AS runtime
USER 1001:1001
WORKDIR /app
COPY --from=builder /app/gatus /app/
COPY --from=builder /app/config.yaml /app/config/config.yaml
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 8080
ENTRYPOINT ["./gatus"]

3 Answers

Answered By PixelHarbor8 On

The missing /etc/passwd file is not what prevents ICMP from working. A numeric USER such as 1001:1001 works in a scratch image; it just means the process has no username to resolve. Some libraries that call os/user may care, but that is separate from ICMP.

Adding NET_RAW to the container’s capabilities only places the capability in the permitted or bounding sets. A non-root process still needs it in its effective set. File capabilities or ambient capabilities can provide that, but file capabilities are awkward with multi-stage builds because Docker’s COPY may not preserve the security.capability extended attribute. A capability applied in the builder stage can therefore disappear when the binary is copied into the scratch stage.

A simpler solution is to avoid raw sockets. Gatus uses pro-bing, which can use unprivileged ICMP on Linux when the kernel permits it. Configure the ping group range in the ECS container definition rather than adding NET_RAW:

"systemControls": [{"namespace": "net.ipv4.ping_group_range", "value": "0 2147483647"}]

For local testing, the equivalent Docker option is:

docker run --sysctl net.ipv4.ping_group_range="0 2147483647" ...

With unprivileged ICMP enabled, a scratch image and non-root user can still work without NET_RAW.

MellowCedar47 -

Thanks—this is more detail than I’m familiar with, but I’ll try the unprivileged ICMP setting. I may also test a distroless image as a simpler fallback.

Answered By CopperLynx62 On

You can also copy an /etc/passwd file into scratch if an application needs to resolve UID 1001 to a username, but that will not grant ICMP permissions. Treat the user lookup issue and the socket capability issue as separate problems.

Answered By QuietMaple23 On

If you do not specifically need a completely empty filesystem, try a static non-root distroless image such as gcr.io/distroless/static:nonroot. It is still very small and includes a suitable non-root user, certificates, and basic system files. That can avoid some of the rough edges of scratch.

Depending on the exact image variant, the CA bundle may already be included, so you may not need to copy /etc/ssl/certs/ca-certificates.crt yourself. Verify the image contents and your application’s certificate requirements before removing that COPY step.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.