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

Building Static Binaries for multi platform deployment. #9

Open
1 of 3 tasks
Anunayj opened this issue Feb 27, 2021 · 8 comments
Open
1 of 3 tasks

Building Static Binaries for multi platform deployment. #9

Anunayj opened this issue Feb 27, 2021 · 8 comments

Comments

@Anunayj
Copy link
Contributor

Anunayj commented Feb 27, 2021

Static Binaries for each platform can be built using:

Linux

Static Binaries for Linux can be made using the following Dockerfile (or running the commands in Dockerfile manually).

# DOCKER_BUILDKIT=1 docker build -f static.Dockerfile -o build/ . 
FROM golang:alpine AS base
RUN apk update && apk add linux-headers gcc make perl musl-dev expat-dev openssl-libs-static openssl-dev


#Install Unbound
FROM base as setup-unbound
WORKDIR /tmp
RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.13.1.tar.gz && tar -xzf unbound-1.13.1.tar.gz
WORKDIR /tmp/unbound-1.13.1
RUN ./configure --prefix=/opt/install
RUN make && make install

FROM base as builder
#Install Dependencies
COPY --from=setup-unbound /opt/install/ /usr/local
#Optionally export this layer and cache this permanently somewhere.

#Copy stuff overgo mod download
WORKDIR /tmp/dane
COPY go.mod /tmp/dane/go.mod
RUN go mod download
#Will allow caching dependencies in layers.

COPY . /tmp/dane/
WORKDIR /tmp/dane/cmd/letsdane
#Build Static
RUN go build -tags unbound --ldflags '-extldflags "-lunbound -lssl -lcrypto -static"'

FROM scratch
COPY --from=builder /tmp/dane/cmd/letsdane/letsdane /
ENTRYPOINT [ "letsdane" ]

Use DOCKER_BUILDKIT=1 docker build -f static.Dockerfile -o build/ . to export the binary into build directory.

Running this first time can take >500 seconds (Mostly because of all the compiling that needs to be done for unbound and openssl), The Image layer can be exported to Dockerhub and used repeatedly if necessary. Subsequent builds should take <100 seconds.

Todo:

  • Document building static binaries for Linux
  • Document building static binaries for Windows
  • Document building static binaries for MacOS
@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 27, 2021

This Dockerfile can be used to automatically make builds using GitHub Actions, though I'm unsure how I should deal with the dependencies part, Should I export that as a Image and keep it on Dockerhub (and use FROM anunayj/go-alpine-unbound) or should I keep it like this and let the cache do it's job. (GitHub Actions evicts cache after 7 days of inactivity).

@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 27, 2021

Also I have no idea what was causing the problems compiling earlier when i was using a lxd container. I guess I installed unbound from apk and then built it from source causing a unholy mess.
I really feel like Alpine packagers should include static library (.a) for libraries in their -dev packages. Specially considering libmusl is used for static binaries, and their distribution is one of they few that implements libmusl.

@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 27, 2021

Alternatively to save time/or if you are lazy to compile openssl this can be used.
(Remember you will basically be trusting me to have shipped a exploit-free version of unbound library)

FROM anunayj/golang-libunbound@sha256:4db0797175be0d38f0a65f81517d1862a72c745cfb42d53b09aed477f00d6e5d as builder
#Install Dependendencies
WORKDIR /tmp/dane
COPY go.mod /tmp/dane/go.mod
RUN go mod download
#Will allow caching dependencies in layers.

COPY . /tmp/dane/
WORKDIR /tmp/dane/cmd/letsdane
#Build Static
RUN go build -tags unbound --ldflags '-extldflags "-lunbound -lssl -lcrypto -static"'

FROM scratch
COPY --from=builder /tmp/dane/cmd/letsdane/letsdane /
ENTRYPOINT [ "letsdane" ]

Sources for intermediate image here https://gist.github.com/Anunayj/f58463793ef3902eb4d0f4a24ce8b875

@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 27, 2021

I have no idea how one would go about compiling for windows and mac, go does allow you to specify to build for windows or darwin, but how would that work with unbound? GitHub actions does provide both windows and macOS images to run stuff on which can be used to make binaries, but I don't have a MacOS machine to test stuff on, I'll try messing about on my windows machine later.

P.S. We really need a Windows subsystem for Linux haha.

@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 27, 2021

Also are https://www.openssl.org/source/openssl-1.1.1j.tar.gz and https://www.nlnetlabs.nl/downloads/unbound/unbound-1.13.1.tar.gz updated with appropriate security fixes if there is one?

@buffrr
Copy link
Owner

buffrr commented Feb 27, 2021

Should I export that as a Image and keep it on Dockerhub (and use FROM anunayj/go-alpine-unbound) or should I keep it like this and let the cache do it's job. (GitHub Actions evicts cache after 7 days of inactivity).

Building static binaries isn't a frequent process if it's only done for every release. It's okay to leave it to build from source even if takes a bit of time. It's unfortunate that alpine doesn't include *.a libs. For openssl it seems there is a package on alpine openssl-libs-static which i found here https://pkgs.alpinelinux.org/package/edge/main/x86/openssl-libs-static. It could save time if we did apk add openssl-dev openssl-libs-static instead it of building openssl (i didn't test this)

I have no idea how one would go about compiling for windows and mac, go does allow you to specify to build for windows or darwin, but how would that work with unbound? GitHub actions does provide both windows and macOS images to run stuff on which can be used to make binaries, but I don't have a MacOS machine to test stuff on, I'll try messing about on my windows machine later.

Unbound is an optional dependency so letsdane can be built on most OSs with just go build (assuming the user has a validating resolver like hsd).

building on macOS with unbound is pretty simple (shared libs)

brew install unbound
git clone ... 
go build -tags unbound

To bundle unbound in the same binary, I built it this way (on MBP running Big Sur). I just installed unbound using brew (so it's built with some other dependencies that we probably don't need like nghttp2) but here's a quick way to build it:

macos prefers dynamic libraries so if there is a dynamic lib and a static lib in the same directory it will pick the dynamic one. So I specified the path for each .a lib

CGO_LDFLAGS="/usr/local/opt/unbound/lib/libunbound.a \
             /usr/local/opt/openssl/lib/libssl.a \
             /usr/local/opt/openssl/lib/libcrypto.a \
             /usr/local/opt/nghttp2/lib/libnghttp2.a \
             /usr/local/opt/libevent/lib/libevent.a" go build -tags unbound

it's not clear to me yet if that's the best way to do it.

Also are https://www.openssl.org/source/openssl-1.1.1j.tar.gz and https://www.nlnetlabs.nl/downloads/unbound/unbound-1.13.1.tar.gz updated with appropriate security fixes if there is one?

Those are links to the latest stable versions. They must be updated if new versions come up. Unbound includes a link for latest version https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz which is always updated (but this probably wouldn't be very stable if they include a new dependency or change something that affects compatibility)

@Anunayj
Copy link
Contributor Author

Anunayj commented Feb 28, 2021

Building static binaries isn't a frequent process if it's only done for every release. It's okay to leave it to build from source even if takes a bit of time. It's unfortunate that alpine doesn't include *.a libs. For openssl it seems there is a package on alpine openssl-libs-static which i found here https://pkgs.alpinelinux.org/package/edge/main/x86/openssl-libs-static. It could save time if we did apk add openssl-dev openssl-libs-static instead it of building openssl (i didn't test this)

Just tested it, and it works, updated the dockerfile.

@Anunayj
Copy link
Contributor Author

Anunayj commented Mar 3, 2021

I tried making binaries for macos using this command

C_INCLUDE_PATH="$(brew --prefix)/include" LIBRARY_PATH="$(brew --prefix)/lib" CGO_LDFLAGS="$(brew --prefix)/lib/libunbound.a \
  $(brew --prefix)/lib/libssl.a \
  $(brew --prefix)/lib/libcrypto.a \
  $(brew --prefix)/lib/libnghttp2.a \
  $(brew --prefix)/lib/libevent.a" go build -tags unbound ./cmd/letsdane

but seems like it still dynamically linked the binary?

linux-vdso.so.1 (0x00007fff935e3000)
	libunbound.so.8 => not found
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5da032b000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5da0139000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f5da035f000)

if I use

C_INCLUDE_PATH="$(brew --prefix)/include" CGO_LDFLAGS="$(brew --prefix)/lib/libunbound.a \
  $(brew --prefix)/lib/libssl.a \
  $(brew --prefix)/lib/libcrypto.a \
  $(brew --prefix)/lib/libnghttp2.a \
  $(brew --prefix)/lib/libevent.a" go build -tags unbound ./cmd/letsdane

I get

go: downloading github.com/buffrr/hsig0 v0.0.0-20200928223456-eca10c3b5481
go: downloading github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0
go: downloading github.com/miekg/dns v1.1.31
go: downloading golang.org/x/net v0.0.0-20190923162816-aa69164e4478
go: downloading golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
go: downloading golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
go: downloading github.com/miekg/unbound v0.0.0-20180419064740-e2b53b2dbcba
# github.com/miekg/unbound
/usr/bin/ld: cannot find -lunbound
collect2: error: ld returned 1 exit status
Error: Process completed with exit code 2.

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