Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Gontier-Julien opened this issue Aug 24, 2023 · 5 comments
Open

Comments

@Gontier-Julien
Copy link
Contributor

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
Copy link
Contributor Author

Adfditional comment on what the ldflags do:

-s    disable symbol table
-w    disable DWARF generation

@Jamesits
Copy link
Collaborator

Jamesits commented Aug 24, 2023

-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.

@Gontier-Julien
Copy link
Contributor Author

Gontier-Julien commented Aug 24, 2023

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 ?

@Jamesits
Copy link
Collaborator

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...)

@Gontier-Julien
Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants