Local Nix cache is ignored because NAR info file lacks a signature

nixnixos

[partially solved here: https://plus.google.com/110416349762686874861/posts/PVGHL1Tpeb9; but not entirely]

I want to use one of my NixOS machines as a cache for the packages, to not pull everything from Hydra for each one on every upgrade. So I try to do this:
https://nixos.org/nix/manual/#sec-sharing-packages. But

  • When I try to use nix-serve -p <port>, nix-env --option extra-binary-caches http://<host>:<port>/ (even when run as root!) just ignores this cache, saying that "NAR info file" … "lacks a signature", and attempts to switch to cache.nixos.org. So nix-serve doesn't work as expected.
    Does it mean that the documentation is not actual anymore, or that nix-serve is broken?

  • While nix-copy-closure --to <user>@<host> does work (if <user> is added as trusted user into nix.trustedUsers). But doing this on every upgrade would be very inconvenient. BTW. What also puzzles me here is how --from is supposed to be used?

I have general understanding of how cryptographic signatures work, and why they are used to sign packages (and repositories). But

  • In these circumstances I simply don't need it: the risk of a MITM is not something to worry about. I just want to copy few GiB's of binaries from one machine to another, regularly and effortlessly.
    I could, of course, simply disable the verification completely via nix.requireSignedBinaryCaches = false, but isn't there a less radical way?
    If, say, I invoke nix-env as root, or as a "trusted user" and have this "extra" binary cache registered as "trusted binary cache"; doesn't it imply that I know what I'm doing, and don't need the system nagging about missing signatures? (without the need for disabling the checking altogether)
    I haven't found how to disable the verification only for a certain "trusted" source/user, and pretty much suspect it doesn't exist (i.e. bug).

  • I could probably also, to comply with this signing mechanism, try to set up a "full-fledged" binary cache, by making use of nix-store --generate-binary-cache-key, then nix-push --dest <somewhere> --key-file <secret-1> --none <cherrypicked-paths>, and registering the public key on the client(s). But it's quite not what I want: storing the archives on the "server" and setting the paths for nix-push explicitly. I just want that either nix-serve signed those shared packages before delivery, so that the receiving side would be happy, or nix-env to not complain about the lack of signing, if I'm absolutely sure that this particular source is trustworthy!

In conclusion: I'm pretty sure nix-serve or the signature checking mechanism (or both) are simply broken/unmaintained. So one can consider it all less as a question, more as a bug report candidate. But if I'm wrong — yet better.

Best Answer

Here is how you configure the server and client for using a signed binary cache served via the nix-serve command. This does not require using nix-push to generate the cache and you can serve your /nix/store directly using this method.

This is documented in the man-page for nix-push so if you want more details you can check that out, too

Server configuration

This example assumes that your server's hostname is cache.example.com.

First, you need to generate a signing-key pair using nix-store --generate-binary-cache-key, like this:

$ nix-store --generate-binary-cache-key cache.example.com-1 nix-serve.sec nix-serve.pub

... just replacing cache.example.com with any appropriate hostname for your server. It doesn't have to match the real host name, but it helps if they match so that you can easily distinguish which server a public key belongs to.

If your cache server is running NixOS then you can serve your cache by adding these two lines to your NixOS configuration file:

nix-serve = {
  enable = true;
  secretKeyFile = "/path/to/nix-serve.sec";
};

... and make sure that the nix-serve user has read-access to the nix-serve.sec key.

If you are not using NixOS and you want to serve your cache directly using the nix-serve executable, then you need to use the NIX_SECRET_KEY_FILE environment variable to specify the path to the secret key, like this:

NIX_SECRET_KEY_FILE=/path/to/nix-serve.sec nix-serve ...

Client configuration

If your client machine is a NixOS machine, then you can add these lines to your NixOS configuration file:

nix.binaryCaches = [
  "https://cache.nixos.org/"

  # This assumes that you use the default `nix-serve` port of 5000
  "http://cache.example.com:5000"
];

nix.binaryCachePublicKeys = [
  "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="

  # Replace the following string with the contents of the
  # `nix-serve.pub` file you generated in the "Server configuration"
  # section above
  "cache.example.com-1:...="
];

If you are not on a NixOS machine then you can manually edit your nix.conf file to have the following settings:

binary-caches = https://cache.nixos.org/ http://cache.example.com
binary-cache-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.example.com-1:...=

If you prefer to enable a binary cache for just one build you can instead pass these binary cache configuration flags directly to any Nix utility like nix-build or nixos-rebuild, like this:

nixos-rebuild build --option binary-caches "https://cache.nixos.org/ http://cache.example.com" --option binary-cache-public-keys "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.example.com-1:...="
Related Question