Skip boringcrypto when Go provides the native FIPS module
What does this MR do?
Guards the FIPS_MODE=1 boringcrypto activation in Makefile.build.mk so it
is skipped when the Go toolchain provides the native Go Cryptographic Module
(GOFIPS140, Go 1.24+, CMVP certificate 5247).
Root cause
The existing probe is too weak:
BORINGCRYPTO_SUPPORT := $(shell GOEXPERIMENT=boringcrypto go version > /dev/null 2>&1; echo $$?)go version never initializes build or FIPS configuration, so this probe
succeeds on every toolchain, including ones whose go.env bakes in
GOFIPS140=v1.0.0 (as the CNG FIPS images now do, see
gitlab-org/build/CNG!3010 (merged)). The build
target then inlines $(GO_BUILD_ENV) into the go install invocation (note:
in this repo GO_BUILD_ENV is an inline recipe prefix, not an export as in
gitaly/gitlab-shell/workhorse) and fails:
go: cannot use GOFIPS140 with GOEXPERIMENT=boringcryptoFailed CNG FIPS job: https://gitlab.com/gitlab-org/build/CNG/-/jobs/15234386707
The fix
Only run the probe (and set GO_BUILD_ENV=GOEXPERIMENT=boringcrypto) when
go env GOFIPS140 reports empty or off:
- A toolchain that selects the native module reports its version (e.g.
v1.0.0) — boringcrypto must be skipped. offmust be treated as "legacy toolchain": golang-fips 1.24+ toolchains (still used by omnibus-gitlab) reportoff, never empty. A strictly-empty check would silently drop the OpenSSL backend from Omnibus FIPS builds. Empty covers golang-fips toolchains older than Go 1.24, which don't know the variable at all.
The GO_BUILD_TAGS := $(GO_BUILD_TAGS),fips line is unchanged, and the
post-build self-check (go tool nm $(BINDIR)/gitlab-pages | grep FIPS) is
retained as-is: the native module's symbols live under
crypto/internal/fips140* and satisfy the same grep (verified empirically
below).
Known follow-up (out of scope here)
With the Makefile conflict fixed, a native-module FIPS build of gitlab-pages
still cannot link yet: the fips build tag selects
gitlab.com/gitlab-org/labkit/fips/fips.go, which imports crypto/boring —
a stdlib package gated on //go:build boringcrypto — so compilation fails
with imports crypto/boring: build constraints exclude all Go files ....
labkit needs a GOFIPS140-aware implementation (e.g. based on
crypto/fips140.Enabled()). This MR removes the Makefile-level blocker; the
labkit change is tracked as part of
https://gitlab.com/gitlab-org/distribution/team-tasks/-/work_items/1706.
The empirical native-chain build below was validated with a locally patched
labkit (crypto/boring → crypto/fips140) via an uncommitted go.mod
replace, to prove the Makefile guard and the self-check end to end.
Empirical testing
All on macOS arm64 with the repo-pinned Go 1.26.4 (mise exec). The native
chain is simulated with GOFIPS140=v1.0.0 in the environment; unset, this
toolchain reports exactly what a golang-fips 1.24+ toolchain reports:
$ GOFIPS140=v1.0.0 go env GOFIPS140
v1.0.0
$ go env GOFIPS140
offPre-patch failure reproduced (matches the CNG job):
$ GOFIPS140=v1.0.0 FIPS_MODE=1 make gitlab-pages
GOBIN=.../bin GOEXPERIMENT=boringcrypto go install -v ... -tags "continuous_profiler_stackdriver,fips" ...
go: cannot use GOFIPS140 with GOEXPERIMENT=boringcrypto
make: *** [build] Error 1Post-patch, native sim — GO_BUILD_ENV is now empty (no GOEXPERIMENT
in the recipe), and with the labkit shim in place the build succeeds and the
line-27 self-check passes on native-module symbols:
$ GOFIPS140=v1.0.0 FIPS_MODE=1 make gitlab-pages
GOBIN=.../bin go install -v ... -tags "continuous_profiler_stackdriver,fips" -buildmode exe gitlab.com/gitlab-org/gitlab-pages
...
go tool nm .../bin/gitlab-pages | grep FIPS >/dev/null && echo "binary is correctly built in FIPS mode" || ...
binary is correctly built in FIPS mode
$ go version -m bin/gitlab-pages | grep GOFIPS140
build GOFIPS140=v1.0.0-c2097c7c
$ go tool nm bin/gitlab-pages | grep -c 'crypto/internal/fips140'
1164(With stock labkit the build proceeds past the old GOFIPS140/boringcrypto
conflict and stops only at the labkit crypto/boring import described above.)
Post-patch, legacy sim (GOFIPS140 unset → off) — behavior is
byte-identical to pre-patch: the probe runs, GOEXPERIMENT=boringcrypto is
applied, the build succeeds and the self-check passes:
$ FIPS_MODE=1 make gitlab-pages
GOBIN=.../bin GOEXPERIMENT=boringcrypto go install -v ... -tags "continuous_profiler_stackdriver,fips" ...
binary is correctly built in FIPS mode(On darwin the boringcrypto experiment is accepted but the backend is inert;
the nm self-check passed both pre- and post-patch here because Go ≥ 1.24
binaries carry crypto/internal/fips140* symbols regardless.)
No-FIPS regression check:
$ make gitlab-pages # builds clean, no FIPS flags anywhere
$ make unit-test
...
DONE 900 tests in 12.094sTODO
- Feature flag
- This feature does not require a feature flag
- I added the
Changelogtrailer to the commits that need to be included in the changelog (e.g.Changelog: added) - I added unit tests or they are not required (build-system-only change; empirical build matrix above)
- I added acceptance tests or they are not required
- I added documentation (or it's not required)
- I followed code review guidelines
- I followed Go Style guidelines
Related to #1200 (closed)