Makefile: Skip boringcrypto when Go ships native FIPS module

What does this MR do?

Guards the GOEXPERIMENT=boringcrypto activation in the FIPS_MODE=1 block of the Makefile so it is skipped when the Go toolchain provides the native FIPS 140-3 module (the Go Cryptographic Module, selected via GOFIPS140).

Root cause

GitLab CNG is moving its FIPS images from the golang-fips (OpenSSL-backed) toolchain to upstream Go with GOFIPS140=v1.0.0 baked into the toolchain's go.env (CNG!3010, CMVP certificate 5247).

The existing probe is too weak for that toolchain: GOEXPERIMENT=boringcrypto go version succeeds unconditionally because go version never initializes the build/FIPS configuration. The Makefile therefore exports GOEXPERIMENT=boringcrypto, and the first real go build fails with:

go: cannot use GOFIPS140 with GOEXPERIMENT=boringcrypto

See this failed CNG FIPS job: https://gitlab.com/gitlab-org/build/CNG/-/jobs/15232574018

The fix: empty-or-off guard (dual-toolchain safety)

The boringcrypto probe + export now only run when go env GOFIPS140 reports empty or off:

GOFIPS140_MODULE := $(shell go env GOFIPS140 2>/dev/null)
ifeq ($(filter-out off,$(GOFIPS140_MODULE)),)
    # ... existing boringcrypto probe + export ...
endif

Treating off as "legacy toolchain" is deliberate: golang-fips 1.24+ toolchains (still used by omnibus-gitlab) report off, never empty, and still require GOEXPERIMENT=boringcrypto to enable their OpenSSL backend. A strictly-empty check would silently drop the OpenSSL backend from Omnibus FIPS builds. Go < 1.24 toolchains report empty and are likewise unaffected.

Deliberately unchanged

Everything else in the FIPS_MODE block remains as-is because it is valid under both toolchains:

  • SERVER_BUILD_TAGS := ${SERVER_BUILD_TAGS},fips
  • GIT_FIPS_MESON_BUILD_OPTIONS := -Dsha256_backend=openssl (bundled Git's SHA256 backend)
  • export GITALY_TESTING_ENABLE_FIPS := YesPlease

Empirical testing

Tested on macOS arm64 with the pinned Go from .tool-versions (go1.25.7, installed via mise). With any Go >= 1.24, GOFIPS140=v1.0.0 go env GOFIPS140 reports v1.0.0 (identical to a toolchain with the value baked into go.env, i.e. the CNG native chain), and plain go env GOFIPS140 reports off (exactly what a golang-fips 1.24+ toolchain reports), so both toolchain flavours can be simulated faithfully:

$ go version
go version go1.25.7 darwin/arm64
$ go env GOFIPS140
off
$ GOFIPS140=v1.0.0 go env GOFIPS140
v1.0.0

1. Pre-patch reproduction of the CNG failure (native sim):

$ GOFIPS140=v1.0.0 FIPS_MODE=1 make _build/bin/gitaly-debug   # on master
go: cannot use GOFIPS140 with GOEXPERIMENT=boringcrypto
make: *** [_build/bin/gitaly-debug] Error 1

2. Post-patch native sim — build succeeds, boringcrypto not exported, native module embedded:

$ GOFIPS140=v1.0.0 FIPS_MODE=1 make _build/bin/gitaly-debug
$ go version -m _build/bin/gitaly-debug | grep -E 'GOFIPS140|GOEXPERIMENT'
	build	GOFIPS140=v1.0.0-c2097c7c
$ GOFIPS140=v1.0.0 FIPS_MODE=1 make -p -q lint | grep -E 'GOEXPERIMENT|GOFIPS140_MODULE'
GOFIPS140_MODULE := v1.0.0

3. Post-patch legacy sim (GOFIPS140 unset, i.e. go env GOFIPS140 = off) — identical to pre-patch:

$ FIPS_MODE=1 make -p -q lint | grep -E 'GOEXPERIMENT|GOFIPS140_MODULE'
GOEXPERIMENT = boringcrypto
GOFIPS140_MODULE := off

# pre-patch (git stash) for comparison:
$ FIPS_MODE=1 make -p -q lint | grep -E 'GOEXPERIMENT'
GOEXPERIMENT = boringcrypto

# real build, post-patch:
$ FIPS_MODE=1 make _build/bin/gitaly-debug
$ go version -m _build/bin/gitaly-debug | grep -E 'GOEXPERIMENT|GOFIPS140'
	build	GOEXPERIMENT=boringcrypto

4. Post-patch no-FIPS — unaffected:

$ make _build/bin/gitaly-debug
$ go version -m _build/bin/gitaly-debug | grep -cE 'GOFIPS140|GOEXPERIMENT'
0
$ make -p -q lint | grep -E 'GOEXPERIMENT|GOFIPS140_MODULE'
(no output)

🤖 Generated with Claude Code

Related to #7305 (closed)

Edited by Jason Plum

Merge request reports

Loading
Loading