From dad7066244d82f5d8b58298772fc244307961838 Mon Sep 17 00:00:00 2001 From: c41ms0n <193478517+c41ms0n@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:36:06 +0300 Subject: [PATCH] docker: BuildKit cache + CGO LTO optimization Add persistent cache mounts for the Go module cache, Go build cache, and apt so only changed packages are re-downloaded or recompiled on subsequent builds. CGO LTO is injected via `make LIBFIDO2_LDFLAGS=...` rather than ENV because the Makefile sets CGO_LDFLAGS inline in go-build-finalize, clobbering any inherited environment variable. Binary stripping is done with strip --strip-all post-build since the Makefile owns the -ldflags chain and cannot be extended without losing the -X version constants. --- build/Dockerfile | 111 ++++++++++++++++++++++++++++++++++++++++++----- deb/Dockerfile | 8 +++- 2 files changed, 106 insertions(+), 13 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 7e7ef90..4d79bf4 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,21 +1,104 @@ +# syntax=docker/dockerfile:1 ### The Deb install is just a repack of the official ProtonMail Bridge deb package with less dependencies. ### I recommend you don't use this. It's here for legacy reasons. +# ============================================================================= +# BUILD STAGE +# ============================================================================= FROM golang:1.26-trixie AS build ARG version ENV version=${version} +# --------------------------------------------------------------------------- +# LTO — why ENV CGO_LDFLAGS didn't work: +# +# The Makefile's go-build-finalize on Linux expands to: +# CGO_LDFLAGS="${LIBFIDO2_LDFLAGS}" go build ... +# That inline shell assignment *overwrites* any CGO_LDFLAGS inherited from +# the Docker environment entirely. +# +# Fix: pass LIBFIDO2_LDFLAGS on the make command line (see RUN step below). +# make CLI variables override Makefile ?= and := definitions, so our flags +# reach the CGO_LDFLAGS inline assignment verbatim. +# +# To disable LTO at build time: +# docker buildx build --build-arg CGO_LTO_FLAGS="" ... +# --------------------------------------------------------------------------- +ARG CGO_LTO_FLAGS="-flto=auto -O2" -RUN apt-get update && apt-get install -y build-essential libsecret-1-dev libfido2-dev libcbor-dev +# -trimpath: the Makefile never sets this flag, so GOFLAGS is safe here. +# Strips host machine paths from the binary → reproducible builds, no path leaks. +ENV GOFLAGS="-trimpath" -# Build +# --------------------------------------------------------------------------- +# System dependencies +# --------------------------------------------------------------------------- +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + libsecret-1-dev \ + libfido2-dev \ + libcbor-dev + +# --------------------------------------------------------------------------- +# Fetch source via BuildKit's git ADD (shallow clone at the requested ref). +# Because the entire tree arrives in one shot we cannot pre-copy go.mod, +# but the module cache mount below still avoids re-downloading dependencies. +# --------------------------------------------------------------------------- ADD https://github.com/ProtonMail/proton-bridge.git#${version} /build/ WORKDIR /build/ -RUN make build-nogui vault-editor -# ----------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# Pre-seed the Go module cache. +# This layer is invalidated only when go.mod / go.sum change (dep upgrades). +# On pure source changes the cache is warm and this step is near-instant. +# --------------------------------------------------------------------------- +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + go mod download -x +# --------------------------------------------------------------------------- +# Compile. +# +# Cache mounts: +# /go/pkg/mod — downloaded module zips (read, shared) +# /root/.cache/go-build — incremental .a object cache (read-write, locked) +# +# make variable overrides: +# LIBFIDO2_LDFLAGS — appends LTO flags to the CGO_LDFLAGS inline assignment +# in go-build-finalize without touching any other flags. +# Default Makefile value: -lfido2 -lcbor -lssl -lcrypto +# +# Why not -s -w here: +# The Makefile builds its own -ldflags chain: +# BUILD_FLAGS += -ldflags '${GO_LDFLAGS}' +# GO_LDFLAGS carries the -X version/revision constants computed at build +# time. Overriding GO_LDFLAGS on the CLI loses those constants. +# GOFLAGS=-ldflags="-s -w" is silently ignored when the Makefile already +# passes -ldflags on the go build command line (last -ldflags wins in go). +# → We strip the binaries in the next step instead. +# --------------------------------------------------------------------------- +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + make build-nogui vault-editor \ + BUILD_ENV=prod \ + LIBFIDO2_LDFLAGS="-lfido2 -lcbor -lssl -lcrypto ${CGO_LTO_FLAGS}" + +# --------------------------------------------------------------------------- +# Strip debug symbols — equivalent to go -ldflags="-s -w". +# Typically saves 25-35 % on binary size. +# Remove this RUN if you need delve / runtime stack traces. +# --------------------------------------------------------------------------- +RUN strip --strip-all \ + /build/proton-bridge \ + /build/bridge \ + /build/vault-editor + +# ============================================================================= +# RUNTIME STAGE — minimal Debian image, no build toolchain +# ============================================================================= FROM debian:trixie-slim LABEL maintainer="Simon Felding " @@ -29,25 +112,31 @@ EXPOSE 143/tcp WORKDIR /protonmail COPY gpgparams entrypoint.sh /protonmail/ -# Copy protonmail -COPY --from=build /build/bridge /protonmail/ -COPY --from=build /build/proton-bridge /protonmail/ -COPY --from=build /build/vault-editor /protonmail/ -RUN apt-get update \ +COPY --from=build /build/bridge /protonmail/ +COPY --from=build /build/proton-bridge /protonmail/ +COPY --from=build /build/vault-editor /protonmail/ + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update \ && apt-get install -y --no-install-recommends \ socat pass libsecret-1-0 libfido2-1 ca-certificates procps \ && case "${PTY_TOOL}" in \ dtach) apt-get install -y --no-install-recommends dtach ;; \ abduco) apt-get install -y --no-install-recommends abduco ;; \ reptyr) apt-get install -y --no-install-recommends reptyr ;; \ - *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported values are: dtach, abduco, reptyr." >&2; exit 1 ;; \ + *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported: dtach, abduco, reptyr." >&2 ; exit 1 ;; \ esac \ && chmod +x /protonmail/entrypoint.sh \ && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \ - CMD /bin/bash -c "true < /dev/tcp/localhost/25 && true < /dev/tcp/localhost/143 && true < /dev/tcp/localhost/1025 && true < /dev/tcp/localhost/1143" + CMD /bin/bash -c \ + "true < /dev/tcp/localhost/25 \ + && true < /dev/tcp/localhost/143 \ + && true < /dev/tcp/localhost/1025 \ + && true < /dev/tcp/localhost/1143" ENTRYPOINT ["/protonmail/entrypoint.sh"] CMD ["run"] diff --git a/deb/Dockerfile b/deb/Dockerfile index 8aa81f6..0df10de 100644 --- a/deb/Dockerfile +++ b/deb/Dockerfile @@ -31,13 +31,17 @@ RUN apt-get update \ dtach) apt-get install -y --no-install-recommends dtach ;; \ abduco) apt-get install -y --no-install-recommends abduco ;; \ reptyr) apt-get install -y --no-install-recommends reptyr ;; \ - *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported values are: dtach, abduco, reptyr." >&2; exit 1 ;; \ + *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported: dtach, abduco, reptyr." >&2 ; exit 1 ;; \ esac \ && chmod +x /protonmail/entrypoint.sh \ && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \ - CMD /bin/bash -c "true < /dev/tcp/localhost/25 && true < /dev/tcp/localhost/143 && true < /dev/tcp/localhost/1025 && true < /dev/tcp/localhost/1143" + CMD /bin/bash -c \ + "true < /dev/tcp/localhost/25 \ + && true < /dev/tcp/localhost/143 \ + && true < /dev/tcp/localhost/1025 \ + && true < /dev/tcp/localhost/1143" ENTRYPOINT ["/protonmail/entrypoint.sh"] CMD ["run"]