Skip to content

Commit

Permalink
Build FIPS capable executables in a docker container
Browse files Browse the repository at this point in the history
Build FIPS capable executables in a docker container
  • Loading branch information
sumgarg authored Jan 22, 2025
2 parents 9fb6530 + 60edcd9 commit 902cd31
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 74 deletions.
116 changes: 116 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#
# Dockerfile to build telegraf in a container.
#

#
# Global build args that can be passed from the builder:
#
ARG BASE_GOLANG_VER="1.21"
ARG FIPS_GOLANG_VER="${BASE_GOLANG_VER}"

ARG GOLANG_FIPS_BUILD_ROOT="/root/go/golang-fips"
ARG TELEGRAF_BUILD_ROOT="/root/go/telegraf"

# When BUILD_GO_FIPS=1, build a dynamically linked executable using go patched
# with golang-fips patch and cgo (CGO_ENABLED=1). Otherwise, build a statically
# linked executable with the standard go.
ARG BUILD_GO_FIPS=

# Typically set to any GO specific environment variables; see build.sh for how
# this is used.
ARG BUILD_GO_OPTS=


# Start with the base golang container and get a custom golang image.
FROM "golang:${BASE_GOLANG_VER}-bookworm" AS golang
ARG \
BASE_GOLANG_VER \
FIPS_GOLANG_VER \
GOLANG_FIPS_BUILD_ROOT \
BUILD_GO_FIPS
ARG GO_SRC_BRANCH="release-branch.go${FIPS_GOLANG_VER}"
ARG GOLANG_FIPS_BRANCH="go${FIPS_GOLANG_VER}-fips-release"

ADD --keep-git-dir=true "https://github.com/golang-fips/go.git#${GOLANG_FIPS_BRANCH}" "${GOLANG_FIPS_BUILD_ROOT}"

RUN ln -sf /bin/bash /bin/sh
RUN \
if [[ "${BUILD_GO_FIPS}" == "1" ]]; then \
git config --global user.email "[email protected]" && \
git config --global user.name "Dev Extreme" && \
cd "${GOLANG_FIPS_BUILD_ROOT}" && \
./scripts/full-initialize-repo.sh "${GO_SRC_BRANCH}" && \
: ; \
fi \
&& \
:


# Copy telegraf sources into the golang build environment.
FROM golang AS source
ARG TELEGRAF_BUILD_ROOT

WORKDIR "${TELEGRAF_BUILD_ROOT}"

COPY .git .git
COPY agent agent
COPY cmd cmd
COPY config config
COPY filter filter
COPY internal internal
COPY logger logger
COPY metric metric
COPY models models
COPY plugins plugins
COPY selfstat selfstat
COPY *.go go.* ./
COPY build_version.txt ./
COPY Makefile ./


# Build telegraf using the custom golang container.
FROM source AS build
ARG \
GOLANG_FIPS_BUILD_ROOT \
BUILD_GO_FIPS \
BUILD_GO_OPTS

RUN \
export LDFLAGS="-w -s" ${BUILD_GO_OPTS} \
&& \
git config --global user.email "[email protected]" \
&& \
git config --global user.name "Dev Extreme" \
&& \
export \
&& \
if [[ "${BUILD_GO_FIPS}" == "1" ]]; then \
apt-get update --yes && \
apt-get install --yes --no-install-recommends --no-install-suggests libssl-dev && \
if [[ "${GOARCH}" == "mips" ]]; then \
apt-get install --yes --no-install-recommends --no-install-suggests gcc-multilib && \
: ; \
fi && \
rm -rf /var/lib/apt/lists/* && \
export PATH="${GOLANG_FIPS_BUILD_ROOT}/bin:${PATH}" && \
export CGO_ENABLED=1 && \
: ; \
else \
export CGO_ENABLED=0 && \
: ; \
fi \
&& \
make telegraf && \
:


# Copy the binary to an empty container.
FROM scratch AS binary
ARG TELEGRAF_BUILD_ROOT

WORKDIR /

COPY --from=build "${TELEGRAF_BUILD_ROOT}/telegraf" /usr/bin/

ENTRYPOINT ["/usr/bin/telegraf"]
CMD ["--help"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
HOSTGO := env -u GOOS -u GOARCH -u GOARM -- go

LDFLAGS := $(LDFLAGS) -X main.commit=$(commit) -X main.branch=$(branch) -X main.goos=$(GOOS) -X main.goarch=$(GOARCH)
LDFLAGS := $(LDFLAGS) -X main.commit=$(commit) -X main.branch=$(branch) -X main.goos=$(GOOS) -X main.goarch=$(GOARCH)
ifeq ($(GOARCH), arm)
LDFLAGS := $(LDFLAGS) -X main.goarm=$(GOARM)
endif
Expand Down
232 changes: 160 additions & 72 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

# Simple ExtremeNetworks script to facilitate building and
# uploading the telegraf utility.
Expand All @@ -8,98 +8,186 @@
# The extr_version is the version used when we make Extreme specific changes on
# a particular telegraf branch.
#
set -e

export LDFLAGS="-w -s"

# Default ARM type for arm architecture
arm_type=5

usage()
show_usage()
{
echo "usage: $0 arch {build | upload}"
echo " . arch : valid architectures: arm, arm64, x86_64, mips"
echo " . build : build and tar utility for specified architecture"
echo " . upload: upload specified architecture's tar to Artifactory"
echo "usage: ${0} <arch> {build | upload}"
echo " . arch : valid architectures: arm, arm64, x86_64, mips"
echo " . build : build and tar utility for specified architecture"
echo " . upload : upload specified architecture's tar to Artifactory"
}

build()
do_build()
{
make clean
rm -f ${target}
make CGO_ENABLED=0 GOOS=linux GOARCH=${bld_arch} GOARM=${arm_type} GOPROXY=https://proxy.golang.org,direct GOSUMDB=sum.golang.org
tar -cf ${target} telegraf MIT generic_MIT
local _extr_arch="${1}"
local _target_tarball="${2}"

# set the target architecture for the executable
local _target_arch="${_extr_arch}"
if [[ "${_target_arch}" = "x86_64" ]]; then
_target_arch="amd64"
fi

# default to building a FIPS executable
local _go_fips=1
if [[ "${_target_arch}" == "arm" || "${_target_arch}" == "mips" ]]; then
# except for 32 bit ARM and MIPS
_go_fips=0
fi

# options passed to GO
local _go_opts=""
if [[ "${_target_arch}" == "arm" ]]; then
# set GOARM only when building for 32 bit ARM
_go_opts+="${_go_opts:+ }GOARM=${arm32_type}"
elif [[ "${_target_arch}" == "mips" ]]; then
# set GOARCH to build a 32 bit executable for MIPS
_go_opts+="${_go_opts:+ }GOARCH=${_target_arch}"
fi

rm -f "${_target_tarball}"

#
# REF: https://docs.docker.com/build/building/multi-platform/#qemu
#
# We do not leverage the cross compilation feature of golang to build the executable;
# in other words, GOOS and GOARCH are (typically) not passed to the go compiler.
# Instead we build on a node (i.e. container) that has the same CPU as that target;
# in other words:
# arch of container running golang compiler
# == target arch
# This node can be one of the following:
# 1. the host on which docker is running (typically amd64 or arm64) i.e.
# arch of host running the golang containers used to build the executable
# == arch of container running golang compiler
# 2. a container running under qemu (qemu is provided by the host) i.e.
# arch of host running the golang containers used to build the executable
# != arch of container running golang compiler
#
# For #2 above, the container effectively runs under qemu. You might need to run
# the following command to install qemu on the host:
# docker run --privileged --rm tonistiigi/binfmt --install linux/${_container_arch}
#
# NOTE: We are not building a multi-platform image as that requires some capabilities
# in the image store that might/might not be present in the docker install.
#

# architecture of the container to use for building
local _container_arch="${_target_arch}"
if [[ "${_container_arch}" == "mips" ]]; then
# use "mips64le" for the container arch when target arch is "mips"
_container_arch="mips64le"
fi

local _dockerfile_stage="binary"
local _target_image="telegraf/${_dockerfile_stage}/${_target_arch}:$(git describe --dirty)"
docker buildx build --progress plain \
--build-arg BUILD_GO_FIPS="${_go_fips}" \
--build-arg BUILD_GO_OPTS="${_go_opts}" \
--platform "linux/${_container_arch}" \
--tag "${_target_image}" \
--target "${_dockerfile_stage}" \
.

local _copy_container="$(docker container create --quiet "${_target_image}")"
docker container cp "${_copy_container}:/usr/bin/telegraf" telegraf
docker container rm "${_copy_container}"

docker image rm "${_target_image}"

tar -cf "${_target_tarball}" telegraf MIT generic_MIT
rm -f telegraf
}

upload()
do_upload()
{
if [ ! -f ${target} ]; then
echo "info: ${target} not found; building first..."
build
if [ ! -f ${target} ]; then
echo "error: could not find or build '${target}' tarball"
local _extr_arch="${1}"
local _target_tarball="${2}"

if [[ ! -f "${_target_tarball}" ]]; then
echo "info: ${_target_tarball} not found; building first..."
do_build "${_extr_arch}" "${_target_tarball}"
if [[ ! -f "${_target_tarball}" ]]; then
echo "error: could not find or build '${_target_tarball}' tarball"
exit 1
fi
fi

# make sure jfrog config is set up
if ! jfrog config show ${salem} &>/dev/null ; then
echo "Could not find Salem Artifactory config. Let's create one..."
echo "Accept defaults and use your corporate password."; echo ""
jfrog config add --url http://engartifacts1.extremenetworks.com:8081 --user $(whoami) ${salem}
if ! jfrog config show ${salem} &>/dev/null ; then
echo "error: failed to configure ${salem} Artifactory server access"
if ! jfrog config show "${afy_server_name}" &>/dev/null ; then
echo "Could not find ${afy_server_name} Artifactory config. Let's create one..."
echo "Accept defaults and use your corporate password."; echo
jfrog config add --url "${afy_server_url}" --user "${USER}" "${afy_server_name}"
if ! jfrog config show "${afy_server_name}" &>/dev/null ; then
echo "error: failed to configure ${afy_server_name} Artifactory server access"
exit 1
fi
fi
jfrog rt upload ${target} xos-binaries-local-release/telegraf/${arch}/${target} --server-id ${salem}

jfrog rt upload "${_target_tarball}" "${aft_repo}/${_extr_arch}/${_target_tarball}" --server-id "${afy_server_name}"
}

#######################
# execution starts here
#######################
if [[ -z "$1" || "$1" == "--help" || "$1" == "-h" || "$1" == "?" ]]; then
usage
exit 0
fi

# force go to use alternate location of modules
go env -w GOMODCACHE=/opt/go/pkg/mod
go env -w GOCACHE=$(pwd -P)/.cache/go-build
# grab version strings
telegraf_version=$(cat build_version.txt)
extr_version=$(cat extr_version.txt)
salem=Salem

# set architecture name and build architecture as used by golang
arch=$1
if [ "$arch" = "x86_64" ]; then
bld_arch=amd64
else
bld_arch=${arch}
fi
target=telegraf_${arch}_${telegraf_version}.${extr_version}.tar

# check action argument
case $2 in
build | upload)
action=$2
;;
*)
echo "error: invalid action '$2'"
usage
exit 1
esac

# perform action
case $1 in
arm64 | mips | x86_64 | arm)
$action
;;
*)
echo "error: invalid architecture '$1'"
usage

main()
{
# all hard-coded globals go here
arm32_type="5" # default ARM type for "arm" architecture
afy_server_name="Salem"
afy_server_url="http://engartifacts1.extremenetworks.com:8081"
aft_repo="xos-binaries-local-release/telegraf"

local _extr_arch="${1}"
local _script_action="${2}"

# verify inputs - count of args
if [[ ${#} -ne 2 ]]; then
echo "error: incorrect number of arguments '${#}'"
show_usage
exit 1
esac
fi

# verify inputs - architecture
case "${_extr_arch}" in
arm64 | mips | x86_64 | arm)
:
;;
--help | -h | -?)
show_usage
exit 0
;;
*)
echo "error: invalid architecture '${1}'"
show_usage
exit 1
esac

# verify inputs - action
case "${_script_action}" in
build | upload)
:
;;
--help | -h | -?)
show_usage
exit 0
;;
*)
echo "error: invalid action '$2'"
show_usage
exit 1
esac

# grab version strings and set the name of the target tarball
local _telegraf_version="$(< build_version.txt)"
local _extr_version="$(< extr_version.txt)"
local _target_tarball="telegraf_${_extr_arch}_${_telegraf_version}.${_extr_version}.tar"

"do_${_script_action}" "${_extr_arch}" "${_target_tarball}"
}

set -o errexit
set -o pipefail

main "${@}"
2 changes: 1 addition & 1 deletion extr_version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
12
13

0 comments on commit 902cd31

Please sign in to comment.