dns-over-https icon indicating copy to clipboard operation
dns-over-https copied to clipboard

Build the docker image from scratch, for a lighter docker image?

Open Gontier-Julien opened this issue 1 year ago • 5 comments

It just a proposal for now, but it being working perfectly, and i run it everyday with my own docker image.

Currently the docker image is build like this:

FROM golang:alpine AS build-env

RUN apk add --no-cache git make

WORKDIR /src
ADD . /src
RUN make doh-server/doh-server

FROM alpine:latest

COPY --from=build-env /src/doh-server/doh-server /doh-server

ADD doh-server/doh-server.conf /doh-server.conf

RUN sed -i '$!N;s/"127.0.0.1:8053",\s*"\[::1\]:8053",/":8053",/;P;D' /doh-server.conf

EXPOSE 8053

ENTRYPOINT ["/doh-server"]
CMD ["-conf", "/doh-server.conf"]

My proposition. To build the final image from 'scratch':

FROM golang:alpine AS build-env

RUN apk add --no-cache git make

WORKDIR /src
ADD . /src
RUN make doh-server/doh-server

FROM scratch

COPY --from=build-env /src/doh-server/doh-server /doh-server

ADD doh-server/doh-server.conf /doh-server.conf //Comment. this can also be changed to a COPY instead of a ADD

RUN sed -i '$!N;s/"127.0.0.1:8053",\s*"\[::1\]:8053",/":8053",/;P;D' /doh-server.conf

COPY --from=build-env /etc/passwd /etc/passwd
COPY --from=build-env /etc/group /etc/group
# run as non-privileged user
USER nobody:nobody

EXPOSE 8053

ENTRYPOINT ["/doh-server"]
CMD ["-conf", "/doh-server.conf"]

Advantage:

  • Since it a go image it can be shipped like this
  • the resulting image will be smaller
  • less attack surface since there will only be the go application.

Disadvantage:

  • currently no logs output (can be figured out i think)

The image could be also made smaller by adding '-s -w' to the 'ldflags' like this:

CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w"

I can make a pull request if your okay with those changes, but i just wanted to discuss it with you first ^^ I've been running my image for 9month straight with no issues on my side You can check my current docker image and GitHub for it.

Gontier-Julien avatar Aug 24 '23 14:08 Gontier-Julien

Adfditional comment on what the ldflags do:

-s    disable symbol table
-w    disable DWARF generation

Gontier-Julien avatar Aug 24 '23 14:08 Gontier-Julien

-ldflags "-s -w"

I'm perfectly fine with this. Just make a PR and I'll be happy to merge it.

If you have strong space requirements you can even UPX the executables to trade cold start time for some (usually 50%) additional space savings (but I don't recommend doing this for everyone).

Disabling CGO

Disabling CGO is usually fine for most web applications in the wild, but this is a DNS server, and disabling CGO slightly changes how the DNS client in netgo behaves (from directly calling into the libc to mimicking the behavior in pure Go). This change has been causing unexpected problems all the time and I really don't recommend doing this.

And there will not be any libc version incompatibilities because we invented Docker to solve exactly this problem.

Starting from scratch

This is another popular thing that I don't recommend. 3 reasons:

  • There is a high chance you forget to add something that silently changes the behavior of the program in unexpected ways. For example, you forget to copy the trusted CA certificates which might cause problems when the program is connecting to a HTTPS server.
  • You lose the capability to live debug in the container.
  • Others cannot easily extend the image by FROM image; RUN .... For example, it is now 10x harder to add dig command to the container for health checks (this is a real use case: I deploy it in a cluster and have rolling updates).

Starting from scratch will save you at most a few megabytes, plus base image layers will be shared between images, so I personally feel it has more cons than pros.

Running as nobody

While running as non-root comes with some security defaults that makes the system slightly more secure, there are multiple problems:

  • Running as non-root with a host volume causes unexpected permission mappings. A lot people run this software not on dedicated application servers but their own computers or shared workstations, and we cannot reasonably assume any UID/GID will be mapped to a certain permission set on the host.
  • Not every Linux distro use the same UID/GID for nobody, which makes the first problem even worse.
  • If Docker rootless mode is configured, we cannot assume how many subUIDs/subGIDs has been allocated to a certain user namespace. Using a large UID/GID in the container might cause troubles.

There are modern ways to achieve the same security enhancements. Recommended:

  • Use docker run --cap-drop ... to remove non-essential capabilities (our documentation does not have an example; if you have time, do help add an example here!)
  • Set up Docker/Podman rootless mode.

Jamesits avatar Aug 24 '23 14:08 Jamesits

I'll make a pr now for the ldflags 👍🏻

Also other question since we at it, is the latest tag based on commit or latest stable version ?

Gontier-Julien avatar Aug 24 '23 15:08 Gontier-Julien

Currently based on master, since the automated build is set up like 2 days ago and we don't have a tag newer than that yet. (Plus we don't have the correct GitHub workflow for a Git tag...)

Jamesits avatar Aug 24 '23 15:08 Jamesits

Alright so when a new version will be up the will be a tag for it then? ^^

Gontier-Julien avatar Aug 24 '23 15:08 Gontier-Julien

I guess this can be safely closed for now.

Jamesits avatar Jul 18 '24 08:07 Jamesits