How to access CA certificates when building derivation

Viewed 697

I want to automatically add CA certificates to Java keystore when running nix-shell. Before the following script worked:

with import <nixpkgs> {
  config.allowUnfree = true;
  config.oraclejdk.accept_license = true;
};

let dependencies = rec {
  _oraclejdk8 = stdenv.lib.overrideDerivation pkgs.oraclejdk8 (attrs : {
    installPhase = ''
      ${attrs.installPhase}

      pems_dir=pems
      mkdir "$pems_dir"

      echo "split bundled ca-certificates into separate files 'cert.N.pem'\
                and add them to default keystore"

      keytool=$out/bin/keytool
      keystore=$jrePath/lib/security/cacerts

      pushd "$pems_dir"
      awk 'BEGIN {c=0;doPrint=0;} /END CERT/ {print > "cert." c ".pem";doPrint=0;} /BEGIN CERT/{c++;doPrint=1;} { if(doPrint == 1) {print > "cert." c ".pem"} }' < /etc/ssl/certs/ca-bundle.crt

      # import certificates
      for f in `ls cert.*.pem`; do
        alias=`basename $f`
        $keytool -import -trustcacerts -noprompt -keystore "$keystore" -alias "$alias" -file "$f" -storepass changeit;
      done
      popd

    '';
  });

};
in with dependencies;
stdenv.mkDerivation rec {
  name = "env";
  buildInputs = [ _oraclejdk8 ];
}

But now it fails with the following error:

/nix/store/9fx2jfmks2zhvv2kmqgl6rg0fbkc3da0-stdenv-linux/setup: line 1391: /etc/ssl/certs/ca-bundle.crt: No such file or directory

It looks like now it is not possible to access any existing files. But actually this file is just a link to store:

$ readlink -f /etc/ssl/certs/ca-bundle.crt
/nix/store/w3vw4q9z7s0wig6ng4nv62af1917ynrm-ca-certificates.crt

How can I access this file in my script?

1 Answers

Nix is a hermetic build system, meaning that all inputs must be declared so they can all be tracked. You could pull in Nixpkgs' set of certificate authority certificates, being pkgs.cacert:

  # CAREFUL, read on!
  awk 'BEGIN {c=0;doPrint=0;} /END CERT/ {print > "cert." c ".pem";doPrint=0;} /BEGIN CERT/{c++;doPrint=1;} { if(doPrint == 1) {print > "cert." c ".pem"} }' < ${cacert}/etc/ssl/certs/ca-bundle.crt

However, be careful when pinning cryptographic configuration, because when it changes you need it to be updated everywhere. Suppose you want to run a 2-year old version of your software, do you want to run it with 2-year old certificates or the certificates from the environment?

Another problem is where are these certificates supposed to be written to? Keytool writes to the home directory by default, which is the non-existent one in the build sandbox.

It seems like the right solution in your case is not adding the certificates inside the sandbox, but returning a script that will do it in the context where it makes sense to do so. When and where is up to you, but please make sure you get those certificates from a good source. If you can assume your deployments are always done with a sufficiently up to date Nixpkgs, you can go with that. Otherwise, you're probably better off with the ones already on the system in /etc.

Related