Finding the TLS Certificates in Guix Environments
One of my favorite features of Guix is its ability to quickly create Linux namespaces via guix shell --container.
However, when using this feature to setup development environments for working on an existing piece of software, I often face issues with TLS certificates.
For example, when attempting to build Go software in such an environment, I recently ran into the following error message:
$ guix shell --container --network go git nss-certs coreutils
[env] $ go build
Get "https://proxy.golang.org/github.com/go-git/go-billy/v5/@v/v5.8.0.zip": tls: failed to verify certificate: x509: certificate signed by unknown authority
Get "https://proxy.golang.org/github.com/go-git/go-git/v5/@v/v5.17.0.zip": tls: failed to verify certificate: x509: certificate signed by unknown authority
Get "https://proxy.golang.org/github.com/go-git/go-git/v5/@v/v5.17.0.zip": tls: failed to verify certificate: x509: certificate signed by unknown authority
…
Even though, nss-certs, which provides the trusted system certificates, is in the closure, the Go toolchain is unable to verify the Go proxy certificate.
Normally, Go (and other software) looks for the system certificates in hardcoded paths (usually /etc subdirectories).
However, in Guix, the certificates (provided by nss-certs) live in the GNU store, not in /etc.
When using Guix as an operating system, the /etc directory is populated with the system certificate through the guix-data-service-profile-packages Shepherd service.
This does not work in guix shell, because it does not use the Shepherd.
When using guix shell without --container, the /etc (and thus certificates) of your host system (e.g., a foreign distro or your Guix operating system) is accessible and thus used.
As a quick workaround, it is thus possible to include the host’s /etc in the Linux namespace, for example, using:
$ guix shell --container --network \
--expose="$(guix build nss-certs)/etc/ssl=/etc/ssl" \
go git nss-certs coreutils
This binds the /etc subdirectory of the nss-certs package, which contains the system certificates, to /etc within the Linux container.
However, bind mounting does not work when using guix shell without the --container flag.
Therefore, this is a workaround as Guix intends utilization of SSL_CERT_DIR and SSL_CERT_FILE for this purpose.
The remainder of this text documents my present understanding of how this is meant to work.
SSL_CERT_DIR and SSL_CERT_FILE
SSL_CERT_DIR and SSL_CERT_FILE are environment variables that are widely used to enable users to specify a custom directory or file containing CA certificates.
As far as I can tell, they originate from OpenSSL, where their usage is described in the openssl-env(7) man page as follows:
SSL_CERT_DIR, SSL_CERT_FILE: Specify the default directory or file containing CA certificates. SSL_CERT_DIR can contain multiple directories separated by colons (or semicolons on Windows).
Apart from OpenSSL, a lot of software honors these variables (including curl and the Go TLS library).
Search Paths
Guix presupposes that these variables are respected by software attempting to verify TLS certificates.
In order to identify software, which makes use of these variables, it uses its search path facilities.
The guix search-paths Guile module contains a search-path-specification for these variables.
The nss-certs package provides the required certificate files that are added to these search paths.
Therefore, if both nss-certs and a software using SSL_CERT_DIR or SSL_CERT_FILE are in the closure, then these environment variables are set accordingly.
$ guix shell --search-paths openssl nss-certs --pure
…
export SSL_CERT_DIR="/gnu/store/c9d62jwq42h9y8isaagyn4jah00a95q9-profile/etc/ssl/certs"
export SSL_CERT_FILE="/gnu/store/c9d62jwq42h9y8isaagyn4jah00a95q9-profile/etc/ssl/certs/ca-certificates.crt"
Making Software Honor SSL_CERT_{DIR,FILE}
If a piece of software does not find the system certificates in a guix shell --container, this can have one of the following reasons:
- Within Guix, the software does not declare that it uses the
SSL_CERT_{DIR,FILE}vianative-search-paths. - The software does not respect the aforementioned environment variables; in this case, it must be patched accordingly first.
An example for the former is the Go toolchain. An example for the latter is the Zig compiler.