Currently, when building a NixOS configuration from a remote repository, you are trusting the Git server to serve the correct files.
If the server were compromised, an attacker could easily overtake the machines that update from its repositories by serving additional commits.
The same goes for nixpkgs and GitHub, making the security of all NixOS machines depend on GitHub’s security as well as the security of GitHub accounts with commit access.
This challenge was discussed previously in:
- A thread on nixpkgs security
- A nixpkgs issue on commit process security
- RFC 34 on signing commits to nixpkgs (closed).
One solution to this is commit signature verification, where a downloaded commit is verified against known keys.
The solution works for small teams but has several restrictions, such as not providing a way to change keys, which generally makes it unsuitable for larger teams and nixpkgs.
Another solution could be guix-style verification.
Essentially, a ‘secure’ commit is specified via its SHA1 hash.
The commit must contain a file that lists the public keys of all allowed committers.
Following commits will be checked against their parent’s committers file, making serving unauthorized commits impossible, even if an attacker controls the Git server.
This proposal was discussed in RFC 100, but it was put on ice because it would have forced changes to the workflows of all committers.
Especially not being able to use GitHub’s merge button was seen as a showstopper.
My program tries to loosen the requirements for incremental (guix-style) verification to make it viable in more scenarios, with a focus on nixpkgs.
I consider it to be a proof of concept to help the discussion.
The project lives at https://codeberg.org/flandweber/git-verify.
- Unsigned merges: Committers can continue to use the merge button.
- Unprotected prefixes: Some folders can be excluded from the verification to allow incremental introduction.
- Granular authorization: Committers can be restricted to subdirectories.
Currently, the built-in Nix git fetcher (for obvious reasons) does not allow access to the git history, which means a verifying git fetcher can only be implemented as a fixed-output derivation.
Unfortunately, this is not compatible with common update workflows (
nix flake update).
This could be fixed by modifying Nix or using another locking mechanism.
The authentication scheme also requires rollback prevention to disallow attackers from running simple downgrade attacks.
Guix implements this as a separate mechanism, which seems reasonable to me.
Nixpkgs backports pose another challenge as they’d need to be signed separately.
Squash-merges and rebase-merges would need to be done offline by committers.
Thanks to all the members of this community who took the time to discuss this project and share their insights.
It helped shape my approach to the problem and foresee some obstacles early on.
I intend to write my thesis on securing Nix updates and would appreciate any further input.
You can reach me via email (email@example.com) and Matrix (@flandweber:envs.net).
If somebody is interested, I’d also be happy to arrange calls to further discuss the problem and possible solutions.