Build Go components against the FIPS module unconditionally
What does this MR do?
FIPS builds could produce Go binaries two ways: against the golang-fips fork, or against upstream Go's native FIPS 140-3 module. USE_GO_FIPS_MODULE and FIPS_BUILDER_IMAGE_SUFFIX selected between them, and nothing tied the two variables to each other or to the build cache.
This MR collapses that to one configuration. The golang-fips path is gone, GOFIPS140 is exported once from omnibus.rb instead of per recipe, and a config-load check stops the build when the toolchain cannot supply the module.
It supersedes !9771 (closed), !9773 (closed), !9774 (closed) and !9781 (closed), which are the same work spread across a toggle that no longer exists. Those can close when this merges.
Builder image dependency
gitlab-omnibus-builder!512 (merged) removes the go_fips snippet, so the FIPS builder images install the same upstream Go release as every other image. That merged as 5588a699, and tag 5.69.0 points at exactly that commit. BUILDER_IMAGE_REVISION is now 5.69.0.
This pin matters. 5.68.0 predates !512 (closed) and still ships golang-fips. That toolchain accepts GOFIPS140 and builds without an error, and the binaries then stop at init on a host in FIPS mode.
One gate, one behavior
Review raised that FIPS_MODE=1 was being gated on which Go FIPS mechanism we use, which joins unrelated concerns. The gates are now independent:
Build::Check.use_system_ssl?controls what C code links against.Build::Check.use_go_fips_module?controls only which crypto module Go compiles against. It is no longer implied byuse_system_ssl?.
Every FIPS build job sets USE_SYSTEM_SSL, USE_SYSTEM_LIBGCRYPT and USE_GO_FIPS_MODULE together in .fips_branch_template and .fips_tag_template, so no pipeline changes behavior. Because the gates no longer cover for each other, a build that sets USE_SYSTEM_SSL without USE_GO_FIPS_MODULE now skips the GOFIPS140 export, skips the toolchain check, and gives its Go components no FIPS_MODE=1.
Why gitaly.rb gates on either check
Gitaly's Makefile makes FIPS_MODE a single switch over two concerns. Under ifdef FIPS_MODE it appends the fips Go build tag to SERVER_BUILD_TAGS, and it sets GIT_FIPS_MESON_BUILD_OPTIONS to -Dsha256_backend=openssl for the bundled Git C build. Upstream offers no way to request one without the other.
gitaly.rb runs make install, which builds the Go binaries and bundled Git, so neither gate alone is correct for it. use_system_ssl? alone drops the Go build tag from a module build; use_go_fips_module? alone drops the OpenSSL SHA256 backend from the C build. Both fail silently. It therefore sets FIPS_MODE when either is true.
git.rb runs make git install-bundled-git, which builds no Go binaries, so FIPS_MODE there does nothing but select the SHA256 backend. It stays on use_system_ssl? alone, unchanged.
Splitting FIPS_MODE upstream in Gitaly would let each gate drive exactly one behavior. That is a Gitaly change, tracked separately.
Why the toggle had to go rather than flip
The rollback story was "flip a variable", and the build cache breaks it. The omnibus git cache digest covers a definition file, its version and its dependency shasums — not the environment, not the Go toolchain — and neither .branch-cache nor .tag-cache carried the toggle. Both configurations of a FIPS job shared one cache key.
That is not theoretical. A package built from this work carried a native-module registry restored from another pipeline's cache next to golang-fips binaries built in place, with no error anywhere. Thanks to @achugitlab for installing the package and finding it.
CACHE_KEY_SUFFIX therefore moves to -v4, once. Without it the first build after this change restores golang-fips binaries for every component whose recipe and version did not change.
Why the _fips images stay
FIPS_BUILDER_IMAGE_SUFFIX is removed, but every FIPS job still names its _fips image — now literally. Those images carry more than the Go toolchain: their curl_<platform>_fips snippets install the libidn2, libpsl, brotli, libnghttp2 and libssh2 development packages that FIPS builds need to link the distribution's curl (doc/curl_fips.md in the builder repo).
Measured on almalinux_9 5.67.0, running this repo's own CurlHelper inside both images:
_fips image |
base image | |
|---|---|---|
CurlHelper.pkg_config_files |
7 files | 1 file (libcurl.pc) |
threshold met (>= 5) |
yes | no |
OpenSSLHelper.pkg_config_files |
3 | 3 — identical |
gitaly.rb, git.rb and gitlab-rails.rb copy only the resolved .pc files into an overrides directory and then replace PKG_CONFIG_PATH, so a base-image FIPS build would run with 4 of the 10 files it expects. PKG_CONFIG_THRESHOLD = 5 is not enforced anywhere, so nothing would have stopped it.
The *_fips package check jobs likewise name their images literally: those jobs install and exercise a built package, so the image is a verification environment, not a build toolchain.
The registry and labkit
Every Go component now sets its own FIPS switch, thus the registry sets one too. The registry reads no FIPS_MODE. Its Makefile passes BUILDTAGS to go build -tags, so registry.rb appends fips there.
That tag needs a labkit that builds without crypto/boring. Before labkit v1.64.10, the //go:build fips files import crypto/boring, which has no buildable files under upstream Go. The registry pinned labkit v1.64.8, so the first attempt broke all four FIPS builds and was reverted. v4.42.0-gitlab moves the registry to labkit v1.65.3, which puts the BoringCrypto probe behind fips && boringcrypto. This MR takes that release and restores the tag.
Measured in almalinux_9_fips:5.69.0, which carries upstream Go 1.26.7:
| Case | Result |
|---|---|
v4.42.0-gitlab, fips tag on |
Builds. go version -m gives -tags=include_gcs,include_oss,fips,fips140v1.0, GOFIPS140=v1.0.0-c2097c7c, labkit v1.65.3 |
v4.41.0-gitlab, fips tag on, same image and environment |
Fails: imports crypto/boring: build constraints exclude all Go files |
The built binary, run with database.enabled: false |
Starts and logs FIPS mode is enabled. Using the native Go Cryptographic Module. |
labkit v1.65.3 probe, tag off |
fips.Enabled() is false, although crypto/fips140.Enabled() is true |
Row 2 shows that the labkit bump is the cause of the correction, not the newer builder image. Row 3 is the built registry reporting its own FIPS status correctly. Row 4 shows the defect this closes: without the tag the module is active, but the component reports FIPS as off, thus the S3 driver does not select the AWS FIPS endpoints.
Commits
Each commit is scoped to one change and is independently lint-clean and test-green. Commits 9 to 11 address review on this MR and sit on top of the original series rather than being folded into it.
Drop the golang-fips fallback from Go builds—GOEXPERIMENT/boringcrypto_supported?; gitlab-pages decides its own backend fromgo env GOFIPS140Make use_go_fips_module? the single gate for Go FIPS buildsRetire the FIPS builder image suffix variableRetire FIPS build caches written under the old toggle—CACHE_KEY_SUFFIX-v4Export GOFIPS140 once for all Go builds— central export, plus a spec that fails any definition settingGOFIPS140without a justifiedoffbypassStop a FIPS build when the toolchain lacks the module—go list stdresolves the module wherego versionaccepts anythingGate FIPS_MODE on use_go_fips_module?Set FIPS_MODE=1 for the gitlab-kas build— thefipstag its ownbuild/verify_fips_binary.shasserts onDecouple the Go FIPS module gate from system OpenSSL— narrows commit 2, souse_system_ssl?no longer implies the moduleSet gitaly's FIPS_MODE from either FIPS gate— narrows commit 7 for the one component that builds both Go and CName the checks that turn on the Go FIPS module— doc wording, per@achugitlabBuild FIPS packages on images with upstream Go—BUILDER_IMAGE_REVISIONto5.69.0Build the registry with the fips tag— the first attemptRevert the registry fips tag: labkit is too old— it broke all four FIPS builds, and the cause was the labkit pinBound the Go FIPS toolchain probe and report its errorUpdate dependency container-registry to v4.42.0-gitlab— labkitv1.64.8tov1.65.3Build the registry with the fips tag— restored, now that labkit supports it
Related issues
Closes #10091 (closed). Closes #10090 (closed). Related to #10002 (closed) and gitlab-org#22761 (closed).
Checklist
See Definition of done.
- Merge gitlab-omnibus-builder!512 (merged)
- Cut a tag from !512 (closed) and set
BUILDER_IMAGE_REVISIONto it (5.69.0) - Verify a FIPS package's Go binaries report
GOFIPS140viago version -m, and thatregistrycarries thefipstag - Documented in
doc/development/ci-variables.mdanddoc/development/new-software-definition.md - Added the
~"workflow::ready for review"label - Pipeline is green