Image Overview: go-fips
Container image for building Go applications with FIPS
Download this Image
The image is available on cgr.dev
:
docker pull cgr.dev/chainguard-private/go-fips:latest
Go FIPS with OpenSSL
This image provides go toolchain that produces FIPS compliant binaries. It is go toolchain compiled with golang-fips/go patches applied. They are further enhanced to always default to FIPS mode, without ability to opt out. For non-FIPS toolchain see go
container image.
Binaries built with this edition of go on Linux:
- require OpenSSL at runtime
- require OpenSSL FIPS provider at runtime
- will abort execution if above conditions are not satisfied
Whilst Chainguard’s edition of OpenSSL FIPS is recommended, the resulting binaries are vendor-agnostic and can be used at runtime with OpenSSL FIPS providers on other OpenSSL FIPS hosts.
FIPS compliance is achieved by not using any native golang cryptographic functionality and redirecting all calls to OpenSSL at runtime. Specifically the following modules are patched to redirect to the OpenSSL FIPS provider:
crypto
crypto/tls
- subset of
golang.org/x/crypto
that use stockcrypto
primitives only
If no other cryptographic algorithms are implemented or used, certification status will depend on the runtime OpenSSL FIPS certification. For Chainguard that is #4282 and the submitted rebrand of that.
Notable exceptions are:
crypto/md5
available for non-cryptographically secure use cases only
If your application uses crypto/md5
or any other third-party golang cryptographic modules, do engage with a CST testing laboratory for audit and certification needs.
Usage guidance
- Use native architecture builds (no-cross compilation)
- Ensure default build settings are used
- Do not customize toolchain
-tags
,GOEXPERIMENT
,CGO_ENABLED
Interactive build with FIPS operation validation
This section contains two examples of how you can use the Go FIPS Chainguard Image to build an example Go application. For more information on working with this Image, check out our Getting Started with the Go Chainguard Image guide.
Start interractive shell in the go-fips
image:
docker run --rm -ti --entrypoint bash -w /root cgr.dev/chainguard-private/go-fips:latest
Install a golang demo application helloserver
:
# go install golang.org/x/example/helloserver@latest
go: downloading golang.org/x/example v0.0.0-20240205180059-32022caedd6a
go: downloading golang.org/x/example/helloserver v0.0.0-20240205180059-32022caedd6a
Observe toolchain flags used to build the binary:
# go version go/bin/helloserver
go/bin/helloserver: go1.22.2 X:boringcrypto
This indicates the binary was built with 1.22.2
version of go, with X:boringcrypto
experiment enabled as an indicator that FIPS is in use. Please note that whilst the toolchain experiment is called boringcrypto
the actual implementation uses OpenSSL as evident from symbols table (see below).
Observe further build settings used on the binary:
# go version -m go/bin/helloserver
go/bin/helloserver: go1.22.2 X:boringcrypto
path golang.org/x/example/helloserver
mod golang.org/x/example/helloserver v0.0.0-20240205180059-32022caedd6a h1:zS1QYVOUpIsBoK6hpWlanELg0mrDgjk+mWqblK1nkjM=
build -buildmode=exe
build -compiler=gc
build DefaultGODEBUG=httplaxcontentlength=1,httpmuxgo121=1,panicnil=1,tls10server=1,tlsrsakex=1,tlsunsafeekm=1
build CGO_ENABLED=1
build CGO_CFLAGS=
build CGO_CPPFLAGS=
build CGO_CXXFLAGS=
build CGO_LDFLAGS=
build GOARCH=amd64
build GOEXPERIMENT=boringcrypto
build GOOS=linux
build GOAMD64=v1
Observe the following:
build CGO_ENABLED=1
setting is in placebuild GOEXPERIMENT=boringcrypto
setting is in place
Verify that OpenSSL symbols are used by the binary:
# go tool nm go/bin/helloserver | grep -e OpenSSL_version
6511f0 T _cgo_f207f3f06c6f_Cfunc_go_openssl_OpenSSL_version
964960 D _g_OpenSSL_version
4df600 t vendor/github.com/golang-fips/openssl/v2._Cfunc_go_openssl_OpenSSL_version.abi0
8d09b0 d vendor/github.com/golang-fips/openssl/v2._cgo_f207f3f06c6f_Cfunc_go_openssl_OpenSSL_version
Note that golang-fips/openssl contains bindings for all available APIs, even if individual binary may not use all of them.
Verify binary execution with suitable OpenSSL FIPS provider (use Ctrl+C
to terminate):
# go/bin/helloserver
2024/04/15 10:22:21 serving http://localhost:8080
^C
Now tamper with the fips provider to observe failure to start the application in FIPS mode
# cp /etc/ssl/fipsmodule.cnf fipsmodule.cnf.back
# cat fipsmodule.cnf.back | tr 89 98 > /etc/ssl/fipsmodule.cnf
# go/bin/helloserver
panic: opensslcrypto: can't enable FIPS mode for OpenSSL 3.3.0 9 Apr 2024: OSSL_PROVIDER_try_load
openssl error(s):
error:1C8000D4:Provider routines::invalid state
providers/fips/self_test.c:262
error:1C8000D8:Provider routines::self test post failure
providers/fips/fipsprov.c:707
error:078C0105:common libcrypto routines::init fail
crypto/provider_core.c:966
goroutine 1 [running]:
crypto/internal/backend.init.0()
/usr/lib/go/src/crypto/internal/backend/openssl.go:55 +0x13f
As you can see above helloserver
panics when on startup OpenSSL FIPS fails self tests.
Now restore fipsmodule.cnf
to get back into operational state:
cp fipsmodule.cnf.back /etc/ssl/fipsmodule.cnf
Dockerfile example
The following example Dockerfile builds a helloserver program in Go and copies it on top of the cgr.dev/chainguard-private/glibc-openssl-fips:latest
base image:
FROM cgr.dev/chainguard-private/go-fips:latest as build
RUN go install golang.org/x/example/helloserver@latest
FROM cgr.dev/chainguard-private/glibc-openssl-fips:latest
COPY --from=build /root/go/bin/helloserver /helloserver
CMD ["/helloserver"]
Run the following command to build the demo image and tag it as go-helloserver-fips
:
docker build -t go-helloserver-fips .
Now you can run the image with:
docker run go-helloserver-fips
Last updated: 2024-04-18 00:43