Skip to content

Commit f1c29b9

Browse files
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
1 parent 8899271 commit f1c29b9

File tree

5 files changed

+185
-13
lines changed

5 files changed

+185
-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

+31-12
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ 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}"
@@ -74,7 +84,12 @@ ifeq ($(shell uname -p),arm)
7484
else
7585
$(eval ARCH := amd64)
7686
endif
77-
@cat dev-tools/multipass-cloud-init.yml.envsubst | GO_VERSION=${GO_VERSION} ARCH=${ARCH} envsubst > dev-tools/multipass-cloud-init.yml
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
7893
@multipass launch --cloud-init=dev-tools/multipass-cloud-init.yml --mount ..:~/git --name fleet-server-dev --memory 8G --cpus 2 --disk 50G noble
7994
@rm dev-tools/multipass-cloud-init.yml
8095

@@ -85,7 +100,7 @@ list-platforms: ## - Show the possible PLATFORMS
85100
.PHONY: local
86101
local: ## - Build local binary for local environment (bin/fleet-server)
87102
@printf "${CMD_COLOR_ON} Build binaries using local go installation\n${CMD_COLOR_OFF}"
88-
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 .
89104
@printf "${CMD_COLOR_ON} Binaries in ./bin/\n${CMD_COLOR_OFF}"
90105

91106
.PHONY: $(COVER_TARGETS)
@@ -95,7 +110,7 @@ $(COVER_TARGETS): cover-%: ## - Build a binary with the -cover flag for integrat
95110
$(eval $@_GO_ARCH := $(lastword $(subst /, ,$(lastword $(subst cover-, ,$@)))))
96111
$(eval $@_ARCH := $(TARGET_ARCH_$($@_GO_ARCH)))
97112
$(eval $@_BUILDMODE:= $(BUILDMODE_$($@_OS)_$($@_GO_ARCH)))
98-
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,) .
99114

100115
.PHONY: clean
101116
clean: ## - Clean up build artifacts
@@ -170,11 +185,11 @@ test-release: ## - Check that all release binaries are created
170185

171186
.PHONY: test-unit
172187
test-unit: prepare-test-context ## - Run unit tests only
173-
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
174189

175190
.PHONY: benchmark
176191
benchmark: prepare-test-context install-benchstat ## - Run benchmark tests only
177-
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)"
178193

179194
.PHONY: install-benchstat
180195
install-benchstat: ## - Install the benchstat package
@@ -210,7 +225,7 @@ $(PLATFORM_TARGETS): release-%:
210225
$(eval $@_GO_ARCH := $(lastword $(subst /, ,$(lastword $(subst release-, ,$@)))))
211226
$(eval $@_ARCH := $(TARGET_ARCH_$($@_GO_ARCH)))
212227
$(eval $@_BUILDMODE:= $(BUILDMODE_$($@_OS)_$($@_GO_ARCH)))
213-
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 .
214229
@$(MAKE) OS=$($@_OS) ARCH=$($@_ARCH) package-target
215230

216231
.PHONY: build-docker
@@ -247,14 +262,14 @@ package-target: build/distributions
247262
ifeq ($(OS),windows)
248263
@mv build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH)/fleet-server build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH)/fleet-server.exe
249264
@cd build/binaries && zip -q -r ../distributions/fleet-server-$(VERSION)-$(OS)-$(ARCH).zip fleet-server-$(VERSION)-$(OS)-$(ARCH)
250-
@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
251266
else ifeq ($(OS)-$(ARCH),darwin-arm64)
252267
@mv build/binaries/fleet-server-$(VERSION)-$(OS)-$(ARCH) build/binaries/fleet-server-$(VERSION)-$(OS)-aarch64
253268
@tar -C build/binaries -zcf build/distributions/fleet-server-$(VERSION)-$(OS)-aarch64.tar.gz fleet-server-$(VERSION)-$(OS)-aarch64
254-
@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
255270
else
256-
@tar -C build/binaries -zcf build/distributions/fleet-server-$(VERSION)-$(OS)-$(ARCH).tar.gz fleet-server-$(VERSION)-$(OS)-$(ARCH)
257-
@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
258273
endif
259274

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

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

272291
.PHONY: docker-cover-e2e-binaries
273292
docker-cover-e2e-binaries: build-releaser
274-
## Build for local architecture and for linux/amd64 for docker images.
275-
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)
276295

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

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ write_files:
4646
path: /etc/profile.d/docker-compose-alias.sh
4747
runcmd:
4848
- [ mkdir, /run/go ]
49-
- [ wget, https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz, -O, /run/go/go.tar.gz ]
49+
- [ wget, ${DOWNLOAD_URL}, -O, /run/go/go.tar.gz ]
5050
- [ tar, -xzf, /run/go/go.tar.gz, -C, /usr/local/ ]
5151
- [ rm, -rf, /run/go ]
5252
- [ su, ubuntu, -c, "/usr/local/go/bin/go install github.com/magefile/mage@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)