Altering package (oraclejdk11) installation fails

Hi there! For work, I need oraclejdk11 with some additional certificates installed. I’m new to nixos, believe I found a solution that should work, but cannot get it to actually do so.

In the normal world (non-nixos), I’d just install the JDK and then use the keytool that’s found in its bin directory to add those certificates. In nix, that doesn’t work, because the installation directory is readonly.

Others have posted solutions to achieve the same result, but I think they are not as clean as my attempt and I don’t fully understand them. I’ve asked for clarification, but I don’t know if that’ll arrive in time.

If we break my problem down, it seems quite simple:

  1. Install package
  2. Run a few additional commands within the install dir

My attempt is to look at the derivation file for oraclejdk11 and then use an override to add the additional steps I require. This is what that looks like:

# custom-jdk.nix
{ pkgs ? import <nixpkgs> {}, ... }:

let
  # Define the source for the custom certificate
  cert1 = pkgs.fetchurl {
    url = "http://cert1.crt";  # Replace with the actual path or URL to the certificate file
    sha256 = "91a3b1962b...";  # Replace with the actual SHA-256 hash of the certificate file
  };
  cert2 = pkgs.fetchurl {
    url = "http://cert2.crt";
    sha256 = "fbaef2da...";
  };

  # Override the official Oracle JDK derivation
  customOracleJdk = pkgs.oraclejdk11.overrideAttrs (oldAttrs: {
    # I tried the following:
    # installPhase = oldAttrs.installPhase + '' # claims that `keytools` can't be found
    # postFixup = oldAttrs.postFixup + '' # /nix/store/gv2cl6qvvslz5h15vqd89f1rpvrdg5yc-stdenv-linux/setup: line 146: pop_var_context: head of shell_variables not a function context
    postInstall = '' # simply ignored
      # Debugging
      ls -l $out/bin > $out/content_of_bin.txt; // works and does find keytool executable
      $out/bin/keytool > $out/output_of_keytool.txt; // crashes: keytool not found
      
      # Import the custom certificates
      $out/bin/keytool -noprompt -trustcacerts -import -alias cert1 \
        -storepass changeit -keystore $out/lib/security/cacerts -file ${cert1};
      $out/bin/keytool -noprompt -trustcacerts -import -alias cert2 \
        -storepass changeit -keystore $out/lib/security/cacerts -file ${cert2};
    '';
  });
in
  customOracleJdk

I don’t understand why keytool won’t run even if the ls command shows the file is there when installPhase is used. I have no clue what the error coming up using postFixup means. And finally, I don’t understand why postInstall is simply ignored.

I also wonder if this general approach to make a new derivation based on another is the best way to solve the problem. A friend suggested overlays, but I feel more comfortable like this (and we didn’t get overlays to work, either). Do you have any other suggestions?

I found a solution. I’m not sure it’s a good one.

I turned around the order of the installPhase, doing my custom logic first now, and in the archive directory as opposed to the $out$ dir. That seems to make more sense.

And I don’t refer to the keytool coming from the archive anymore. Instead, I use the keytool from the official oraclejdk11. I’m not particularly happy about this, because this only works because I’m only modifying a package that already exists - I shouldn’t have to rely on that. Also, I’m not sure why oraclejdk11 is even available on my system, without it being installed. Is that only because it’s still hanging around somewhere from a previous install? Or is this supposed to always work? Why, if so?

Here’s how it actually works. Please comment

{ pkgs ? import <nixpkgs> {}, ... }:

let
  # Define the source for the custom certificate
  cert1 = pkgs.fetchurl {
    url = "http://cert1.crt";  # Replace with the actual path or URL to the certificate file
    sha256 = "91a3b1962b...";  # Replace with the actual SHA-256 hash of the certificate file
  };
  cert2 = pkgs.fetchurl {
    url = "http://cert2.crt";
    sha256 = "fbaef2da...";
  };

  # Override the official Oracle JDK derivation
  customOracleJdk = pkgs.oraclejdk11.overrideAttrs (oldAttrs: {
    installPhase = ''
      # Debugging executable
      ${pkgs.oraclejdk11}/bin/keytool > $out/output_of_keytool.txt; // this works!
      
      # Import the custom certificates
      ${pkgs.oraclejdk11}/bin/keytool -noprompt -trustcacerts -import -alias cert1 \
        -storepass changeit -keystore lib/security/cacerts -file ${cert1};
      ${pkgs.oraclejdk11}/bin/keytool -noprompt -trustcacerts -import -alias cert2 \
        -storepass changeit -keystore lib/security/cacerts -file ${cert2};
    '' + oldAttrs.installPhase;
  });
in
  customOracleJdk

for context, this is saved to a file like custom-jdk-11.nix and I import it into my configuration.nix like

environment.systemPackages = with pkgs; [
  ...
  (import ./custom-jdk-11.nix { inherit pkgs; })
];