From 5f7e1f65f4d0de17011dfad5666f49a033397143 Mon Sep 17 00:00:00 2001 From: Cam Hutchison Date: Tue, 23 Jul 2024 10:19:09 +1000 Subject: [PATCH] buildbox: Add new cross-compiling buildbox for Teleport (#44130) * buildbox: Add new cross-compiling buildbox for Teleport Add Dockerfiles and make targets to build a new buildbox that has a set of cross compilers for the four architectures we target for Linux - amd64, arm64, 386, and arm (32-bit). The buildbox also contains the third-party C libraries that Teleport needs to statically link against for a full set of features, again compiled for each target architecture. The cross compilers are built using crosstool-NG. The compilers must have a "vendor" field in the "target triple" of "unknown" (default). Setting it to anything else means rust does not find the cross compilers properly. Using "unknown" causes the triple to match the rust target. (This may ultimately be unnecessary to make these match, but is needed if we have the boring crate build boringssl itself - see last paragraph). Two container images are produced: * `buildbox-thirdparty` - a base image that contains the third-party tools (compilers, etc) and libraries that we build from source. Once build, this should never need to be rebuilt unless we change the version of one of the components it builds. * `buildbox-ng` - the buildbox used to build Teleport. It copies out the third-party components from the previous image and installs whatever other tools are needed by the build, whether from the distro archive or directly from upstream (such as Go and Rust compilers). Additionally, the three intermediate build stages of `buildbox-thirdparty` can be built for working on the buildbox; `ctng`, `compilers` and `tplibs`. `buildbox-ng` will become just `buildbox` once it can replace the others entirely, at which point those others will be removed. Currently the build tools to build a FIPS version of teleport is not in the buildbox. That has been troublesome as the rust boring crate wants to build boringssl itself and it is not being done correctly with the cross compilers. Further work is needed here, likely building boringssl ourselves and pointing the rust boring crate at the pre-built libraries. * buildbox: Add libelf.pc for pkg-config/pkconf Add a libelf.pc file taken from elfutils and minorly adjusted for how we install it separately. libelf was taken out of elfutils but did not take the libelf.pc file, so we do that ourselves. This will allow the build to use pkg-config/pkconf for selecting the libraries to link teleport to as some later versions of libelf also need `-lzstd`, such as this buildbox but also ubuntu-24.04. * buildbox: Set prefix for zstd, add sh-cross-vars target Set `PREFIX` when building `zstd` to ensure it's pkg-config file has the correct prefix set in it, otherwise it has `-L/usr/local/lib` which we do not want for cross-compiling. Add a `sh-cross-vars` target to echo the cross-compiling vars for an architecture, in a form that can be sourced by the shell: eval $(make -s -f cross-compile.mk ARCH=arm64 sh-cross-vars) This will set up your shell for cross-compiling for the given architecture, which is useful when working on the libraries in the buildbox. * Address Roman's review comments * Rename ctng to crosstoolng throughout * Rename tplibs.mk to thirdparty-libs.mk * Fix spelling mistakes. * Address Jakub's review comments * Remove `--hostname $(HOSTNAME)` when running containers. * Remove `--volume /tmp:/tmp` when running containers. * Rename bbcommon.mk to buildbox-common.mk * buildbox: Fix up broken renaming Renaming things was done poorly in the last few commits. Fix all that up. * buildbox: Use gold linker for arm64 builds Use the `gold` linker when building arm64 binaries, as Enterprise Teleport will not build with the binutils (bfd) linker; it gives numerous errors when linking of the form: something.rs: (.text.unlikely._XXX): relocation truncated to fit: R_AARCH64_CALL26 against symbol ... These errors do not occur when using the `gold` linker, which is already included and built by crosstool-NG. * buildbox: Change crosstool arm tuple to match rust Tweak the crosstool-NG configuration for the arm cross-compiler so that the tuple for it matches the tuple currently used in the build by rust for the same target. This is mostly to cause less confusion as there's no real need for them to be different. * buildbox: Add support for rust cross-compiling Add environment variables so that cargo can find the appropriate architecture-specific linker when cross-compiling. This is needed now as we have a rust binary (fdpass-teleport) in the build, as opposed to just rust compiled to a library linked to Go code. Rust does not have a cross platform linker and relies an a linker in the toolchain for the target architecture. To make this work without needing per-architecture setup when building, all of the toolchain binaries are symlinked into /opt/thirdparty/host/bin so they are all in the path. Because each of the binaries is prefixed with the target tuple, there are no name clashes. * buildbox: Add stages to buildbox Dockerfile for better caching Download Go and Rust in separate stages and copy their installation into the final container image. This helps as these stages no longer have unrelated layers before them so they cache better. The Go and Rust stages should only need to be rebuilt if the versions of the compilers change, or some other aspect of the installation of the compilers change. Previously adding a new package to be installed would cause Go and Rust to be re-installed. This no longer happens. * buildbox: Remove include of buildbox-common.mk in thirdparty-libs.mk Remove the include of `buildbox-common.mk` from `thirdparty-libs.mk` as it is included by `cross-compile.mk`. This caused some duplicate target warnings when make ran. * build: Add support for buildbox-ng to Makefile Add supoprt for building in buildbox-ng to the build by including the cross-compiling definitions if we are running in that buildbox and gating some older cross-compiling definitions from being used. * build: Add release targets using buildbox-ng Add a set of targets to build releases using buildbox-ng instead of the standard buildboxes. Ultimately these targets will be removed when the old buildboxes are removed. These new targets are temporary until that happens supporting testing of the new buildbox. * buildbox: Add teleport user, expand comments Add the teleport user (1000:1000) to the buildbox for running in CI where a repository is checked-out inside the container instead of mounting a volume on to /home/teleport. Make that home directory world-writable so that it can still be used if 1000:1000 cannot be used for some reason. Expand and clean up comments. Re-order ENV vars a little for better grouping. Put Rust and Go temp directories under /tmp/build so a single volume can be mounted there for persistent caches across runs. * build: Fix invocation of pkg-config in $(shell ...) Explicitly set `PKG_CONFIG_PATH` when running `$(PKGCONF)` as when running that in a `$(shell ...)` expression, variables exported in the Makefile are not exported for that shell, so `$(PKGCONF)` does not see `PKG_CONFIG_PATH`. GNU make 4.4 changes this so that exported variables are exported to `$(shell ...)` expressions, but the buildbox has GNU make 4.3. This was causing libbpf to not be detected properly so was building teleport without bpf. --- Makefile | 18 +- build.assets/Makefile | 131 ++++++++++ build.assets/buildbox/Dockerfile | 178 +++++++++++++ build.assets/buildbox/Dockerfile-thirdparty | 139 ++++++++++ build.assets/buildbox/buildbox-common.mk | 89 +++++++ build.assets/buildbox/cross-compile.mk | 50 ++++ .../crosstoolng-configs/386.defconfig | 14 ++ .../crosstoolng-configs/amd64.defconfig | 15 ++ .../crosstoolng-configs/arm.defconfig | 18 ++ .../crosstoolng-configs/arm64.defconfig | 18 ++ build.assets/buildbox/crosstoolng.mk | 81 ++++++ .../buildbox/pkgconfig/libcrypto-static.pc | 12 + build.assets/buildbox/pkgconfig/libelf.pc | 14 ++ .../buildbox/pkgconfig/libfido2-static.pc | 13 + build.assets/buildbox/thirdparty-libs.mk | 237 ++++++++++++++++++ build.assets/images.mk | 3 + common.mk | 9 +- 17 files changed, 1031 insertions(+), 8 deletions(-) create mode 100644 build.assets/buildbox/Dockerfile create mode 100644 build.assets/buildbox/Dockerfile-thirdparty create mode 100644 build.assets/buildbox/buildbox-common.mk create mode 100644 build.assets/buildbox/cross-compile.mk create mode 100644 build.assets/buildbox/crosstoolng-configs/386.defconfig create mode 100644 build.assets/buildbox/crosstoolng-configs/amd64.defconfig create mode 100644 build.assets/buildbox/crosstoolng-configs/arm.defconfig create mode 100644 build.assets/buildbox/crosstoolng-configs/arm64.defconfig create mode 100644 build.assets/buildbox/crosstoolng.mk create mode 100644 build.assets/buildbox/pkgconfig/libcrypto-static.pc create mode 100644 build.assets/buildbox/pkgconfig/libelf.pc create mode 100644 build.assets/buildbox/pkgconfig/libfido2-static.pc create mode 100644 build.assets/buildbox/thirdparty-libs.mk diff --git a/Makefile b/Makefile index 15047ec116514..308dabf20405c 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,13 @@ ARCH ?= $(GO_ENV_ARCH) FIPS ?= RELEASE = teleport-$(GITTAG)-$(OS)-$(ARCH)-bin +# If we're building inside the cross-compiling buildbox, include the +# cross compilation definitions so we select the correct compilers and +# libraries. +ifeq ($(BUILDBOX_MODE),cross) +include build.assets/buildbox/cross-compile.mk +endif + # Include common makefile shared between OSS and Ent. include common.mk @@ -254,18 +261,21 @@ TEST_LOG_DIR = ${abspath ./test-logs} # Set CGOFLAG and BUILDFLAGS as needed for the OS/ARCH. ifeq ("$(OS)","linux") -# True if $ARCH == amd64 || $ARCH == arm64 ifeq ("$(ARCH)","arm64") - ifeq (,$(IS_NATIVE_BUILD)) - CGOFLAG += CC=aarch64-linux-gnu-gcc - endif +ifneq ($(BUILDBOX_MODE),cross) +ifeq (,$(IS_NATIVE_BUILD)) +CGOFLAG += CC=aarch64-linux-gnu-gcc +endif +endif else ifeq ("$(ARCH)","arm") CGOFLAG = CGO_ENABLED=1 # ARM builds need to specify the correct C compiler +ifneq ($(BUILDBOX_MODE),cross) ifeq (,$(IS_NATIVE_BUILD)) CC=arm-linux-gnueabihf-gcc endif +endif # Add -debugtramp=2 to work around 24 bit CALL/JMP instruction offset. # Add "-extldflags -Wl,--long-plt" to avoid ld assertion failure on large binaries diff --git a/build.assets/Makefile b/build.assets/Makefile index bc9270723cb77..097e313293744 100644 --- a/build.assets/Makefile +++ b/build.assets/Makefile @@ -107,6 +107,64 @@ build-binaries-fips: buildbox-centos7-fips webassets make -C $(SRCDIR)/e ADDFLAGS='$(ADDFLAGS)' VERSION=$(VERSION) GITTAG=v$(VERSION) PIV=$(PIV) FIPS=yes clean full # +# Build the buildbox thirdparty components. This rarely needs to be rebuilt and is +# slow to build, so it is done separately from the main buildbox +# +.PHONY: buildbox-thirdparty +buildbox-thirdparty: + docker buildx build \ + --cache-from $(BUILDBOX_THIRDPARTY) \ + --cache-to type=inline \ + $(if $(PUSH),--push,--load) \ + --tag $(BUILDBOX_THIRDPARTY) \ + -f buildbox/Dockerfile-thirdparty \ + buildbox + +# +# A generic build rule to build a stage of Dockerfile-thirdparty based +# on the $(STAGE) variable. These stage builds are used for development +# of the thirdparty buildbox, whether to configure crosstool-NG +# (see config/buildbox-ng), or when adding additional third party +# libraries using either the compilers stage or libs stage. +# +.PHONY: buildbox-thirdparty-stage +buildbox-thirdparty-stage: + docker buildx build \ + --load \ + --tag buildbox-thirdparty-$(STAGE):$(BUILDBOX_VERSION) \ + -f buildbox/Dockerfile-thirdparty \ + --target $(STAGE) \ + buildbox + +.PHONY: buildbox-thirdparty-crosstoolng +buildbox-thirdparty-crosstoolng: STAGE=crosstoolng +buildbox-thirdparty-crosstoolng: buildbox-thirdparty-stage + +.PHONY: buildbox-thirdparty-compilers +buildbox-thirdparty-compilers: STAGE=compilers +buildbox-thirdparty-compilers: buildbox-thirdparty-stage + +.PHONY: buildbox-thirdparty-libs +buildbox-thirdparty-libs: STAGE=libs +buildbox-thirdparty-libs: buildbox-thirdparty-stage + +# +# Build the buildbox-ng using the pre-built third party components from the +# buildbox-thirdparty image +# +.PHONY: buildbox-ng +buildbox-ng: + docker buildx build \ + --build-arg THIRDPARTY_IMAGE=$(BUILDBOX_THIRDPARTY) \ + --build-arg GOLANG_VERSION=$(GOLANG_VERSION) \ + --build-arg RUST_VERSION=$(RUST_VERSION) \ + --cache-from $(BUILDBOX_NG) \ + --cache-to type=inline \ + $(if $(PUSH),--push,--load) \ + --tag $(BUILDBOX_NG) \ + -f buildbox/Dockerfile \ + buildbox + # Builds a Docker container which is used for building official Teleport binaries # If running in CI and there is no image with the buildbox name:tag combination present locally, # the image is pulled from the Docker repository. If this pull fails (i.e. when a new Go runtime is @@ -381,6 +439,52 @@ enter-root: buildbox -e HOME=$(SRCDIR)/build.assets -w $(SRCDIR) $(BUILDBOX) /bin/bash # +# Reconfigure crosstool-NG for the given ARCH via its menuconfig system. +# e.g. +# $ make config/buildbox-ng CROSSTOOLNG_ARCH=arm64 +# +# After saving and exiting in the menuconfig, you should have an updated +# defconfig file in build.assets/buildbox/crosstoolng-configs +# +# You need to have a local copy of the configuration container image +# created with: make buildbox-thirdparty-crosstoolng +# +.PHONY: config/buildbox-ng +config/buildbox-ng: + @: $(or $(CROSSTOOLNG_ARCH),$(error CROSSTOOLNG_ARCH variable must be set)) + @: $(or $(wildcard buildbox/crosstoolng-configs/$(CROSSTOOLNG_ARCH).defconfig),\ + $(error No config for arch $(CROSSTOOLNG_ARCH))) + docker run -it --rm \ + --volume $(shell pwd)/..:/home/teleport \ + --workdir /home/teleport \ + buildbox-thirdparty-crosstoolng:$(BUILDBOX_VERSION) \ + make -C build.assets/buildbox -f crosstoolng.mk crosstoolng-menuconfig \ + ARCH=$(CROSSTOOLNG_ARCH) + + +# +# Starts a shell in the buildbox-ng container +# We don't use $(DOCKERFLAGS) as it contains stuff for the old buildbox which is +# not relevant or incorrect for this one. +.PHONY: enter/buildbox-ng +enter/buildbox-ng: + docker run -it --rm \ + --volume $(shell pwd)/..:/home/teleport \ + --workdir /home/teleport \ + --user $(shell id -u):$(shell id -g) \ + $(BUILDBOX_NG) + +# +# Starts a root shell in the buildbox-ng container +# +.PHONY: enter-root/buildbox-ng +enter-root/buildbox-ng: + docker run -it --rm \ + --volume $(shell pwd)/..:/home/teleport \ + --workdir /home/teleport \ + --user 0:0 \ + $(BUILDBOX_NG) +# # Starts shell inside the centos7 container # .PHONY:enter/centos7 @@ -487,6 +591,33 @@ release: $(BUILDBOX_TARGET) docker run $(DOCKERFLAGS) $(NOROOT) $(BUILDBOX) \ /usr/bin/make $(RELEASE_TARGET) -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(GOLANG_VERSION) FIDO2=$(FIDO2) PIV=$(PIV) REPRODUCIBLE=yes + +.PHONY: release-ng-amd64 release-ng-arm64 release-ng-386 release-ng-arm +release-ng-amd64: + $(MAKE) release-ng ARCH=amd64 FIDO2=yes PIV=yes +release-ng-arm64: + $(MAKE) release-ng ARCH=arm64 FIDO2=yes PIV=yes +release-ng-386: + $(MAKE) release-ng ARCH=386 +release-ng-arm: + $(MAKE) release-ng ARCH=arm + +.PHONY: release-ng +release-ng: webassets buildbox-ng + docker run --rm --interactive $(shell test -t 0 && echo --tty) \ + --volume $(shell pwd)/..:/home/teleport \ + --workdir /home/teleport \ + --user $(shell id -u):$(shell id -g) \ + $(BUILDBOX_NG) \ + make -e release-unix-preserving-webassets \ + ADDFLAGS="$(ADDFLAGS)" \ + OS="$(OS)" \ + ARCH="$(ARCH)" \ + RUNTIME="$(GOLANG_VERSION)" \ + FIDO2="$(FIDO2)" \ + PIV="$(PIV)" \ + REPRODUCIBLE=yes + # # Create a Teleport FIPS package using the build container. # This is a special case because it only builds and packages the Enterprise FIPS binaries, no OSS. diff --git a/build.assets/buildbox/Dockerfile b/build.assets/buildbox/Dockerfile new file mode 100644 index 0000000000000..afbffbf448368 --- /dev/null +++ b/build.assets/buildbox/Dockerfile @@ -0,0 +1,178 @@ +# syntax=docker/dockerfile:1 +# +# Build the Teleport buildbox with the pre-built crosstool-NG cross compilers, +# pre-built third-party C libraries for the four supported architectures, the +# Go toolchain and the Rust toolchain. +# +# The pre-built crosstool-NG cross compilers and third-party C libraries come +# from a image specified by the THIRDPARTY_IMAGE arg, which is built by the +# Dockerfile-thirdparty dockerfile. +# +# All the compilers/toolchains are owned by the buildbox:buildbox (99:99) user. +# They are not intended to be modifiable when the buildbox runs. By default the +# image runs as user teleport:teleport (1000:1000) and can write to +# /home/teleport. /home/teleport is also world-writable so if the UID/GID +# 1000/1000 cannot be used for some reason, the user can still check out +# repositories into that directory. +# +# Alternatively, map an external repository directory as a volume on to +# /home/teleport and set the uid/gid to the owner of that directory. +# +# The default GOPATH is /tmp/go, with the build cache (GOCACHE) in +# /tmp/go/build. CARGO_HOME is /tmp/rust. This allows Go and Rust to run and +# have a place to cache builds and install binaries. + +ARG BASE_IMAGE=ubuntu:22.04 +ARG THIRDPARTY_IMAGE + +# ---------------------------------------------------------------------------- +# Define a simple base image for installing various compilers which are then +# copied into the final image. This helps with caching as the download+install +# does not depend on previous layers. + +FROM ${BASE_IMAGE} AS base + +# Install curl as it is needed by the Go and Rust stages to download the compilers. +RUN apt-get update \ + && apt-get install -y curl \ + && rm -rf /var/lib/apt/lists/* + +ARG BUILDBOX_UID=99 +ARG BUILDBOX_GID=99 +RUN groupadd -g $BUILDBOX_GID buildbox +RUN useradd -d /home/buildbox -m -g $BUILDBOX_GID -u $BUILDBOX_UID -s /bin/bash buildbox + +ARG TELEPORT_UID=1000 +ARG TELEPORT_GID=1000 +RUN groupadd -g $TELEPORT_GID teleport +RUN useradd -d /home/teleport -m -g $TELEPORT_GID -u $TELEPORT_UID -s /bin/bash teleport +RUN chmod 777 /home/teleport + +# ---------------------------------------------------------------------------- +# Reference the thirdparty image for copying from later. + +FROM ${THIRDPARTY_IMAGE} AS thirdparty + +# ---------------------------------------------------------------------------- +# Install Go +# +# Go is installed into the base image and copied across to the final image in +# the last stage. This make the downloading and installation of the Go +# toolchain dependent on nothing but the base. + +FROM base AS go + +RUN install -d -m 0775 -o buildbox -g buildbox /opt/go +USER buildbox + +ARG BUILDARCH +ARG GOLANG_VERSION +# Set BUILDARCH if not set when not using buildkit. Only works for arm64 and amd64. +RUN BUILDARCH=${BUILDARCH:-$(uname -m | sed 's/aarch64/arm64/g; s/x86_64/amd64/g')}; \ + curl -fsSL https://storage.googleapis.com/golang/${GOLANG_VERSION}.linux-${BUILDARCH}.tar.gz | \ + tar -C /opt -xz && \ + /opt/go/bin/go version + +# ---------------------------------------------------------------------------- +# Install Rust +# +# Rust is installed into the base image and copied across to the final image in +# the last stage. This make the downloading and installation of the Rust +# toolchain dependent on nothing but the base. + +FROM base AS rust + +RUN install -d -m 0775 -o buildbox -g buildbox /opt/rust +USER buildbox + +ARG RUST_VERSION +ENV RUSTUP_HOME=/opt/rust +ENV CARGO_HOME=/opt/rust +RUN curl --proto =https --tlsv1.2 -fsSL https://sh.rustup.rs | \ + sh -s -- -y --profile minimal --default-toolchain ${RUST_VERSION} && \ + ${CARGO_HOME}/bin/rustup --version && \ + ${CARGO_HOME}/bin/cargo --version && \ + ${CARGO_HOME}/bin/rustc --version && \ + ${CARGO_HOME}/bin/rustup target add \ + x86_64-unknown-linux-gnu \ + aarch64-unknown-linux-gnu \ + i686-unknown-linux-gnu \ + arm-unknown-linux-gnueabihf \ + wasm32-unknown-unknown + +# ---------------------------------------------------------------------------- +# buildbox image +# +# Build the final buildbox image by installing required packages and copying +# the toolchains from the previous stages/images. + +FROM base AS buildbox + +RUN apt-get update && apt-get install -y \ + autoconf \ + automake \ + autopoint \ + bison \ + clang-12 \ + cmake \ + flex \ + gettext \ + git \ + libtool \ + make \ + ninja-build \ + pkg-config \ + sed \ + w3m \ + wget \ + xsltproc \ + xz-utils \ + && rm -rf /var/lib/apt/lists/* + +RUN install -d -m 1777 -o teleport -g teleport /tmp/build + +USER buildbox + +# Copy compilers from other images +ARG THIRDPARTY_DIR=/opt/thirdparty +COPY --from=thirdparty ${THIRDPARTY_DIR} ${THIRDPARTY_DIR} +COPY --from=rust /opt/rust /opt/rust +COPY --from=go /opt/go /opt/go + +# Set RUSTUP_HOME so cargo does not warn/error about not finding it at ~/.cargo +ENV RUSTUP_HOME=/opt/rust + +ENV PATH=/opt/go/bin:/opt/rust/bin:${THIRDPARTY_DIR}/host/bin:${PATH} + +# Ensure THIRDPARTY_DIR gets propagated to the makefiles if the provided arg +# is not the default value. +ENV THIRDPARTY_DIR=${THIRDPARTY_DIR} + +# Set up env vars for rust to cross-compile binaries. I needs a linker for the +# appropriate architecture, which is invoked via `cc`. These compilers are all +# on the PATH. +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-unknown-linux-gnu-gcc +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-unknown-linux-gnu-gcc +ENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=i686-unknown-linux-gnu-gcc +ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-unknown-linux-gnueabihf-gcc + +# Set CARGO_HOME, GOPATH and GOCACHE to somewhere writable as the user of the +# buildbox will have a UID/GID different to the buildbox user. Also create +# /home/teleport as a world-writable directory so that can be used as a +# workspace when cloning a git repo directly in the buildbox as opposed to +# mapping a volume from outside. +# The /tmp/build directory can be a volume so the build/package cache can be +# carried across builds. +ENV CARGO_HOME=/tmp/build/rust +ENV GOPATH=/tmp/build/go +ENV GOCACHE=/tmp/build/go/build + +# Add the writable cargo and go bin directories to the path so we will find +# binaries build with `cargo install` and `go install` during a build. +ENV PATH=${CARGO_HOME}/bin:${GOPATH}/bin:${PATH} + +# Set a var so the build system can know it's running in this buildbox. +ENV BUILDBOX_MODE=cross + +USER teleport:teleport +WORKDIR /home/teleport diff --git a/build.assets/buildbox/Dockerfile-thirdparty b/build.assets/buildbox/Dockerfile-thirdparty new file mode 100644 index 0000000000000..a455eac5368b1 --- /dev/null +++ b/build.assets/buildbox/Dockerfile-thirdparty @@ -0,0 +1,139 @@ +# syntax=docker/dockerfile:1 +# +# Cross-compiling build box. +# +# This Dockefile builds a crosstool-NG toolchain (gcc et al) for the four +# supported Teleport architectures (amd64. arm64 386, arm), as well as the +# C libraries needed for various Teleport features. The image is not run +# directly. It is used as an input to the final buildbox image. + +ARG BASE_IMAGE=ubuntu:22.04 + +# Directory where the third-party components are installed. Used in multiple +# stages, so defined here globally before the first stage. +ARG THIRDPARTY_DIR=/opt/thirdparty + +# ----------------------------------------------------------------------------- +# Install required build tools and install crosstool-ng + +FROM ${BASE_IMAGE} AS crosstoolng + +# Bash used for some brace expansions in clean-up +SHELL ["/bin/bash", "-c"] + +# Create a buildbox user that owns all the tools installed in /opt. When using the +# buildbox, a different uid/gid should be used as these tools should not be +# modifiable when using the buildbox +ARG BUILDBOX_UID=99 +ARG BUILDBOX_GID=99 +RUN groupadd -g $BUILDBOX_GID buildbox +RUN useradd -d /home/buildbox -m -g $BUILDBOX_GID -u $BUILDBOX_UID -s /bin/bash buildbox + +ARG THIRDPARTY_DIR +RUN install -d -m 0775 -o buildbox -g buildbox $THIRDPARTY_DIR + +# Non-interactive configuration of tzdata +ENV DEBIAN_FRONTEND=noninteractive +ENV DEBCONF_NONINTERACTIVE_SEEN=true +RUN { echo 'tzdata tzdata/Areas select Etc'; echo 'tzdata tzdata/Zones/Etc select UTC'; } | debconf-set-selections + +RUN apt-get update +RUN apt-get install -y \ + autoconf \ + autoconf-archive \ + automake \ + autopoint \ + bison \ + bzip2 \ + cmake \ + curl \ + flex \ + g++ \ + gawk \ + gcc \ + gettext \ + git \ + gperf \ + help2man \ + libncurses5-dev \ + libstdc++6 \ + libtool \ + libtool-bin \ + make \ + meson \ + patch \ + pkg-config \ + python3-dev \ + rsync \ + texinfo \ + texi2html \ + unzip \ + xz-utils + +USER buildbox +WORKDIR /home/buildbox + +COPY crosstoolng-configs crosstoolng-configs +COPY pkgconfig pkgconfig +COPY buildbox-common.mk crosstoolng.mk . + +# Build and install crosstool-ng +ARG BUILDARCH +ARG THIRDPARTY_DIR +ENV THIRDPARTY_DIR=$THIRDPARTY_DIR + +RUN make -f crosstoolng.mk install-crosstoolng && \ + rm -rf ${THIRDPARTY_DIR}/host/src + +# ----------------------------------------------------------------------------- +# Build the compilers for the four architecures + +FROM crosstoolng AS compilers + +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f crosstoolng.mk crosstoolng-build ARCH=amd64 && \ + rm -rf ${THIRDPARTY_DIR}/amd64/crosstoolng +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f crosstoolng.mk crosstoolng-build ARCH=arm64 && \ + rm -rf ${THIRDPARTY_DIR}/arm64/crosstoolng +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f crosstoolng.mk crosstoolng-build ARCH=386 && \ + rm -rf ${THIRDPARTY_DIR}/386/crosstoolng +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f crosstoolng.mk crosstoolng-build ARCH=arm && \ + rm -rf ${THIRDPARTY_DIR}/arm/crosstoolng + +# ----------------------------------------------------------------------------- +# Build the third-party C libraries for the four architectures + +FROM compilers AS libs + +COPY cross-compile.mk thirdparty-libs.mk . + +ARG THIRDPARTY_DIR +ENV PATH=${THIRDPARTY_DIR}/host/bin:${PATH} + +# Build and install third party C libraries for all architectures +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f thirdparty-libs.mk thirdparty-build-libs ARCH=amd64 && \ + rm -rf ${THIRDPARTY_DIR}/amd64/{bin,sbin,src} +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f thirdparty-libs.mk thirdparty-build-libs ARCH=arm64 && \ + rm -rf ${THIRDPARTY_DIR}/arm64/{bin,sbin,src} +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f thirdparty-libs.mk thirdparty-build-libs ARCH=386 && \ + rm -rf ${THIRDPARTY_DIR}/386/{bin,sbin,src} +RUN --mount=type=cache,id=download,uid=${BUILDBOX_UID},target=${THIRDPARTY_DIR}/download \ + make -f thirdparty-libs.mk thirdparty-build-libs ARCH=arm && \ + rm -rf ${THIRDPARTY_DIR}/arm/{bin,sbin,src} + +# ----------------------------------------------------------------------------- +# Start buildbox-thirdparty from a fresh ${BASE_IMAGE} and copy in +# /opt/thirdparty so as to remove any layers above with the build tools. We +# copy it into ${BASE_IMAGE} instead of scratch so that we can run the +# container and easily inspect and work with it for development purposes. +FROM ${BASE_IMAGE} AS buildbox-thirdparty + +# Install third party toolchain and libraries +ARG THIRDPARTY_DIR +COPY --from=libs ${THIRDPARTY_DIR} ${THIRDPARTY_DIR} diff --git a/build.assets/buildbox/buildbox-common.mk b/build.assets/buildbox/buildbox-common.mk new file mode 100644 index 0000000000000..513f8efb7b918 --- /dev/null +++ b/build.assets/buildbox/buildbox-common.mk @@ -0,0 +1,89 @@ +# Common definitions shared between makefiles + +# Default parallelism of builds +NPROC ?= $(shell nproc) + +# THIRDPARTY_DIR is the root of where third-party libraries and programs are +# downloaded, built and installed. +THIRDPARTY_DIR ?= /opt/thirdparty + +# THIRDPARTY_PREFIX is the root of where architecture-specific third-party +# libraries are installed. Each architecture has its own directory as libraries +# are architecture-specific. +THIRDPARTY_PREFIX = $(THIRDPARTY_DIR)/$(ARCH) + +# THIRDPARTY_DLDIR holds downloaded tarballs of third-party libraries and +# programs so we don't have to keep downloading them. It is safe to delete. +THIRDPARTY_DLDIR = $(THIRDPARTY_DIR)/download + +# THIRDPARTY_SRCDIR is the directory where the source for third-party is +# extracted and built. Each architecture has its own extracted source as the +# build is done within the source tree. +THIRDPARTY_SRCDIR = $(THIRDPARTY_PREFIX)/src + +# THIRDPARTY_HOST_PREFIX is the root of where host-specific third-party +# programs are installed, such as ct-ng and the compilers it builds. These +# run on the host that is running the build, regardless of that host +# architecture. THIRDPARTY_HOST_SRCDIR is the directory where the source +# for host-specific third-party applications is extracted and built. +THIRDPARTY_HOST_PREFIX = $(THIRDPARTY_DIR)/host +THIRDPARTY_HOST_SRCDIR = $(THIRDPARTY_HOST_PREFIX)/src + +# ----------------------------------------------------------------------------- +# tp-src-dir and tp-src-host-dir expand to the source directory for a third- +# party source directory which has the version of the source appended. It +# is used with `$(call ...)`, like `$(call tp-src-dir,zlib)` or +# `$(call tp-src-host-dir,crosstoolng)`. +tp-src-dir = $(THIRDPARTY_SRCDIR)/$1-$($1_VERSION) +tp-src-host-dir = $(THIRDPARTY_HOST_SRCDIR)/$1-$($1_VERSION) + +# ----------------------------------------------------------------------------- +# Helpers + +# Create top-level directories when required +$(THIRDPARTY_SRCDIR) $(THIRDPARTY_HOST_SRCDIR) $(THIRDPARTY_DLDIR): + mkdir -p $@ + +# vars for fetch-git-%. `$*` represents the `%` match. +tp-git-ref = $($*_GIT_REF) +tp-git-repo = $($*_GIT_REPO) +tp-git-ref-hash = $($*_GIT_REF_HASH) +tp-git-src-dir = $($*_SRCDIR) +define tp-git-fetch-cmd + git -C "$(dir $(tp-git-src-dir))" \ + -c advice.detachedHead=false clone --depth=1 \ + --branch=$(tp-git-ref) $(tp-git-repo) "$(tp-git-src-dir)" +endef + +# Fetch source via git. +fetch-git-%: + mkdir -p $(dir $(tp-git-src-dir)) + $(if $(wildcard $(tp-git-src-dir)),,$(tp-git-fetch-cmd)) + @if [ "$$(git -C "$(tp-git-src-dir)" rev-parse HEAD)" != "$(tp-git-ref-hash)" ]; then \ + echo "Found unexpected HEAD commit for $(1)"; \ + echo "Expected: $(tp-git-ref-hash)"; \ + echo "Got: $$(git -C "$(tp-git-src-dir)" rev-parse HEAD)"; \ + exit 1; \ + fi + +# vars for fetch-https-%. `$*` represents the `%` match. +tp-download-url = $($*_DOWNLOAD_URL) +tp-sha1 = $($*_SHA1) +tp-download-filename = $(THIRDPARTY_DLDIR)/$(notdir $(tp-download-url)) +tp-strip-components = $($*_STRIP_COMPONENTS) +tp-https-download-cmd = curl -fsSL --output "$(tp-download-filename)" "$(tp-download-url)" +tp-https-src-dir = $(call tp-src-dir,$*) +define tp-https-extract-tar-cmd + @echo "$(tp-sha1) $(tp-download-filename)" | sha1sum --check + mkdir -p "$(tp-https-src-dir)" + tar -x -a \ + --file="$(tp-download-filename)" \ + --directory="$(tp-https-src-dir)" \ + --strip-components="$(tp-strip-components)" +endef + +# Fetch source tarball via https +fetch-https-%: + @mkdir -p $(THIRDPARTY_DLDIR) $(dir $(tp-https-src-dir)) + $(if $(wildcard $(tp-download-filename)),,$(tp-https-download-cmd)) + $(if $(wildcard $(tp-https-src-dir)),,$(tp-https-extract-tar-cmd)) diff --git a/build.assets/buildbox/cross-compile.mk b/build.assets/buildbox/cross-compile.mk new file mode 100644 index 0000000000000..7e8a579612334 --- /dev/null +++ b/build.assets/buildbox/cross-compile.mk @@ -0,0 +1,50 @@ +# This Makefile fragment defines some environment variables for using the +# crosstool-NG cross compilers installed in the buildbox. By including this +# makefile fragment in another Makefile, various environment variables will +# be set to find the cross compilers and the third-party libraries. +# +# $(ARCH) should be set to one of amd64, arm64, 386 or arm so that the +# correct compiler and libraries are referenced. +# +# Care should be taked to NOT include this file when building the cross +# compilers themselves. + +mk_dir := $(dir $(lastword $(MAKEFILE_LIST))) +include $(mk_dir)/buildbox-common.mk + +# Environment setup for building with crosstoolng toolchain and third party libraries. + +# Define the crosstool-NG target triple +CROSSTOOLNG_TARGET = $(CROSSTOOLNG_TARGET_$(ARCH)) +CROSSTOOLNG_TARGET_amd64 = x86_64-unknown-linux-gnu +CROSSTOOLNG_TARGET_arm64 = aarch64-unknown-linux-gnu +CROSSTOOLNG_TARGET_386 = i686-unknown-linux-gnu +CROSSTOOLNG_TARGET_arm = arm-unknown-linux-gnueabihf + +# Define environment variables used by gcc, clang and make to find the +# appropriate compiler and third party libraries. +export C_INCLUDE_PATH = $(THIRDPARTY_PREFIX)/include +export LIBRARY_PATH = $(THIRDPARTY_PREFIX)/lib +export PKG_CONFIG_PATH = $(THIRDPARTY_PREFIX)/lib/pkgconfig +export CC = $(CROSSTOOLNG_TARGET)-gcc +export CXX = $(CROSSTOOLNG_TARGET)-g++ +export LD = $(CROSSTOOLNG_TARGET)-ld + +CROSS_VARS = C_INCLUDE_PATH LIBRARY_PATH PKG_CONFIG_PATH CC CXX LD + +# arm64 has linking issues using the binutils linker when building the +# Enterprise Teleport binary ("relocation truncated to fit: R_AARCH64_CALL26 +# against symbol") that is resolved by using the gold linker. Ensure that +# is used for arm64 builds +ifeq ($(ARCH),arm64) +export CTNG_LD_IS := gold +CROSS_VARS += CTNG_LD_IS +endif + +# sh-cross-vars prints the cross-compiling variables in a form that can be +# sourced by the shell, allowing you to set them in an outer shell for +# development purposes: +# eval $(make -s -f cross-compile.sh ARCH=arm64 sh-cross-vars) +.PHONY:sh-cross-vars +sh-cross-vars: + @/usr/bin/env bash -c 'for v in $(CROSS_VARS); do echo "export $$v=$${!v@Q}"; done' diff --git a/build.assets/buildbox/crosstoolng-configs/386.defconfig b/build.assets/buildbox/crosstoolng-configs/386.defconfig new file mode 100644 index 0000000000000..2de6cfef6c393 --- /dev/null +++ b/build.assets/buildbox/crosstoolng-configs/386.defconfig @@ -0,0 +1,14 @@ +CT_CONFIG_VERSION="4" +CT_OBSOLETE=y +CT_LOCAL_TARBALLS_DIR="${THIRDPARTY_DLDIR}" +CT_WORK_DIR="${CT_TOP_DIR}/build" +CT_PREFIX_DIR="${THIRDPARTY_HOST_PREFIX}/${CT_TARGET}" +# CT_PREFIX_DIR_RO is not set +# CT_LOG_PROGRESS_BAR is not set +CT_ARCH_X86=y +CT_ARCH_ARCH="i686" +CT_KERNEL_LINUX=y +CT_LINUX_V_6_2=y +CT_GLIBC_V_2_17=y +CT_CC_LANG_CXX=y +CT_COMP_LIBS_LIBELF=y diff --git a/build.assets/buildbox/crosstoolng-configs/amd64.defconfig b/build.assets/buildbox/crosstoolng-configs/amd64.defconfig new file mode 100644 index 0000000000000..d43a315ca03d5 --- /dev/null +++ b/build.assets/buildbox/crosstoolng-configs/amd64.defconfig @@ -0,0 +1,15 @@ +CT_CONFIG_VERSION="4" +CT_OBSOLETE=y +CT_LOCAL_TARBALLS_DIR="${THIRDPARTY_DLDIR}" +CT_WORK_DIR="${CT_TOP_DIR}/build" +CT_PREFIX_DIR="${THIRDPARTY_HOST_PREFIX}/${CT_TARGET}" +# CT_RM_RF_PREFIX_DIR is not set +# CT_PREFIX_DIR_RO is not set +# CT_LOG_PROGRESS_BAR is not set +CT_ARCH_X86=y +CT_ARCH_64=y +CT_KERNEL_LINUX=y +CT_LINUX_V_6_2=y +CT_GLIBC_V_2_17=y +CT_CC_LANG_CXX=y +CT_COMP_LIBS_LIBELF=y diff --git a/build.assets/buildbox/crosstoolng-configs/arm.defconfig b/build.assets/buildbox/crosstoolng-configs/arm.defconfig new file mode 100644 index 0000000000000..07a82c6695964 --- /dev/null +++ b/build.assets/buildbox/crosstoolng-configs/arm.defconfig @@ -0,0 +1,18 @@ +CT_CONFIG_VERSION="4" +CT_OBSOLETE=y +CT_LOCAL_TARBALLS_DIR="${THIRDPARTY_DLDIR}" +CT_WORK_DIR="${CT_TOP_DIR}/build" +CT_PREFIX_DIR="${THIRDPARTY_HOST_PREFIX}/${CT_TARGET}" +# CT_PREFIX_DIR_RO is not set +# CT_LOG_PROGRESS_BAR is not set +CT_ARCH_ARM=y +CT_ARCH_CPU="cortex-a7" +CT_ARCH_FLOAT_HW=y +CT_STATIC_TOOLCHAIN=y +CT_KERNEL_LINUX=y +CT_LINUX_V_6_2=y +CT_GLIBC_V_2_17=y +CT_CC_LANG_CXX=y +CT_COMP_LIBS_LIBELF=y +CT_ZLIB_NEEDED=y +CT_ZSTD_NEEDED=y diff --git a/build.assets/buildbox/crosstoolng-configs/arm64.defconfig b/build.assets/buildbox/crosstoolng-configs/arm64.defconfig new file mode 100644 index 0000000000000..e7f5f9f49cd76 --- /dev/null +++ b/build.assets/buildbox/crosstoolng-configs/arm64.defconfig @@ -0,0 +1,18 @@ +CT_CONFIG_VERSION="4" +CT_OBSOLETE=y +CT_LOCAL_TARBALLS_DIR="${THIRDPARTY_DLDIR}" +CT_WORK_DIR="${CT_TOP_DIR}/build" +CT_PREFIX_DIR="${THIRDPARTY_HOST_PREFIX}/${CT_TARGET}" +# CT_PREFIX_DIR_RO is not set +# CT_LOG_PROGRESS_BAR is not set +CT_ARCH_ARM=y +CT_ARCH_64=y +CT_KERNEL_LINUX=y +CT_LINUX_V_6_2=y +CT_BINUTILS_LINKER_LD_GOLD=y +CT_BINUTILS_GOLD_THREADS=y +CT_BINUTILS_LD_WRAPPER=y +CT_BINUTILS_PLUGINS=y +CT_GLIBC_V_2_17=y +CT_CC_LANG_CXX=y +CT_COMP_LIBS_LIBELF=y diff --git a/build.assets/buildbox/crosstoolng.mk b/build.assets/buildbox/crosstoolng.mk new file mode 100644 index 0000000000000..135da23246c3c --- /dev/null +++ b/build.assets/buildbox/crosstoolng.mk @@ -0,0 +1,81 @@ +# Makefile for building cross-compilers with crosstool-ng and building +# the third-party C library dependencies for Teleport. +# +# This Makefile is intended to be used inside a docker build and as such is +# Linux-only. It could be run on a linux host outside of docker, in which case +# you will likely want to override THIRDPARTY_DIR. + +# Default ARCH to the host architecture. Normally it is set to specify which +# architecture to build for, but some rules need it to be set even if not used. +UNAME_M := $(shell uname -m) +ARCH_aarch64 = arm64 +ARCH = $(or $(ARCH_$(UNAME_M)),$(UNAME_M)) + +mk_dir := $(dir $(lastword $(MAKEFILE_LIST))) +include $(mk_dir)/buildbox-common.mk + +# ----------------------------------------------------------------------------- +# crosstool-ng +# +# crosstool-ng is a host tool - it runs on the build host. It is installed in +# $(THIRDPARTY_HOST_PREFIX). + +crosstoolng_VERSION = 1.26.0 +crosstoolng_GIT_REF = crosstool-ng-$(crosstoolng_VERSION) +crosstoolng_GIT_REF_HASH = 334f6d6479096b20e80fd39e35f404319bc251b5 +crosstoolng_GIT_REPO = https://github.com/crosstool-ng/crosstool-ng +crosstoolng_SRCDIR = $(call tp-src-host-dir,crosstoolng) + +.PHONY: install-crosstoolng +install-crosstoolng: fetch-git-crosstoolng + cd $(crosstoolng_SRCDIR) && ./bootstrap + cd $(crosstoolng_SRCDIR) && ./configure --prefix=$(THIRDPARTY_HOST_PREFIX) + $(MAKE) -C $(crosstoolng_SRCDIR) -j$(NPROC) + $(MAKE) -C $(crosstoolng_SRCDIR) install + +# ----------------------------------------------------------------------------- +# Configure and build crosstool-ng compilers +# +# We use crosstool-ng, installed in $(THIRDPARTY_HOST_PREFIX) to build a +# compiler and glibc for each of the architectures: amd64, arm64, 386 and arm. +# These architecture names are as Go names them. The architecture of the +# toolchain to build is specified by the $(ARCH) variable. + +CROSSTOOLNG_BUILDDIR = $(THIRDPARTY_PREFIX)/crosstoolng +$(CROSSTOOLNG_BUILDDIR): + mkdir -p $@ + +CROSSTOOLNG_DEFCONFIG = $(CROSSTOOLNG_BUILDDIR)/defconfig +CROSSTOOLNG_CONFIG = $(CROSSTOOLNG_BUILDDIR)/.config + +CTNG = $(THIRDPARTY_HOST_PREFIX)/bin/ct-ng -C $(CROSSTOOLNG_BUILDDIR) + +# Create a defconfig if it does not exist +crosstoolng-configs/$(ARCH).defconfig: + touch $@ + +# Copy the defconfig into the build dir +$(CROSSTOOLNG_DEFCONFIG): crosstoolng-configs/$(ARCH).defconfig | $(CROSSTOOLNG_BUILDDIR) + cp $^ $@ + +# Create an expanded config from the defconfig +$(CROSSTOOLNG_CONFIG): $(CROSSTOOLNG_DEFCONFIG) + $(CTNG) defconfig + +# Run `ct-ng menuconfig` on the arch-specific config from the defconfig in build.assets +# and copy it back when finished with menuconfig +.PHONY: crosstoolng-menuconfig +crosstoolng-menuconfig: $(CROSSTOOLNG_CONFIG) | $(CROSSTOOLNG_BUILDDIR) + $(CTNG) menuconfig + $(CTNG) savedefconfig + cp $(CROSSTOOLNG_DEFCONFIG) crosstoolng-configs/$(ARCH).defconfig + +# Build the toolchain with the config in the defconfig for the architecture. We need to +# clear out some env vars because ct-ng does not want them set. We export a couple of +# vars because we reference them in the config. +# The config specifies where the toolchain is installed ($(THIRDPARTY_HOST_PREFIX)/TARGET). +.PHONY: crosstoolng-build +crosstoolng-build: $(CROSSTOOLNG_CONFIG) | $(CROSSTOOLNG_BUILDDIR) + @mkdir -p $(THIRDPARTY_DLDIR) + THIRDPARTY_HOST_PREFIX=$(THIRDPARTY_HOST_PREFIX) THIRDPARTY_DLDIR=$(THIRDPARTY_DLDIR) $(CTNG) build + cd $(THIRDPARTY_HOST_PREFIX)/bin && ln -ns ../$$($(CTNG) -s show-tuple)/bin/* . diff --git a/build.assets/buildbox/pkgconfig/libcrypto-static.pc b/build.assets/buildbox/pkgconfig/libcrypto-static.pc new file mode 100644 index 0000000000000..2ff82400aa739 --- /dev/null +++ b/build.assets/buildbox/pkgconfig/libcrypto-static.pc @@ -0,0 +1,12 @@ +prefix=@@PREFIX@@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +enginesdir=${libdir}/engines-3 +modulesdir=${libdir}/ossl-modules + +Name: OpenSSL-libcrypto +Description: OpenSSL cryptography library +Version: 3.0.13 +Libs: ${libdir}/libcrypto.a -ldl -pthread +Cflags: -I${includedir} diff --git a/build.assets/buildbox/pkgconfig/libelf.pc b/build.assets/buildbox/pkgconfig/libelf.pc new file mode 100644 index 0000000000000..ee9c9730ab0ae --- /dev/null +++ b/build.assets/buildbox/pkgconfig/libelf.pc @@ -0,0 +1,14 @@ +prefix=@@PREFIX@@ +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: libelf +Description: elfutils libelf library to read and write ELF files +Version: 0.191 +URL: http://elfutils.org/ + +Libs: -L${libdir} -lelf +Cflags: -I${includedir} + +Requires.private: zlib libzstd diff --git a/build.assets/buildbox/pkgconfig/libfido2-static.pc b/build.assets/buildbox/pkgconfig/libfido2-static.pc new file mode 100644 index 0000000000000..5b011526288d9 --- /dev/null +++ b/build.assets/buildbox/pkgconfig/libfido2-static.pc @@ -0,0 +1,13 @@ +prefix=@@PREFIX@@ +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: libfido2 +Description: A FIDO2 library +URL: https://github.com/yubico/libfido2 +Version: 1.14.0 +Requires: libcrypto-static +# libfido2, libcbor and libudev combined here for simplicity. +Libs: ${libdir}/libfido2.a ${libdir}/libcbor.a ${libdir}/libudev.a -pthread +Cflags: -I${includedir} diff --git a/build.assets/buildbox/thirdparty-libs.mk b/build.assets/buildbox/thirdparty-libs.mk new file mode 100644 index 0000000000000..4d2ef2fa061cf --- /dev/null +++ b/build.assets/buildbox/thirdparty-libs.mk @@ -0,0 +1,237 @@ +# Third-party libraries needed to build Teleport. + +mk_dir := $(dir $(lastword $(MAKEFILE_LIST))) +include $(mk_dir)/cross-compile.mk + +# We build these libraries ourself and statically link them into the Teleport +# binary as we need them build with PIE (Position Independent Executable) mode +# so as to make use of ASLR (Address Space Layout Randomization). We cannot +# rely on a host OS/packager to have built them this way. + +THIRDPARTY_LIBS = zlib zstd libelf libbpf libtirpc libpam libudev_zero \ + libcbor openssl libfido2 libpcsclite + +.PHONY: thirdparty-build-libs +thirdparty-build-libs: $(addprefix tp-build-,$(THIRDPARTY_LIBS)) + +# ----------------------------------------------------------------------------- +# zlib + +zlib_VERSION = 1.3.1 +zlib_GIT_REF = v$(zlib_VERSION) +zlib_GIT_REF_HASH = 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf +zlib_GIT_REPO = https://github.com/madler/zlib +zlib_SRCDIR = $(call tp-src-dir,zlib) + +.PHONY: tp-build-zlib +tp-build-zlib: fetch-git-zlib + cd $(zlib_SRCDIR) && \ + ./configure --prefix="$(THIRDPARTY_PREFIX)" --static + $(MAKE) -C $(zlib_SRCDIR) CFLAGS+=-fPIE -j$(NPROC) + $(MAKE) -C $(zlib_SRCDIR) install + +# ----------------------------------------------------------------------------- +# zstd + +zstd_VERSION = 1.5.6 +zstd_GIT_REF = v$(zstd_VERSION) +zstd_GIT_REF_HASH = 794ea1b0afca0f020f4e57b6732332231fb23c70 +zstd_GIT_REPO = https://github.com/facebook/zstd +zstd_SRCDIR = $(call tp-src-dir,zstd) + +.PHONY: tp-build-zstd +tp-build-zstd: fetch-git-zstd + $(MAKE) -C $(zstd_SRCDIR) PREFIX=$(THIRDPARTY_PREFIX) CPPFLAGS_STATICLIB+=-fPIE -j$(NPROC) + $(MAKE) -C $(zstd_SRCDIR) install PREFIX=$(THIRDPARTY_PREFIX) + +# ----------------------------------------------------------------------------- +# libelf + +libelf_VERSION = 0.191 +libelf_GIT_REF = v$(libelf_VERSION) +libelf_GIT_REF_HASH = b80c36da9d70158f9a38cfb9af9bb58a323a5796 +libelf_GIT_REPO = https://github.com/arachsys/libelf +libelf_SRCDIR = $(call tp-src-dir,libelf) + +.PHONY: tp-build-libelf +tp-build-libelf: fetch-git-libelf + $(MAKE) -C $(libelf_SRCDIR) CFLAGS+=-fPIE -j$(NPROC) libelf.a + $(MAKE) -C $(libelf_SRCDIR) install-headers install-static PREFIX=$(THIRDPARTY_PREFIX) + sed "s|@@PREFIX@@|${THIRDPARTY_PREFIX}|" \ + < pkgconfig/libelf.pc \ + > $(PKG_CONFIG_PATH)/libelf.pc + +# ----------------------------------------------------------------------------- +# libbpf + +libbpf_VERSION = 1.2.2 +libbpf_GIT_REF = v$(libbpf_VERSION) +libbpf_GIT_REF_HASH = 1728e3e4bef0e138ea95ffe62163eb9a6ac6fa32 +libbpf_GIT_REPO = https://github.com/libbpf/libbpf +libbpf_SRCDIR = $(call tp-src-dir,libbpf) + +.PHONY: tp-build-libbpf +tp-build-libbpf: fetch-git-libbpf + $(MAKE) -C $(libbpf_SRCDIR)/src \ + BUILD_STATIC_ONLY=y EXTRA_CFLAGS=-fPIE PREFIX=$(THIRDPARTY_PREFIX) LIBSUBDIR=lib V=1 \ + install install_uapi_headers + +# ----------------------------------------------------------------------------- +# libtirpc + +libtirpc_VERSION = 1.3.4 +libtirpc_SHA1 = 63c800f81f823254d2706637bab551dec176b99b +libtirpc_DOWNLOAD_URL = https://zenlayer.dl.sourceforge.net/project/libtirpc/libtirpc/$(libtirpc_VERSION)/libtirpc-$(libtirpc_VERSION).tar.bz2 +libtirpc_STRIP_COMPONENTS = 1 +libtirpc_SRCDIR = $(call tp-src-dir,libtirpc) + +.PHONY: tp-build-libtirpc +tp-build-libtirpc: fetch-https-libtirpc + cd $(libtirpc_SRCDIR) && \ + CFLAGS=-fPIE ./configure \ + --prefix=$(THIRDPARTY_PREFIX) \ + --enable-shared=no \ + --disable-gssapi \ + $(if $(CROSSTOOLNG_TARGET),--host=$(CROSSTOOLNG_TARGET)) + $(MAKE) -C $(libtirpc_SRCDIR) -j$(NPROC) + $(MAKE) -C $(libtirpc_SRCDIR) install + +# ----------------------------------------------------------------------------- +# libpam + +libpam_VERSION = 1.6.1 +libpam_GIT_REF = v$(libpam_VERSION) +libpam_GIT_REF_HASH = 9438e084e2b318bf91c3912c0b8ff056e1835486 +libpam_GIT_REPO = https://github.com/linux-pam/linux-pam +libpam_SRCDIR = $(call tp-src-dir,libpam) + +# libpam wants the host arg to be i686 for 386 builds. The other architectures +# are just the architecture name we use. +libpam_HOST_386 = i686 + +.PHONY: tp-build-libpam +tp-build-libpam: fetch-git-libpam + cd $(libpam_SRCDIR) && \ + ./autogen.sh + cd $(libpam_SRCDIR) && \ + CFLAGS=-fPIE ./configure --prefix=$(THIRDPARTY_PREFIX) \ + --disable-doc --disable-examples \ + --includedir=$(THIRDPARTY_PREFIX)/include/security \ + --host=$(or $(libpam_HOST_$(ARCH)),$(ARCH)) + $(MAKE) -C $(libpam_SRCDIR) -j$(NPROC) + $(MAKE) -C $(libpam_SRCDIR) install + +# ----------------------------------------------------------------------------- +# libudev-zero + +libudev_zero_VERSION = 1.0.3 +libudev_zero_GIT_REF = $(libudev_zero_VERSION) +libudev_zero_GIT_REF_HASH = ee32ac5f6494047b9ece26e7a5920650cdf46655 +libudev_zero_GIT_REPO = https://github.com/illiliti/libudev-zero +libudev_zero_SRCDIR = $(call tp-src-dir,libudev_zero) + +.PHONY: tp-build-libudev_zero +tp-build-libudev_zero: fetch-git-libudev_zero + $(MAKE) -C $(libudev_zero_SRCDIR) \ + PREFIX=$(THIRDPARTY_PREFIX) \ + install-static -j$(NPROC) + +# ----------------------------------------------------------------------------- +# libcbor + +libcbor_VERSION = 0.10.2 +libcbor_GIT_REF = v$(libcbor_VERSION) +libcbor_GIT_REF_HASH = efa6c0886bae46bdaef9b679f61f4b9d8bc296ae +libcbor_GIT_REPO = https://github.com/PJK/libcbor +libcbor_SRCDIR = $(call tp-src-dir,libcbor) + +.PHONY: tp-build-libcbor +tp-build-libcbor: fetch-git-libcbor + cd $(libcbor_SRCDIR) && \ + cmake \ + -DCMAKE_INSTALL_PREFIX=$(THIRDPARTY_PREFIX) \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_EXAMPLES=OFF \ + . + $(MAKE) -C $(libcbor_SRCDIR) -j$(NPROC) + $(MAKE) -C $(libcbor_SRCDIR) install + +# ----------------------------------------------------------------------------- +# openssl + +openssl_VERSION = 3.0.13 +openssl_GIT_REF = openssl-$(openssl_VERSION) +openssl_GIT_REF_HASH = 85cf92f55d9e2ac5aacf92bedd33fb890b9f8b4c +openssl_GIT_REPO = https://github.com/openssl/openssl +openssl_SRCDIR = $(call tp-src-dir,openssl) + +openssl_TARGET_linux_amd64 = linux-x86_64 +openssl_TARGET_linux_arm64 = linux-aarch64 +openssl_TARGET_linux_386 = linux-x86 +#openssl_TARGET_linux_arm = linux-generic32 +openssl_TARGET_linux_arm = linux-armv4 +openssl_TARGET = $(or $(openssl_TARGET_linux_$(ARCH)),$(error Unsupported ARCH ($(ARCH)) for openssl)) + +.PHONY: tp-build-openssl +tp-build-openssl: fetch-git-openssl + cd $(openssl_SRCDIR) && \ + ./config "$(openssl_TARGET)" enable-fips --release -fPIC no-shared \ + --prefix=$(THIRDPARTY_PREFIX) \ + --libdir=$(THIRDPARTY_PREFIX)/lib + $(MAKE) -C $(openssl_SRCDIR) -j$(NPROC) + $(MAKE) -C $(openssl_SRCDIR) install_sw install_ssldirs install_fips + sed "s|@@PREFIX@@|${THIRDPARTY_PREFIX}|" \ + < pkgconfig/libcrypto-static.pc \ + > $(PKG_CONFIG_PATH)/libcrypto-static.pc + +# ----------------------------------------------------------------------------- +# libfido2 + +libfido2_VERSION = 1.14.0 +libfido2_GIT_REF = $(libfido2_VERSION) +libfido2_GIT_REF_HASH = 1a9d335c8f0e821f9eff27482fdda96e59a4f577 +libfido2_GIT_REPO = https://github.com/Yubico/libfido2 +libfido2_SRCDIR = $(call tp-src-dir,libfido2) + +.PHONY: tp-build-libfido2 +tp-build-libfido2: fetch-git-libfido2 + cd $(libfido2_SRCDIR) && \ + cmake \ + -DCMAKE_C_FLAGS="-ldl -pthread" \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_INSTALL_PREFIX=$(THIRDPARTY_PREFIX) \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_MANPAGES=OFF \ + -DBUILD_TOOLS=OFF \ + . + $(MAKE) -C $(libfido2_SRCDIR) -j$(NPROC) + $(MAKE) -C $(libfido2_SRCDIR) install + sed "s|@@PREFIX@@|${THIRDPARTY_PREFIX}|" \ + < pkgconfig/libfido2-static.pc \ + > $(PKG_CONFIG_PATH)/libfido2-static.pc + +# ----------------------------------------------------------------------------- +# libpcsclite +# +# Needed for PIV support in teleport and tsh + +libpcsclite_VERSION = 1.9.9-teleport +libpcsclite_GIT_REF = $(libpcsclite_VERSION) +libpcsclite_GIT_REF_HASH = eb815b51504024c2218471736ba651cef147f368 +libpcsclite_GIT_REPO = https://github.com/gravitational/PCSC +libpcsclite_SRCDIR = $(call tp-src-dir,libpcsclite) + +.PHONY: tp-build-libpcsclite +tp-build-libpcsclite: fetch-git-libpcsclite + cd $(libpcsclite_SRCDIR) && ./bootstrap + cd $(libpcsclite_SRCDIR) && ./configure \ + $(if $(CROSSTOOLNG_TARGET),--target=$(CROSSTOOLNG_TARGET)) \ + $(if $(CROSSTOOLNG_TARGET),--host=$(CROSSTOOLNG_TARGET)) \ + --prefix="$(THIRDPARTY_PREFIX)" \ + --enable-static --with-pic \ + --disable-libsystemd --with-systemdsystemunitdir=no + $(MAKE) -C $(libpcsclite_SRCDIR)/src -j$(NPROC) PROGRAMS= all + $(MAKE) -C $(libpcsclite_SRCDIR)/src PROGRAMS= install diff --git a/build.assets/images.mk b/build.assets/images.mk index 68e45cef25e7e..fa6bda68b512e 100644 --- a/build.assets/images.mk +++ b/build.assets/images.mk @@ -17,6 +17,9 @@ BUILDBOX_ARM = $(BUILDBOX_BASE_NAME)-arm:$(BUILDBOX_VERSION) BUILDBOX_UI = $(BUILDBOX_BASE_NAME)-ui:$(BUILDBOX_VERSION) BUILDBOX_NODE = $(BUILDBOX_BASE_NAME)-node:$(BUILDBOX_VERSION) +BUILDBOX_NG = $(BUILDBOX_BASE_NAME)-ng:$(BUILDBOX_VERSION) +BUILDBOX_THIRDPARTY = $(BUILDBOX_BASE_NAME)-thirdparty:$(BUILDBOX_VERSION) + .PHONY:show-buildbox-base-image show-buildbox-base-image: @echo "$(BUILDBOX)" diff --git a/common.mk b/common.mk index e9269e3dea3b5..94fc6ae733333 100644 --- a/common.mk +++ b/common.mk @@ -5,8 +5,11 @@ # Set $(PKGCONF) to either pkgconf or pkg-config if either are installed # or /usr/bin/false if not. When it is set to "false", running $(PKGCONF) # will exit non-zero with no output. +# +# Before GNU make 4.4, exported variables were not exported for $(shell ...) +# expressions, so explicitly set PKG_CONFIG_PATH when running $(PKGCONF). -PKGCONF := $(firstword $(shell which pkgconf pkg-config false 2>/dev/null)) +PKGCONF := PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(firstword $(shell which pkgconf pkg-config false 2>/dev/null)) # ----------------------------------------------------------------------------- # libbpf detection @@ -42,13 +45,11 @@ LIBBPF_LIBS := -L/usr/libbpf-$(LIBBPF_VER)/lib64 -lbpf # libbpf needs libelf. Try to find it with pkg-config/pkgconf and fallback to # hard-coded defaults if pkg-config says nothing. LIBBPF_LIBS += $(or $(shell $(PKGCONF) --silence-errors --static --libs libelf),-lelf -lz) -else -ifneq (,$(shell $(PKGCONF) --exists 'libbpf = $(LIBBPF_VER)' && echo true)) +else ifneq (,$(shell $(PKGCONF) --exists 'libbpf = $(LIBBPF_VER)' && echo true)) FOUND_LIBBPF := true LIBBPF_INCLUDES := $(shell $(PKGCONF) --cflags libbpf) LIBBPF_LIBS := $(shell $(PKGCONF) --libs --static libbpf) endif -endif # Is this build targeting the same OS & architecture it is being compiled on, or # will it require cross-compilation? We need to know this (especially for ARM) so we