Skip to content

Commit bcfe7a5

Browse files
[8.x](backport #4318) FIPS compliant builds (#4342)
* FIPS compliant builds (#4318) Add FIPS env flag to enable FIPS mode. FIPS=true will change the following: - PLATFORMS will default to linux/amd64 linux/arm64 - make local, make release-* - Binary will be build with -tags=fipsrequired and GOEXPERIMENT=systemcrypto - make build-releaser - chaingaurd microsoft go image will be used as base - make multipass - microsoft's go toolchain will be downloaded and installed to VM (cherry picked from commit f1c29b9) # Conflicts: # Makefile # dev-tools/multipass-cloud-init.yml * Fix merge --------- Co-authored-by: Michel Laterman <82832767+michel-laterman@users.noreply.github.com> Co-authored-by: michel-laterman <michel.laterman@elastic.co>
1 parent dcf4beb commit bcfe7a5

File tree

5 files changed

+201
-13
lines changed

5 files changed

+201
-13
lines changed

.buildkite/scripts/test-release.sh

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ set -euo pipefail
55
FLEET_SERVER_VERSION=${1:?"Fleet Server version is needed"}
66

77
PLATFORM_FILES=(darwin-aarch64.tar.gz darwin-x86_64.tar.gz linux-arm64.tar.gz linux-x86_64.tar.gz windows-x86_64.zip)
8+
if [ "$FIPS" = "true" ] ; then
9+
PLATFORM_FILES=(linux-arm64-fips.tar.gz linux-x86_64-fips.tar.gz)
10+
fi
811

912
#make release
1013

Dockerfile.fips

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
ARG GO_VERSION
2+
FROM mcr.microsoft.com/oss/go/microsoft/golang:${GO_VERSION}-1-fips-bookworm AS base
3+
4+
ENV FIPS=true
5+
ENV CGO_ENABLED=1
6+
7+
RUN mkdir -p /.cache \
8+
&& chmod 777 /.cache
9+
10+
WORKDIR /go/src/github.com/elastic/fleet-server
11+
12+
# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change
13+
COPY go.mod go.sum ./
14+
RUN go mod download && go mod verify
15+
16+
ENTRYPOINT [ "make" ]
17+
CMD [ "release" ]
18+
19+
FROM base AS builder
20+
21+
COPY . .
22+
23+
ARG GCFLAGS=""
24+
ARG LDFLAGS=""
25+
ARG DEV=""
26+
ARG TARGETPLATFORM
27+
28+
RUN FIPS=true CGO_ENABLED=1 GCFLAGS="${GCFLAGS}" LDFLAGS="${LDFLAGS}" DEV="${DEV}" make release-${TARGETPLATFORM}
29+
30+
FROM docker.elastic.co/wolfi/glibc-openssl-fips:latest
31+
ARG VERSION
32+
ARG TARGETOS
33+
ARG TARGETARCH
34+
35+
RUN groupadd --gid 1000 fleet-server && \
36+
useradd -M --uid 1000 --gid 1000 fleet-server
37+
38+
USER fleet-server
39+
40+
COPY --chown=fleet-server:fleet-server --chmod=644 fleet-server.yml /etc/fleet-server.yml
41+
COPY --chown=fleet-server:fleet-server --chmod=755 --from=builder /usr/src/fleet-server/build/binaries/fleet-server-${VERSION}-${TARGETOS:-linux}-*/fleet-server /usr/bin/fleet-server
42+
43+
ENV GOFIPS=1
44+
45+
CMD /usr/bin/fleet-server -c /etc/fleet-server.yml

Makefile

+41-11
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,45 @@ GOBIN=$(shell go env GOPATH)/bin/
6262

6363
OS_NAME:=$(shell uname -s)
6464

65+
# Set FIPS=true to force FIPS compliance when building
66+
FIPS?=
67+
ifeq "${FIPS}" "true"
68+
BUILDER_IMAGE=fleet-server-fips-builder:${GO_VERSION}
69+
PLATFORMS = linux/amd64 linux/arm64
70+
endif
71+
72+
.EXPORT_ALL_VARIABLES:
73+
FIPS=${FIPS}
74+
6575
.PHONY: help
6676
help: ## - Show help message
6777
@printf "${CMD_COLOR_ON} usage: make [target]\n\n${CMD_COLOR_OFF}"
6878
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | sed -e "s/^Makefile://" | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
6979

80+
.PHONY: multipass
81+
multipass: ## - Launch a multipass instance for development
82+
ifeq ($(shell uname -p),arm)
83+
$(eval ARCH := arm64)
84+
else
85+
$(eval ARCH := amd64)
86+
endif
87+
ifeq "${FIPS}" "true"
88+
$(eval DOWNLOAD_URL := https://aka.ms/golang/release/latest/go${GO_VERSION}-1.linux-${ARCH}.tar.gz)
89+
else
90+
$(eval DOWNLOAD_URL := https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz)
91+
endif
92+
@cat dev-tools/multipass-cloud-init.yml.envsubst | DOWNLOAD_URL=${DOWNLOAD_URL} ARCH=${ARCH} envsubst > dev-tools/multipass-cloud-init.yml
93+
@multipass launch --cloud-init=dev-tools/multipass-cloud-init.yml --mount ..:~/git --name fleet-server-dev --memory 8G --cpus 2 --disk 50G noble
94+
@rm dev-tools/multipass-cloud-init.yml
95+
7096
.PHONY: list-platforms
7197
list-platforms: ## - Show the possible PLATFORMS
7298
@echo "${PLATFORMS}"
7399

74100
.PHONY: local
75101
local: ## - Build local binary for local environment (bin/fleet-server)
76102
@printf "${CMD_COLOR_ON} Build binaries using local go installation\n${CMD_COLOR_OFF}"
77-
go build $(if $(SNAPSHOT),-tags="snapshot",) -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" -o ./bin/fleet-server .
103+
$(if $(FIPS),GOEXPERIMENT=systemcrypto) go build $(if $(SNAPSHOT),-tags="snapshot",) $(if $(FIPS),-tags="requirefips",) -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" -o ./bin/fleet-server .
78104
@printf "${CMD_COLOR_ON} Binaries in ./bin/\n${CMD_COLOR_OFF}"
79105

80106
.PHONY: $(COVER_TARGETS)
@@ -84,7 +110,7 @@ $(COVER_TARGETS): cover-%: ## - Build a binary with the -cover flag for integrat
84110
$(eval $@_GO_ARCH := $(lastword $(subst /, ,$(lastword $(subst cover-, ,$@)))))
85111
$(eval $@_ARCH := $(TARGET_ARCH_$($@_GO_ARCH)))
86112
$(eval $@_BUILDMODE:= $(BUILDMODE_$($@_OS)_$($@_GO_ARCH)))
87-
GOOS=$($@_OS) GOARCH=$($@_GO_ARCH) go build $(if $(SNAPSHOT),-tags="snapshot",) -cover -coverpkg=./... -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" $($@_BUILDMODE) -o build/cover/fleet-server-$(VERSION)-$($@_OS)-$($@_ARCH)/fleet-server$(if $(filter windows,$($@_OS)),.exe,) .
113+
GOOS=$($@_OS) GOARCH=$($@_GO_ARCH) $(if $(FIPS),GOEXPERIMENT=systemcrypto) go build $(if $(SNAPSHOT),-tags="snapshot",) $(if $(FIPS),-tags="requirefips",) -cover -coverpkg=./... -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" $($@_BUILDMODE) -o build/cover/fleet-server-$(VERSION)-$($@_OS)-$($@_ARCH)/fleet-server$(if $(filter windows,$($@_OS)),.exe,) .
88114

89115
.PHONY: clean
90116
clean: ## - Clean up build artifacts
@@ -159,11 +185,11 @@ test-release: ## - Check that all release binaries are created
159185

160186
.PHONY: test-unit
161187
test-unit: prepare-test-context ## - Run unit tests only
162-
set -o pipefail; go test ${GO_TEST_FLAG} -v -race -coverprofile=build/coverage-${OS_NAME}.out ./... | tee build/test-unit-${OS_NAME}.out
188+
set -o pipefail; go test ${GO_TEST_FLAG} $(if $(FIPS),-tags="requirefips",) -v -race -coverprofile=build/coverage-${OS_NAME}.out ./... | tee build/test-unit-${OS_NAME}.out
163189

164190
.PHONY: benchmark
165191
benchmark: prepare-test-context install-benchstat ## - Run benchmark tests only
166-
set -o pipefail; go test -bench=$(BENCHMARK_FILTER) -run=$(BENCHMARK_FILTER) $(BENCHMARK_ARGS) $(BENCHMARK_PACKAGE) | tee "build/$(BENCH_BASE)"
192+
set -o pipefail; go test -bench=$(BENCHMARK_FILTER) $(if $(FIPS),-tags="requirefips",) -run=$(BENCHMARK_FILTER) $(BENCHMARK_ARGS) $(BENCHMARK_PACKAGE) | tee "build/$(BENCH_BASE)"
167193

168194
.PHONY: install-benchstat
169195
install-benchstat: ## - Install the benchstat package
@@ -199,7 +225,7 @@ $(PLATFORM_TARGETS): release-%:
199225
$(eval $@_GO_ARCH := $(lastword $(subst /, ,$(lastword $(subst release-, ,$@)))))
200226
$(eval $@_ARCH := $(TARGET_ARCH_$($@_GO_ARCH)))
201227
$(eval $@_BUILDMODE:= $(BUILDMODE_$($@_OS)_$($@_GO_ARCH)))
202-
GOOS=$($@_OS) GOARCH=$($@_GO_ARCH) go build $(if $(SNAPSHOT),-tags="snapshot",) -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" $($@_BUILDMODE) -o build/binaries/fleet-server-$(VERSION)-$($@_OS)-$($@_ARCH)/fleet-server .
228+
GOOS=$($@_OS) GOARCH=$($@_GO_ARCH) $(if $(FIPS),GOEXPERIMENT=systemcrypto) go build $(if $(SNAPSHOT),-tags="snapshot",) $(if $(FIPS),-tags="requirefips",) -gcflags="${GCFLAGS}" -ldflags="${LDFLAGS}" $($@_BUILDMODE) -o build/binaries/fleet-server-$(VERSION)-$($@_OS)-$($@_ARCH)$(if $(FIPS),-fips,)/fleet-server .
203229
@$(MAKE) OS=$($@_OS) ARCH=$($@_ARCH) package-target
204230

205231
.PHONY: build-docker
@@ -236,14 +262,14 @@ package-target: build/distributions
236262
ifeq ($(OS),windows)
237263
@mv build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH)/fleet-server build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH)/fleet-server.exe
238264
@cd build/binaries && zip -q -r ../distributions/fleet-server-$(VERSION)-$(OS)-$(ARCH).zip fleet-server-$(VERSION)-$(OS)-$(ARCH)
239-
@cd build/distributions && shasum -a 512 fleet-server-$(VERSION)-$(OS)-$(ARCH).zip > fleet-server-$(VERSION)-$(OS)-$(ARCH).zip.sha512
265+
@cd build/distributions && sha512sum fleet-server-$(VERSION)-$(OS)-$(ARCH).zip > fleet-server-$(VERSION)-$(OS)-$(ARCH).zip.sha512
240266
else ifeq ($(OS)-$(ARCH),darwin-arm64)
241267
@mv build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH) build/binaries/fleet-server-$(VERSION)-$(OS)-aarch64
242268
@tar -C build/binaries -zcf build/distributions/fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz fleet-server-$(VERSION)-$(OS)-aarch64
243-
@cd build/distributions && shasum -a 512 fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz > fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz.sha512
269+
@cd build/distributions && sha512sum fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz > fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz.sha512
244270
else
245-
@tar -C build/binaries -zcf build/distributions/fleet-server-$(VERSION)-$(OS)-$(ARCH).tar.gz fleet-server-$(VERSION)-$(OS)-$(ARCH)
246-
@cd build/distributions && shasum -a 512 fleet-server-$(VERSION)-$(OS)-$(ARCH).tar.gz > fleet-server-$(VERSION)-$(OS)-$(ARCH).tar.gz.sha512
271+
@tar -C build/binaries -zcf build/distributions/fleet-server-$(VERSION)-$(OS)-$(ARCH)$(if $(FIPS),-fips,).tar.gz fleet-server-$(VERSION)-$(OS)-$(ARCH)$(if $(FIPS),-fips,)
272+
@cd build/distributions && sha512sum fleet-server-$(VERSION)-$(OS)-$(ARCH)$(if $(FIPS),-fips,).tar.gz > fleet-server-$(VERSION)-$(OS)-$(ARCH)$(if $(FIPS),-fips,).tar.gz.sha512
247273
endif
248274

249275
build-releaser: ## - Build a Docker image to run make package including all build tools
@@ -252,16 +278,20 @@ ifeq ($(shell uname -p),arm)
252278
else
253279
$(eval SUFFIX := ${CROSSBUILD_SUFFIX})
254280
endif
281+
ifeq "${FIPS}" "true"
282+
docker build -t $(BUILDER_IMAGE) -f Dockerfile.fips --target base --build-arg GO_VERSION=$(GO_VERSION) .
283+
else
255284
docker build -t $(BUILDER_IMAGE) -f Dockerfile.build --build-arg GO_VERSION=$(GO_VERSION) --build-arg SUFFIX=${SUFFIX} .
285+
endif
256286

257287
.PHONY: docker-release
258288
docker-release: build-releaser ## - Builds a release for all platforms in a dockerised environment
259289
docker run --rm -u $(shell id -u):$(shell id -g) --volume $(PWD):/go/src/github.com/elastic/fleet-server $(BUILDER_IMAGE) release
260290

261291
.PHONY: docker-cover-e2e-binaries
262292
docker-cover-e2e-binaries: build-releaser
263-
## Build for local architecture and for linux/amd64 for docker images.
264-
docker run --rm -u $(shell id -u):$(shell id -g) --volume $(PWD):/go/src/github.com/elastic/fleet-server -e SNAPSHOT=true $(BUILDER_IMAGE) cover-linux/$(shell go env GOARCH) cover-$(shell go env GOOS)/$(shell go env GOARCH)
293+
## Build for local architecture and for linux/$ARCH for docker images.
294+
docker run --rm -u $(shell id -u):$(shell id -g) --volume $(PWD):/go/src/github.com/elastic/fleet-server -e SNAPSHOT=true $(if $(FIPS),-e FIPS=true) $(BUILDER_IMAGE) cover-linux/$(shell go env GOARCH) cover-$(shell go env GOOS)/$(shell go env GOARCH)
265295

266296
.PHONY: release
267297
release: $(PLATFORM_TARGETS) ## - Builds a release. Specify exact platform with PLATFORMS env.

dev-tools/multipass-cloud-init.yml dev-tools/multipass-cloud-init.yml.envsubst

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
apt:
1111
sources:
1212
docker.list:
13-
source: deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable
13+
source: deb [arch=${ARCH}] https://download.docker.com/linux/ubuntu noble stable
1414
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
1515
groups:
1616
- docker
@@ -32,9 +32,12 @@ packages:
3232
- containerd.io
3333
- docker-buildx-plugin
3434
- docker-compose-plugin
35+
- unzip
36+
- zip
3537
write_files:
3638
- content: |
3739
export PATH=$PATH:/usr/local/go/bin
40+
export PATH=$PATH:$(go env GOPATH)/bin
3841
path: /etc/profile.d/go-bin.sh
3942
# backwords compatibilityi alias for docker-compose
4043
# makefile does not use the alias
@@ -43,6 +46,8 @@ write_files:
4346
path: /etc/profile.d/docker-compose-alias.sh
4447
runcmd:
4548
- [ mkdir, /run/go ]
46-
- [ wget, https://go.dev/dl/go1.20.4.linux-amd64.tar.gz, -O, /run/go/go.tar.gz ] # FIXME don't hardcode go version
49+
- [ wget, ${DOWNLOAD_URL}, -O, /run/go/go.tar.gz ]
4750
- [ tar, -xzf, /run/go/go.tar.gz, -C, /usr/local/ ]
4851
- [ rm, -rf, /run/go ]
52+
- [ su, ubuntu, -c, "/usr/local/go/bin/go install github.com/magefile/mage@latest" ]
53+
- [ su, ubuntu, -c, "/usr/local/go/bin/go install github.com/go-delve/delve/cmd/dlv@latest" ]

docs/fips.md

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# FIPS support
2+
3+
**NOTE: FIPS Support is in-progress**
4+
5+
The fleet-server can be built in FIPS mode.
6+
This forces the use of a FIPS compliant provider to handle any cryptographic calls.
7+
8+
Currently FIPS is provided by compiling with the [microsoft/go](https://github.com/microsoft/go) distribution.
9+
This toolchain must be present for local compilation.
10+
11+
12+
## Build changes
13+
14+
As we are using Microsfot/go as a base we follow their conventions.
15+
16+
The buildtag `requirefips` is passed when FIPS is enabled/required.
17+
Additionally when compiling `GOEXPERIMENT=systemcrypto` is specified.
18+
19+
The `FIPS=true` env var is used by our Makefile as the indicator that controls FIPS.
20+
This env var is also passed to every child process the Makefile starts.
21+
The following make commands have different behaviour when FIPS is enabled:
22+
23+
- `make multipass` - Provision a multipass VM with the Microsoft/go toolchain. See [Multipass VM Usage](#multipass-vm-usage) for additional details.
24+
- `make local` - Compile a fleet-server targetting the machine's GOOS/GOARCH with FIPS enabled
25+
- `make cover-*` - Compile a coverage and fips enabled fleet-server for e2e tests
26+
- `make test-unit` - Run unit tests passing the `requirefips` build tag.
27+
- `make benchmark` - Run benchmarks passing the `requirefips` build tag.
28+
- `make release-*` - Compile a release binary with FIPS enabled. Will have the name fleet-server-$VERSION-$OS-$ARCH-fips
29+
- `make package-target` - Will package a FIPS enabled release and produce the sha512 checksum for it.
30+
- `make build-releaser` - Will create the fleet-server builder image based on Microsoft's FIPS enabled golang image.
31+
- `make docker-release` - Runs `make release` to produce FIPS enabled binaries in a FIPS docker container.
32+
- `make docker-cover-e2e-binaries` - Will produce coverage and FIPS enabled binaries for e2e tests from within the same docker container that `build-release` makes
33+
34+
### Multipass VM Usage
35+
36+
A Multipass VM created with `FIPS=true make multipass` is able to compile FIPS enabled golang programs, but is not able to run them.
37+
When you try to run one the following error occurs:
38+
```
39+
GOFIPS=1 ./bin/fleet-server -c fleet-server.yml
40+
panic: opensslcrypto: can't enable FIPS mode for OpenSSL 3.0.13 30 Jan 2024: openssl: FIPS mode not supported by any provider
41+
42+
goroutine 1 [running]:
43+
crypto/internal/backend.init.1()
44+
/usr/local/go/src/crypto/internal/backend/openssl_linux.go:85 +0x210
45+
```
46+
47+
In order to be able to run a FIPS enabled binary, openssl must have a fips provider.
48+
Openssl [provides instructions on how to do this](https://github.com/openssl/openssl/blob/master/README-FIPS.md).
49+
50+
A TLDR for our multipass container is:
51+
52+
1. Download and compile the FIPS provider for openssl in the VM by running:
53+
```
54+
wget https://github.com/openssl/openssl/releases/download/openssl-3.0.13/openssl-3.0.13.tar.gz
55+
tar -xzf openssl-3.0.13.tar.gz
56+
cd openssl-3.0.13
57+
./Configure enable-fips
58+
make test
59+
sudo make install_fips
60+
sudo openssl fipsinstall -out /usr/local/ssl/fipsmodule.cnf -module /usr/local/lib/ossl-modules/fips.so
61+
```
62+
63+
2. Copy the `fips.so` module to the system library, in order to find the location run:
64+
```
65+
openssl version -m
66+
```
67+
68+
On my VM I would copy the `fips.so` module with:
69+
```
70+
sudo cp /usr/local/lib/ossl-modules/fips.so /usr/lib/aarch64-linux-gnu/ossl-modules/fips.so
71+
```
72+
73+
3. Create an openssl.cnf for the program to use with the contents:
74+
```
75+
config_diagnostics = 1
76+
openssl_conf = openssl_init
77+
78+
.include /usr/local/ssl/fipsmodule.cnf
79+
80+
[openssl_init]
81+
providers = provider_sect
82+
alg_section = algorithm_sect
83+
84+
[provider_sect]
85+
fips = fips_sect
86+
base = base_sect
87+
88+
[base_sect]
89+
activate = 1
90+
91+
[algorithm_sect]
92+
default_properties = fips=yes
93+
```
94+
95+
4. Run the program with the `OPENSSL_CONF=openssl.cnf` and `GOFIPS=1` env vars, i.e.,
96+
```
97+
OPENSSL_CONF=./openssl.cnf GOFIPS=1 ./bin/fleet-server -c fleet-server.yml
98+
23:48:47.871 INF Boot fleet-server args=["-c","fleet-server.yml"] commit=55104f6f ecs.version=1.6.0 exe=./bin/fleet-server pid=65037 ppid=5642 service.name=fleet-server service.type=fleet-server version=9.0.0
99+
i...
100+
```
101+
102+
## Usage
103+
104+
A FIPS enabled binary should be ran with the env var `GOFIPS=1` set.
105+
The system/image is required to have a FIPS compliant provider available.

0 commit comments

Comments
 (0)