Adopt Go's native FIPS 140-3 module (GOFIPS140) across GitLab Go components
## Background Go 1.24 introduced the [Go Cryptographic Module](https://go.dev/doc/security/fips140) — an in-tree, transparently-used implementation of FIPS 140-3 approved algorithms, selected at build time via `GOFIPS140` and controlled at runtime via the `fips140` `GODEBUG`. On 2026-04-27, module **v1.0.0** (shipped in `lib/fips140/` of every Go ≥1.24 distribution) received full CMVP validation ([certificate #5247](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/5247)), satisfying FedRAMP requirements. Module v1.26.0 (Go 1.26) is Module-In-Process under the same filing, as of today. GitLab's FIPS builds have historically used [`golang-fips`](https://github.com/golang-fips/go) — a patched toolchain that routes crypto through the system OpenSSL via CGO, activated by the repurposed `GOEXPERIMENT=boringcrypto`. That project has [announced its sunset](https://github.com/golang-fips/go#migration-to-upstream-fips-certified-cryptography): Go 1.26 is the final major release of the fork, with minor bumps only for the lifetimes of Go 1.25/1.26. **Migration is therefore both a compliance win (fully certified module, no CGO crypto boundary) and a necessity, with a deadline: the legacy path ends at Go 1.26 EOL (~February 2027).** Completing this work will greatly simplify our FIPS patterns, as related to Go. - Reduced complexity in build tooling. - Reduced reliance on upstream distributions (like RedHat) for FIPS certifications. ## Spike and decision record The investigation ran under [gitlab-org/distribution/team-tasks#1706](https://gitlab.com/gitlab-org/distribution/team-tasks/-/work_items/1706): - [Decision record](https://gitlab.com/gitlab-org/distribution/team-tasks/-/work_items/1706#note_3534240878): use the upstream signed Go distribution; set `GOFIPS140` explicitly at build (pinned `v1.0.0`, not the floating `certified`/`inprocess` aliases); replace the `GOLANG_FIPS=1` runtime switch. - [Failure analysis and upstream scoping](https://gitlab.com/gitlab-org/distribution/team-tasks/-/work_items/1706#note_3538243712): all remaining breakage shares one root cause — a 2022-era Makefile pattern ([golang/go#51940](https://github.com/golang/go/issues/51940)) that probes `GOEXPERIMENT=boringcrypto go version` (which always succeeds) and sets `GOEXPERIMENT=boringcrypto`, which `cmd/go` correctly refuses to combine with `GOFIPS140`. - Reversibility was assessed before committing: rollback is a single-commit revert on CNG (registry images are content-addressed, so a revert is cache-hits); the return path decays when we adopt Go 1.27 and is gone at Go 1.26 EOL. One behavioral ratchet identified (Ed25519 TLS certificates: accepted by the native module's FIPS TLS policy, rejected by golang-fips's). - SCR: the FedRAMP sponsor approved use of this module without a formal significant change request ([note](https://gitlab.com/gitlab-org/distribution/team-tasks/-/work_items/1706#note_2775536450)). Key engineering facts carried through the implementation: - `GOFIPS140=v1.0.0` baked into `$GOROOT/go.env` of the CNG toolchain image is inherited by every component build that copies `/usr/local/go` — zero per-component configuration. - Binaries built this way default to `fips140=on` at runtime (recorded as `build GOFIPS140=v1.0.0-c2097c7c` and `build DefaultGODEBUG=fips140=on` in `go version -m`). - **Transition-window rule**: never set `GODEBUG=fips140=<anything>` in deployment-level configuration — any value is fatal at process init to golang-fips binaries, and `fips140=only` is [documented by Go](https://go.dev/doc/security/fips140) as a testing mode not intended for production. Escalation to `only` is a deliberate, QA-backed follow-up. - Legacy detection: golang-fips 1.24+ toolchains report `off` (never empty) from `go env GOFIPS140` — all Makefile guards use the empty-or-`off` form so Omnibus (still on golang-fips) keeps working. - labkit's `fips` package unconditionally imported `crypto/boring`, which **does not compile** on a native-module toolchain (stdlib files are `//go:build boringcrypto`) — making the labkit fix a hard prerequisite for every component that builds with `-tags fips`. ## Status (2026-08-13) **Update 2026-08-20:** the CNG FIPS pipeline is fully green — zero failed jobs. All component labkit bumps are merged. gitaly built natively with `-tags fips`. CNG!3010 is ready to merge. **Update 2026-08-27:** CNG !3010 was rebased onto a master that includes gitlab-agent!4188; its FIPS pipeline is green with zero failed jobs, the chart e2e test passed against its images (all four k3d children including arm64, plus GKE), and an independent CMVP-criteria validation report is posted on the MR. lucus.li approved; a maintainer approval is the sole remaining gate. gitlab-shell !1524 merged (labkit v2.35.0 + `HostKeySigner`), closing the SSH-policy floor. **Update 2026-08-21:** every component labkit bump is merged (gitlab-shell !1523 on 2026-08-17, gitaly !9076 on 2026-08-20, workhorse !249851 on 2026-08-14, container-registry !2922 and zoekt-indexer !1004 earlier). labkit !608 shipped the corrected SSH algorithm policy in **v1.64.12** (v1) and **v2.35.0** (v2). CNG !3010 is green on its final revision and in review. The labkit work is complete. Minimum labkit v1 versions by component: **v1.64.9** is sufficient for components that only report FIPS posture (`fips.Check()`/`Enabled()`): gitaly, workhorse, gitlab-pages, container-registry, zoekt-indexer — all are at or above it. **v1.64.12** is the floor only where labkit's SSH algorithm policy is consumed: gitlab-shell — satisfied 2026-08-27 by the v2 port (!1524, labkit v2.35.0 + `HostKeySigner`). All four Makefile guard merge requests are merged. These merge requests are in flight: - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/244451 — gitlab (docs): FIPS documentation rewrite. It stays draft until CNG!3010 merges; it must describe the !608 three-tier SSH policy. - https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/3010 — build/CNG: the toolchain migration. Pipeline fully green; awaiting reviewer approvals. ## Work in flight | Project | MR | Status | |---|---|---| | build/CNG | 🟢 [!3010](https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/3010) — native FIPS toolchain image (GPG-verified upstream Go + go.env `GOFIPS140`), build assertions, dual-chain-safe entrypoint | Rebased 2026-08-24 (includes gitlab-agent!4188); FIPS pipeline green (zero failed jobs, no retries), chart e2e green (k3d ×4 incl. arm64 + GKE), independent CMVP-criteria validation posted on the MR; approved by lucus.li — awaiting a maintainer approval | | labkit | ✅ [!582](https://gitlab.com/gitlab-org/labkit/-/merge_requests/582) — split `crypto/boring` behind `boringcrypto` build tags; `Enabled()` = boring OR `crypto/fips140` | **Merged** (2026-08-03), released in **v1.64.9** | | labkit | ✅ [!594](https://gitlab.com/gitlab-org/labkit/-/merge_requests/594) — drop empty SSH algorithm names leaked by x/crypto under GOFIPS builds (v1) | **Merged** (2026-08-10), released in **v1.64.10** | | labkit | ✅ [!598](https://gitlab.com/gitlab-org/labkit/-/merge_requests/598) — apply SSH algorithm policy per FIPS backend (v1) | **Merged** (2026-08-12), released in **v1.64.11** | | labkit | ✅ [!595](https://gitlab.com/gitlab-org/labkit/-/merge_requests/595) — port fips to **v2** (`v2/fips` + `v2/fips/sshalgo`; no v2 fips package existed) | **Merged** (2026-08-12), released in **v2.33.0** | | labkit | ✅ [!608](https://gitlab.com/gitlab-org/labkit/-/merge_requests/608) — correct the SSH algorithm policy for both FIPS backends (pubkey-auth filter fix; `HostKeySigner`; three-tier exclusions) | **Merged** (2026-08-21), released in **v1.64.12** and **v2.35.0** — SSH-policy consumers (gitlab-shell) should target these | | gitaly | ✅ [!8992](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/8992) — Makefile boringcrypto guard | **Merged** (2026-07-10) | | gitlab-shell | ✅ [!1516](https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/1516) — Makefile boringcrypto guard (CGO/gssapi left to project) | **Merged** (2026-07-14) | | gitlab-pages | ✅ [!1296](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/1296) — Makefile.build.mk boringcrypto guard | **Merged** (2026-07-14) | | gitlab (workhorse) | ✅ [!244449](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/244449) — workhorse/Makefile boringcrypto guard | **Merged** (2026-08-13) | | container-registry | ✅ [!2922](https://gitlab.com/gitlab-org/container-registry/-/merge_requests/2922) — labkit bump to v1.64.10 | **Merged** (2026-08-12) | | gitlab-shell | ✅ [!1523](https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/1523) — labkit bump to v1.64.11 + native-FIPS test guard | **Merged** (2026-08-17) | | gitlab-shell | ✅ [!1524](https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/1524) — port fips usage to labkit `v2/fips` + `v2/fips/sshalgo` (stanhu) | Merged 2026-08-27 — labkit v2.35.0 with `HostKeySigner` adopted; closes the gitlab-shell SSH-policy floor | | gitlab-shell | ✅ [!1526](https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/1526) — `ssh-audit` CI acceptance check for FIPS and non-FIPS algorithm sets (stanhu) | Merged 2026-08-28 | | gitaly | ✅ [!9076](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/9076) — labkit bump to v1.64.11 | **Merged** (2026-08-20); the stale `ubi-9.5-golang-1.25` `test:fips` CI image remains a gitaly-master maintenance item | | gitlab (workhorse) | ✅ [!249851](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249851) — labkit bump | **Merged** (2026-08-14) | | gitlab-zoekt-indexer | ✅ [!1004](https://gitlab.com/gitlab-org/gitlab-zoekt-indexer/-/merge_requests/1004) — labkit v1.64.2 → v1.64.11 (+ `cockroachdb/errors` v1.14.0, `sourcegraph/log` bump for sentry-go v0.46 compat; supersedes renovate !1001) | **Merged** (2026-08-14) | | gitlab (docs) | 📝 [!244451](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/244451) — rewrite `doc/development/fips_gitlab.md` Go/CNG sections for GOFIPS140 | Open (draft until supporting work merges) | | omnibus-gitlab | ✅ [!9595](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/9595) — two-way-door toggle `USE_GO_FIPS_MODULE` for native GOFIPS140 (defaults off; golang-fips path unchanged until flipped) | **Merged** (2026-07-15) | ## Remaining work (per-project child issues) - [x] https://gitlab.com/gitlab-org/labkit/-/issues/129 — labkit: **v1** ~~land !582 (v1.64.9); SSH empty-name fix !594 (v1.64.10)~~ — done; ~~land !598~~ (merged 2026-08-13, v1.64.11 — v1 bump target is now v1.64.11); **v2** ~~land !595; tag a v2 release~~ (merged 2026-08-12, v2.33.0). Components bump v1 to ≥v1.64.11 now - [x] https://gitlab.com/gitlab-org/gitaly/-/issues/7305 — gitaly: complete and closed 2026-08-20 (guard !8992; labkit v1.64.11 via !9076; native `-tags fips` build of gitaly/praefect verified in the green CNG FIPS pipeline) - [ ] https://gitlab.com/gitlab-org/gitlab-shell/-/issues/865 — gitlab-shell: ~~land !1516~~ (merged 2026-07-14); bump labkit (!1523 in flight; functional: SSH algorithm selection keys on `fips.Enabled()`); decide CGO/gssapi posture under native FIPS - [ ] https://gitlab.com/gitlab-org/gitlab-pages/-/issues/1200 — gitlab-pages: ~~land !1296~~ (merged 2026-07-14); bump labkit - [ ] https://gitlab.com/gitlab-org/gitlab/-/issues/605580 — gitlab: ~~land !244449 (workhorse)~~ (merged 2026-08-13); bump labkit (!249851 in flight); review whether `BUILD_TAGS += fips` and forced CGO remain wanted under native FIPS; land !244451 (docs) once the above merge - [ ] https://gitlab.com/gitlab-org/build/CNG/-/issues/2361 — build/CNG: re-run FIPS pipeline to full green; merge !3010; protect pre-cutover `-fips`/`-build-fips` registry tags; retire renovate-gitlab-bot rules for `GO_FIPS_VERSION`/`GO_FIPS_TAG`; follow-up decision on escalating runtime posture to `GODEBUG=fips140=only` (QA-backed; object-storage/MD5 paths exercised), including images that bypass the gitlab-base entrypoint (kas/agentk/zoekt) - [ ] https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/work_items/1051 — gitlab-agent (Relay): migrate the self-published Relay/agentk FIPS images off golang-fips (Go 1.27 native, in progress by timofurrer). CNG-shipped kas/agentk need nothing: no labkit, no boringcrypto probe, toolchain inherited from CNG!3010. - [ ] https://gitlab.com/gitlab-org/omnibus-gitlab/-/work_items/10002 — omnibus-gitlab: the equivalent migration off golang-fips (must complete before Go 1.26 EOL) — ~~land !9595~~ (merged 2026-07-15, opt-in `USE_GO_FIPS_MODULE` toggle); flip the default once the chain is proven. Builds off the dependencies here. ## Timeline constraints - **Go 1.27 adoption (~August 2026)**: the golang-fips return path narrows to a Go 1.26-pinned emergency fallback. Commit-or-revert decision must precede this. - **Go 1.26 EOL (~February 2027)**: golang-fips minor-bump support ends; legacy path dead. Omnibus must have migrated by then. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
epic