What is the point of "trusted substituters"?

I have an “untrusted” account at aarch64.nixos.community, let’s assume it is sort of golden setup of a recent Nix.

The trust model does not allow me to use arbitrary substitutes (AKA “binary caches”), e.g.
nix-store --realise --substituters http://$myserver/ /nix/store/$mypath
would fail with
warning: ignoring substitute for '/nix/store/...' from 'http://...', as it's not signed by any of the keys in 'trusted-public-keys'

Meanwhile, nix-copy-closure is not restricted, so I am able to download /nix/store/$mypath to my laptop and nix-copy-closure it to aarch64.nixos.community. Obviously, restricting nix-copy-closure would make the builder machine unusable at all.

I wonder what is the trust model for binary caches, why are binary caches restricted and is there any sense to enforce such indirect copying discouraging server-to-server copy using binary caches?

P.S. curl $myserver/.....nar | nix-store --import also works around “trusted substituters”

I think the confusion here lies in that Nix allows you to substitute paths from anywhere but only if it’s signed by a trusted substituter.

You can’t nix-copy-closure untrusted paths either. When you tried to copy, you likely happened to copy a path that was signed by a trusted substituter and when you tried to substitute, you likely tried a path that didn’t originate from a trusted substituter and was therefore not signed by it.

The security issue mitigated by this is the ability for a malicious local user to realise a fixed path with arbitrary content.

With this ability, you could fairly easily perform a privilege escalation: If you can predict the path of a future drv the user will have as part of their closure (fairly simple before a staging-next merge), you could craft a malicious version of the path and install it before the user substitutes it from the trusted substituter or builds it themselves.

I can nix-copy-closure (and nix-store --import) the paths which I cannot substitute from my binary caches.

I see, in your discourse it is a sort of yet unfixed bug.
But if you fix it, the idea of remote building would gone (as every builder will have to bootstrap all “untrusted” code from scratch, not trusting what user can upload).

Also, the third workaround is adding ?trusted=1 to binary cache urls (laughable security)

If those paths don’t have trusted signatures on them, that’d be a huge security issue. Please double check.

I don’t understand this sentence. I have not referenced any bug.

That’s… how remote builds work. The remote fetches everything it can from its trusted substituters and realises the rest on its own.
In order to “upload” local paths, you must be a trusted user on the remote builder.

As mentioned in the reply to the linked comment, that only works if you’re a trusted user and by default only root is trusted.

I don’t see how that’s “laughable security”.

  1. got error
    2, 3. irrelevant
  2. nix-import one of those paths scp’ed to the machine
  3. the same as 1 but with ?trusted=1 - no error, and the nix-imported path is not going to be fetched, it is already here

yes, it is “a huge security issue”, when the security is half-made.

it is not yet secure but already inconvenient.

It works like an user upload all dependencies to a builder and run build there.
The dependencies can be made on builder1 and then uploaded (nix-copy-closure’d) to the builder2 by the user (even during a single nix-build run), and thus they are “untrusted” in the eyes of builder2.

So when you will fix this huge security issue, all these will be broken, the remote build will be available only to users with “trusted” privileges

Shortly, what I want to reach, not to flame nor to fix the security.

My goal is to enhance “nix copy” with the case of copying from http:// (binary cache) to ssh-ng://, where ssh-ng://-machine may be not under my control, it can run mainline Nix (I am not able to patch), from quite old versions to possible future ones, and where I may have no “root” or other “trusted” privileges, but privileges enough to build which implies enough to upload.

So far, it works fine via the local machine, but every attempt to implement direct server-to-server copy has some haxorish smell: straightforward implementation is forbidden by security, while there are at least 3 hacky workarounds which unlikely be gone but may be different next year as security guys fix their issue and build guys introduce a new ?trusted=1 to survive it.

This thread really bugs me and has kept a prominent desktop/browser placement, pending me revisiting it.

@volth does it feel resolved? I must admit, I feel like I’ve confused myself about the expectations regarding trusted=1, trusted-users, trusted-substituters, etc.

I guess to restate something from the nix manual, does what you’re finding circumvent the conditions described in the manual docs for require-sigs?

I’m having a hard time telling if this is “the concept of trust is shaky/broken” or “nix-copy-closure” is stricter than it needs to be".


The security added by sigs is also questionable (because the signer is the trusted distribution server not the trusted builder, so it does not up anything to https), but easier to ignore (I removed this logic from my Nix fork, so I don’t follow its development), while substituters is a nice addition for remote builds on third-party servers, including aarch64.nixos.community, so I’ll have to deal with it somehow.

As for a solution, I’m fine with adding ?trusted=1 to bypass “security” as it solves my problem of enabling user substitutions on remote builds, and my only concern is a possible change in this protocol that would force me to copy all remote build inputs over the local machine (which doesn’t add security anyway, as a derivative with a guessed path could still be created by an untrusted user).

The only sensible solution seems to be to abandon all this safe fence simulacra to avoid inconvenience and confusion.