"SSL peer certificate or SSH remote key was not OK" error on fresh Nix install on macOS

I installed Nix using sh <(curl https://nixos.org/nix/install) --no-daemon which seemed to work fine.

Then installing a package with nix-env -i coreutils gives the following output:

warning: there are multiple derivations named 'coreutils-8.31'; using the first one
installing 'coreutils-8.31'
warning: unable to download 'https://cache.nixos.org/gw15cnpfww4a4in8wbnrlqzrhgjkjrnp.narinfo': SSL peer certificate or SSH remote key was not OK (60); retrying in 299 ms
warning: unable to download 'https://cache.nixos.org/gw15cnpfww4a4in8wbnrlqzrhgjkjrnp.narinfo': SSL peer certificate or SSH remote key was not OK (60); retrying in 656 ms
...

The system curl seems to have no problem with the SSL certificate:

$ curl -L 'https://cache.nixos.org/gw15cnpfww4a4in8wbnrlqzrhgjkjrnp.narinfo'

StorePath: /nix/store/gw15cnpfww4a4in8wbnrlqzrhgjkjrnp-coreutils-8.31-info
URL: nar/0hw493dzj0cynq70dfiriwzdyw64cz79kyfy15432v7vsd87614q.nar.xz
Compression: xz
FileHash: sha256:0hw493dzj0cynq70dfiriwzdyw64cz79kyfy15432v7vsd87614q
FileSize: 181488
NarHash: sha256:08irirlcs90cvfd1v1h8x2z990wadjbsfc3n89a0a700asznk672
NarSize: 921880
References:
Deriver: bv31f6scbnfzzafb6pfjl947i6pn9179-coreutils-8.31.drv
Sig: cache.nixos.org-1:Nc8UKJXeD3MaWunP4YUbxL7U0Knicn9PdHgmS3lBXSCFOFnyp798FOk4C0mtTIZxKZztzcnK2tVHnt+gqnWkBg==

What’s going on?

My initial guess is that NIX_SSL_CERT_FILE isn’t set properly in your shell. Did you make sure to create a new shell that sources the Nix profile setup, instead of just manipulating your PATH to include nix?

I knew it had to be something blindingly obvious. Thank you. :smiley:

Am I correct in thinking that nixpkgs.cacert must be installed (on macOS) so that NIX_SSL_CERT_FILE can actually be set (to ~/.nix-profile/etc/ssl/certs/ca-bundle.crt)?

I ask because I think I installed ca-bundle.crt manually, but everything broke when I uninstalled it, which makes me think that it is effectively a dependency of every nix package that may need to verify SSL certificate authorities.

Furthermore, am I correct in thinking that on macOS I should now ensure that NIX_SSL_CERT_FILE gets initialized for launch daemons, GUI applications, or anything else that may run commands installed via nixpkgs (that could need to verify SSL certificates)?

I believe you are correct in that nixpkgs.cacert must be installed.

As for launch daemons, GUI applications, and anything else, how are these things invoking Nix-installed commands without access to the relevant Nix environment? Are you talking about hard-coding paths into ~/.nix-profile/bin for these things? If so, the simplest thing might be to instead hard-code the path to a script that sources the Nix setup first.

Mostly found via PATH which I make sure include ~/.nix-profile/bin when I may need stuff from that location.

One of the items was a ruby script (with output shown on the desktop), this failed until I learned about the need for NIX_SSL_CERT_FILE, as it was retrieving data via https.

I didn’t have this issue with homebrew’s ruby and I’m a little puzzled about nix here, as while I understand that hardcoding paths when building ruby is not an option, the ca-bundle.crt is linked to from ~/.nix-profile/etc/ssl, so can’t this be used as a fallback path (single-user system)?

Yes, I did briefly look at wrapProgram, but I think I have only four apps that may run nix stuff, but a lot more than four things that will be run from these, so now I have set NIX_SSL_CERT_FILE for all of these.

AFAIK Homebrew’s ruby uses the system-provided certs. In fact, brew info ruby doesn’t even list curl as a dependency, just openssl.

FWIW I’m not actually sure why we can’t use system-supplied certs with Nix curl on macOS. Looking at nix.sh right now, it looks up paths for Ubuntu, Debian, Gentoo, Arch, openSUSE Tumbleweed, Fedora, and CentOS. Maybe it requires patching curl to access the keychain, but that’s something we could do. I originally was going to say it would be an impurity, except given that it uses the system paths on Linux systems, this suggests Nix is ok with using the system trust store.

Looking at nix.sh there’s 6 different paths that NIX_SSL_CERT_FILE could end up pointing to. Maybe there’s some scheme we could come up with to unify it such that there’s a single path we have to look at (e.g. symlinking the system cert path into the profile on such systems), I’m not sure.

Are these apps installed via Nix? If so, you could wrap the executable itself with a script that sets NIX_SSL_CERT_FILE, or modify its Info.plist to add the LSEnvironment key to its Info.plist that specifies the env var. If these are apps installed externally, then I guess it depends on the app.

Alternatively, if you’re only worried about getting this working on your local system, you could get your env var into launchd. There’s no automatic way to do this, but launchctl setenv $key $value will set an env var that will be used by all subsequently-launched processes in the user context (including GUI apps), so you could do something like add a startup script to your user that runs that command.

Surprisingly ca-bundle.crt from nixpkgs is the last location tried, so on most systems, installing this bundle will have no effect on trusted certificate authorities.

And I’d certainly favor creating a symbolic link in some known location, it’s effectively the same thing; either we search for a CA bundle each time a shell is launched and store the location in an environment variable, or we check it when the profile is built, and store it in a symbolic link.

But storing in symbolic link avoids the pain of having to ensure the variable is set (probably mostly an issue on macOS) and it may even allow to add the ca-bundle.crt dependency if the system does not provide a usable CA bundle.

They are not.

I can say from first-hand experience that it must be installed. When I was first starting out with Nix I uninstalled that package—“what is this doing here? I never asked for it to be installed”—and suddenly my Nix installation couldn’t do anything! It took me a while to figure out what I’d done wrong.

I would think that it’s just that nobody bothered given the fairly small number of macOS users.

I looked at curl and we can build it (configureFlags) with --with-secure-transport and it should use the macOS keychain for certificates.

Right now, curl is set to --without-ca-bundle which means that it will rely on the crypto library (openssl) to verify certificates.

I did try adding --with-secure-transport but curl fails to build for me with this error:

configure: error: --with-ssl was given but OpenSSL could not be detected

The option is set like this "--with-ssl=${openssl.dev}" and I verified that openssl.dev is there.

1 Like

I wonder if --with-secure-transport requires using the system-supplied openssl. That would suck of course.

Reading the nixpkgs.curl package right now, it does say

      # Disable default CA bundle, use NIX_SSL_CERT_FILE or fallback
      # to nss-cacert from the default profile.

And the openssl package has two patches, one for darwin that sets the default location to /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt and a generic one that sets it to /etc/ssl/certs/ca-certificates.crt. This suggests that if the default profile has nixpkgs.cacert installed then curl will work without NIX_SSL_CERT_FILE on macOS. It would of course be better to figure out how to get the system keychain integration to work instead.

This actually makes me wonder if using --with-secure-transport means we should omit --with-ssl. There’s also --with-darwinssl which I assume means use the OpenSSL shipped with macOS. Secure Transport is a “legacy” API so it’s probably not the best thing to use long-term, though it does support TLSv1.3. macOS’s bundled OpenSSL is super old so we definitely shouldn’t use that.

Interestingly, /usr/bin/curl --version tells me it’s compiled with LibreSSL, so I’m wondering what Apple’s own setup here is. Do they have a LibreSSL patch to use the keychain?

Probably the ideal solution is to continue to have curl rely on Nix OpenSSL for this, but figure out how to get Nix OpenSSL to use Apple’s Keychain for certificates.

Would make sense, though I tried this and then got another error:

checking run-time libs availability... failed
configure: error: one or more libs available at link-time are not available run-time. Libs used at link-time: -lssh2 -lgssapi_krb5 -lresolv -lz

No idea how the ./configure script is able to check run-time libs, seems sort of backwards with nix, as nix will ensure that linked libraries are there at run-time…

Agree, openssl is probably what most packages rely on for certificate verification, or, like with curl, can be made to rely on.

I had a look at what homebrew does, and they write non-expired root certificates from the system’s keychain into a .pem bundle that is then used by openssl, so any change to the keychain will require a re-install.

It’s a solution but not the one we would prefer. But it may indicate that patching openssl is probably not straightforward.

Just checked my system and I see an /etc/ssl/cert.pem with a header comment that declares the date as “2017/02/24”. I have no idea how the certificates in this bundle compare with what’s in the keychain, but I’m curious if it would be a viable fallback path to use when NIX_SSL_CERT_FILE doesn’t exist.

My system keychain has 172 certificates, ~/.nix-profile/etc/ssl/certs/ca-bundle.crt has 137, and /etc/ssl/cert.pem has 72 (I filtered out expired certificates from all 3 sets).

So probably not ideal, but still a desired improvement because it would be more robust (i.e. support user uninstalling ca-bundle.crt) and bring macOS inline with the other operating systems/distributions, which all get a setting for NIX_SSL_CERT_FILE.

Just only use it as a fallback for when there is no ~/.nix-profile/etc/ssl/certs/ca-bundle.crt, as we don’t know how frequently Apple updates the bundle included with the OS.

@lilyball Will you do a PR for this?

I’m not really sure what the appropriate patch here is. Nix already patches openssl on macOS to fall back to /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt, which is reasonable behavior on a multi-user system because the root user is supposed to have that bundle installed. I suppose we could patch crypto/x509/by_file.c to do a second load attempt if the first one using the default path fails.

Another thing to consider is patching openssl to have a second fallback on macOS would actually break with other platforms. We’ve established that NIX_SSL_CERT_PATH is populated with the system trust store on various Linux systems by default, but if it’s unset, the openssl package will do the exact same fallback it does on darwin, which is to say it will fall back to SSL_CERT_PATH, and then fall back from there to /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt.

I’m actually not sure how this is handled when using Nix to install GUI apps on non-NixOS Linux. Do the GUI apps somehow get NIX_SSL_CERT_PATH setup? Looking at the install script right now it appears to still default to single-user install on Linux.


Another option to consider is to change the Nix single-user install script to set up the default profile to include the cacert package. The downside here is it would likely never get updated again unless the user intentionally mucks with the default profile.