Idea: nix-fod (or whatever other better name)

nix-fod: just like nix-build but replaces the outputHash with a fakeHash and produces the hash you needed at the end of the build with no error. Why? Because I recently was poking at nix-update and ended up adding the following to a private package of mine

  genHash =
    let
      outputHash = lib.fakeHash;
    in
    package.yarnOfflineCache.overrideAttrs (prev: prev // { inherit outputHash; });

  update =
    let
      root = builtins.toString ./.;
    in
    pkgs.writeShellScriptBin "update" ''
      export PATH=${
        lib.makeBinPath [
          pkgs.coreutils
          pkgs.jq
          pkgs.gawk
          pkgs.nix
        ]
      }:$PATH
      set -euo pipefail

      log=$(mktemp)
      trap 'rm -f "$log"' EXIT
      exec 2> >(tee "$log" >&2)

      set +e
      nix-build -A genHash --no-out-link ${root}/default.nix
      set -e

      hash$(tail -n1 "$log" | awk '/got: / { print $2; }')
      jq -n --arg hash $hash '$hash' > ${root}/hash.nix
    '';

to me this seems absolutely bonkers, but I also am no c++ dev and have never peeked at the internals of nix so maybe there are reasons why what I am suggesting can’t work, but it seems to me:

  1. If I can do it outside of nix-build, then surely nix-build itself could be taught
  2. FODs are never going away, and you could think of this as a nix-prefetch-url but for derivations, and no-one disputes the utility of prefetching urls
  3. Its crazy to me that the alternative seems to be trying to inject json into the logs rather than just saying what you mean which is “please sir, calculate me the hash I want”.

Thoughts?

There is one complication that would make this… messy. FODs don’t have ssl certs available. Even when they download an https url, they actually just accept certificates blindly. This is fine normally, since the specified hash protects you against malicious action anyway. This is also fine these days when you explicitly change the hash argument in the code, because there’s a condition in the code which brings in the local nixpkgs’ version of the certificate files when the supplied hash is fakeHash. (and hopes that you’re working with a recent enough nixpkgs that this works…) However nix itself only knows about the FOD itself, not the function in nixpkgs that generates it. If it just inserted a fakeHash at the bottom level, you’d be downloading with no authentication.

1 Like

Speaking of which, I’m not 100% sure your override isn’t causing the same issue in the code you wrote above. If you’re overriding the underlying derivation and not the fetcher invocation, then it would. Not sure if fetchers have been changed to be more properly overrideable than they used to be…

Ha! I was just about to ask, am I potentially opening a wormhole? I guess I need to look harder at what nix-update is doing, because I was basically transposing that.

The problem isn’t how you wrote the script, it’s how you abstracted out the changed FOD. I believe nix-update actually modifies the source code in place, so it’s changing the arguments to the fetcher itself, not invoking any overrides.

EDIT: Yeah, a quick look at the code suggests fetchers haven’t gotten any more overrideable since I last checked. You’re very likely running without certs here.

1 Like

Well now I am very glad I posted this. I guess in a world with perfect memory safety I could just run the script “twice” i.e. the second time with the calculated hash, and if it worked then no one fooled me. Not really a great game to play tho. I guess I’ll make it work with in place updates.

All the more reason to maybe try to push something into nix itself with certs.

You might want to consider an external pinning system like nvfetcher. Generally a lot less painful than rolling a custom update script. Though it does depend what kind of sources you’re following.

I am an avid npins user, the problem is that the yarnOfflineCache which is what I am trying to override isn’t something that you can just “fetch” as far as I am aware without running something like yarn2nix to generate nix from the lock file? It doesn’t seem from a skim that nvfetcher supports this.

Also, maybe I am being dumb here, but looking at the nix_prefetch function combined with how it’s called when updating yarn (called here from here) makes me suspicious that if I have a problem then so does nix-update?

EDIT: actually isn’t this all a bit upside down: yarn is fetching all the things, not nix, and then dumping that fetch into a folder and we are checking the hash of that? So nix’s treatment of ssl doesn’t really come into it?

nix isn’t doing the fetching in the case of any FOD. In pkgs.fetchurl, curl does the fetching, without regard to nix. The issue is how that derivation is written, not anything to do with “nix’s treatment of ssl”. It’s really more “nixpkgs’ treatment of ssl”.

1 Like
nix-repl> (pkgs.fetchurl { url = "https://github.com/Mic92/nix-update/raw/f612fb07436603c68f7d90659d4d6ef82b03a5e6/nix_update/update.py"; }).SSL_CERT_FILE                                                                                                                                      
"/nix/store/6fv8ayzjvgyl3rdhxp924zdhwvhz2iq6-nss-cacert-3.111/etc/ssl/certs/ca-bundle.crt"

nix-repl> ((pkgs.fetchurl { url = "https://github.com/Mic92/nix-update/raw/f612fb07436603c68f7d90659d4d6ef82b03a5e6/nix_update/update.py"; hash = "sha256-1CopUlKvJLuMD1ufGs4Qi7C2eMM28ngtjFC70FI//Bc="; }).overrideAttrs (old: { outputHash = ""; outputHashAlgo = "sha256"; })).SSL_CERT_FILE
"/no-cert-file.crt"

You are not dumb. We’re oblivious, apparently. What nix-update is doing is indeed not safe. I didn’t quite believe it, since I know people worry about this exact issue and I knew it was thought of as handled correctly in nix-update… it seems it isn’t.

2 Likes

That’s fair I was being sloppy with terminology. I meant nixpkgs fetchers / nix builtin fetchers vs yarn, which surely is built with tls support, irrespective of how where it is called from.

Tls support, yes, of course. Curl is the same. But it’s not at all normal to have the certificate database available inside a derivation sandbox.

1 Like

I’m just now realizing nix-update isn’t the same as nixpkgs-update, which is what’s used to automatically update nixpkgs packages, and does handle this safely. So the issue isn’t quite as serious as I thought it was.

Regardless, I opened an issue: nix_prefetch is *not* ssl protected. ¡ Issue #397 ¡ Mic92/nix-update ¡ GitHub

  1. Thanks for opening the issue on nix-update.
  2. I think it does get a fair amount of play (rg 'nix-update ') seems to show a decent number of packages using it directly for their update scripts: for instance.
  3. I think I am safe because fetchYarnDeps does include the cacerts in the sandbox., therefore yarn is verifying tls connections, right?

Yes, that seems like conclusive proof to me. You should be fine even with overrideAttrs with fetchYarnDeps.

1 Like

Why is that, by the way? I don’t get why we’d trust the fod url’s cert when the hash is actually a valid value.

Also, FYI:

Because reproducibility means you’re using old certs when you run old FODs, so they’d start failing after a while with complaints about certs not verifying, which doesn’t actually matter, since you already have an even stronger guarantee that you’re getting the bits you want in the form of the specified hash.