Attempting to initiate a remote build results in the following: error: you are not privileged to build derivations (nix (Nix) 2.2.2)
Adding the remote user to the remote machine's nix.conf trusted-users allows the build to continue as normal. This seems to be intended behaviour, but I had a few problems with it:
nix.trustedUsers, but it wasn't clear whether it was referring to the local (like when needed for use with --builders) or the remote machine.remote builds require a trusted user might be more obvious?nix copy --to ssh://builder /nix/store/whatever.drv && ssh builder nix build /nix/store/whatever.drv && nix copy --no-check-sigs --from ssh://builder /nix/store/whatever works fine, so I was confused why remote builds with --builder and nix.conf builders weren't able to do the same.I understood there would be drawbacks to setting it up this way (not trusting the remote user will prevent them from supplying binary substitutes for example, and with multiple builders could even result in duplicated work if the build scheduler isn't aware), but did not expect or find mention that it would be disallowed and prevented entirely. Is there a reason why this check exists? Attempting to send an unsigned build input to the remote machine would've already failed out the build before hitting this wopBuildDerivation error, so it's not immediately clear to me why this particular operation is gated to trusted users to begin with? Sorry if I'm misunderstanding what a "build derivation" operation actually is/does.
I have stumbled upon this in a different situation. My remote non-NixOS machine has already had my user in trusted-users, but it uses nsswith for authentication, and nix-daemon was rejecting remote builds until I added my user into /etc/passwd.
This requirement means that sharing a build cluster across multiple users makes them all susceptible to a cache poisoning attack (e.g. if one user's machine is compromised then anything can be copied into the build server's Nix store). I asked @grahamc about a more secure method and he mentioned an possible approach where
.drv is created.drv's closure is copied over to the build machine.drv is built on the remote machinewhich avoids the possibility of cache poisoning because everything in a .drv is content-addressed. This is discussed in the original comment as well. Can this be supported as an alternative remote build mechanism?
That's how remote builds used to work, but it's inefficient to copy the drv closure. That's why buildDerivation was added.
Is there a reason we can't have both?
Yes please let's have both.
One gotcha, A derivation can directly refer to a store path direclty (rather than derivation that built it) for sake of store paths with no associated derivation. But say the store path is associated with a derivation? We should outright either ban that, or ensure the derivation is fixed output so the remote builder's drv->build trust mapping isn't grown.
Is there a reason why adding your user as a trusted-user is required?
It seems strange that trusted-user is required, even though in most(?) cases you would be able to directly ssh to the remote machine and run nix-build, which works without being a trusted-user.
I think @arcnmx asked this question in their initial post, but it doesn't appear to be answered in this thread. (Unless it actually has been answered and I just don't understand the answer...)
It seems strange that
trusted-useris required, even though in most(?) cases you would be able to directlysshto the remote machine and runnix-build, which works without being atrusted-user.
The way distributed builds work with Nix, you are supposed not only to be able to trigger a build on a remote machine, but also to export some locally-available build results to the remote machine and make it accept it. If you have two remote builders, and you change stdenv, and the first one has built glibc, you want just to copy this glibc to the second builder instead of building it again. And importing a non-fixed-output store path (bypassing the build) requires some way of establishing trust.
@cdepillabout See above. Remote builds don't use the same mechanism as local builds. They use a special API called buildDerivation() that allows the client to only send the contents of the top-level .drv file that it wants to build, rather than all the dependencies of the .drv. However, this prevents Nix from checking that the .drv is legit (i.e. that its output paths are a hash of the derivation graph) so it has to trust the client. Hence buildDerivation() is restricted to trusted users.
@edolstra @7c6f434c That makes sense. Thanks for the explanation.
However, this prevents Nix from checking that the .drv is legit (i.e. that its output paths are a hash of the derivation graph)
@edolstra Can you explain this comment and integrity issues that come from allowing untrusted users from uploading arbitrary .drv files into the /nix/store? I thought that all correctness properties of the .drv file can be checked using just the .drv file itself (e.g. the hash prefix of the .drv files matches the content of the file).
Also, surely the remote machine needs more that just the top-level .drv file. In order to build all the dependencies it will need the the .drv of all the the dependencies down to the the ones that the remote machine has already built, but this is exactly the set of .drv files that nix-copy-closure needs to send.
(I do get that if the local machine is sending the remote machine build outputs, then, of course the local machine needs to be trusted.)
Okay, after reviewing page 108 of edolstra's thesis I now see that hashDrv for derivation files isn't just a hash of the contents of the derivation file, but it replaces the inputDrvs contents with a hash of the contents of those inputDrvs files. ... I'll need to think about why things are done this way, but it does mean that it is impossible to stick a .drv file into the nix store without having the contents of the input derivation files on hand.
Edit: hashDrv is used to create the output field of a valid .drv file, not the store hash of the .drv file (which is based on the contents of the .drv file). This is what edolstra means by not being able to validate the output fields of drv files without the contents of the input drv files. Though I still don't quite understand why this needs to be done.
I've started to fix this. I'll link this in the PRs which relate to it.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/obsidian-systems-is-excited-to-bring-ipfs-support-to-nix/7375/35
Most helpful comment
I've started to fix this. I'll link this in the PRs which relate to it.