-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Inspired by NixOS/rfcs#34 discussion:
Currently we don't have any way to authenticate flakes. We only have transport security, e.g. by downloading via https or ssh, so if the server is compromised, it can serve Trojaned flakes. Thus, we would like to have the ability to require that flakes are signed in some way.
Authentication is only needed when fetching from a mutable flake reference (i.e. one that doesn't include a Git revision or content hash). It will be assumed that the immutable flake references in a lock file were authenticated at the time the lock file was generated. [Actually it just occurs to me that for security, github flakes must include a content hash in the lock file, since we have no way to verify that the Git revision corresponds to the contents.]
Ideally, the flake registry would contain public keys that are allowed to sign the trees fetched from a mutable flake reference. For example,
{
"version": 1,
"flakes": {
"dwarffs": {
"uri": "github:edolstra/dwarffs/flake",
"publicKeys": ["E52y5ZkdNLl3LUYl3TiwR7XTZAPU7jgkEYDvsmbavio="]
},
"nixpkgs": {
"uri": "github:NixOS/nixpkgs-channels",
"publicKeys": ["FioIjAooQuwH200U4u5H2asLDCNZXaHpmxQnj2fcggQ="]
}
}
}Alternatively, public keys could be part of a flake URI, e.g. a flake dependency could specify a public key like this:
{
requires = [
github:edolstra/dwarffs?publicKey=E52y5ZkdNLl3LUYl3TiwR7XTZAPU7jgkEYDvsmbavio=
];
}However, the main issue is how to associate a signature with the downloaded flake.
-
For
githubflakes, GPG signatures can be fetched out-of-band via the GitHub API, but I would prefer to avoid GPG because 1) it's 1990s crypto; 2) I don't want to make Nix dependent on GPG when we already havelibsodium; 3) GPG keys/signatures are much more unwieldy thanlibsodium'sed25519; 4) it would be nice to use the exact same mechanism as binary caches. We could use Git notes to attach ed25519 signatures, but I don't think they can be queried via the GitHub API. -
For
gitflakes, since we're cloning the whole repo, we do have the ability to use Git notes. -
For tarball flakes, the signature could be included in the tarball, or delivered as a separate file (e.g.
http://example.org/flake.tar.xz.sig). However, the latter is racy because there is a time window betweenflake.tar.xzandflake.tar.xz.sigget updated.
Another possibility is to fetch the signature from some content-addressable source specified in the flake registry, e.g. "nixpkgs": { "sigs": "https://nixos.org/flake-sigs", ... }. The client would compute the hash of the data to sign (see below) and then download the signature from https://nixos.org/flake-sigs/<hash>.
Signature contents: The signature should be over the string <content-hash>:<git-revision>:<git-ref>:<refcount-or-commit-date>. The git-ref (branch or tag name) is included to prevent an attack where the attacker replaces one channel (e.g. nixos-19.03) with another (e.g. nixos-unstable). refcount-or-commit-date could be used to prevent rollback attacks, but this requires the client to keep some state about the last refcount or date it saw from a mutable flake.
Registry authentication: The registry should also be signed. The public signing key will be hard-coded in Nix.
Certificates: We should allow the private signing key ("cold key") for a flake to be kept offline. This can be done by signing the flake using a hot key, and including in the signature file a certificate where the hot key signs the cold key. Revoked keys could be listed in the registry. (This would be pretty nice to have for binary cache signing as well.)