Move nix-cache's binary cache signing key into sops

nix-serve's secretKeyFile was a manual, undocumented-outside-a-comment
`nix-store --generate-binary-cache-key` step per host -- easy to miss on
a fresh nix-cache instance (as lxc-nix-cache testing just found: systemd
fails the unit with EXIT_CREDENTIALS when LoadCredential can't find the
source file, which nginx then reports as a 502 from clients). It also
can't be regenerated per-host safely: modules/nix-cache/client.nix
hardcodes every client's trust in one specific public key, so every
nix-cache instance has to share the exact same keypair.

Sourced from secrets/nix-cache.yaml's new cache-priv-key entry instead,
via the same sops-nix pattern every other secret in this repo already
uses. Verified the added value derives to the exact public key
modules/nix-cache/client.nix already trusts before committing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
This commit is contained in:
2026-07-20 09:49:30 +10:00
co-authored by Claude Sonnet 5
parent 0ba837817e
commit 53b9a64826
3 changed files with 42 additions and 20 deletions
+28 -9
View File
@@ -11,19 +11,38 @@ This repository configures `nix-cache` as a **binary cache server** and a **remo
- Client hosts import `modules/nix-cache/client.nix` and, when remote building is enabled, `modules/nix-cache/remote-builder-client.nix`. - Client hosts import `modules/nix-cache/client.nix` and, when remote building is enabled, `modules/nix-cache/remote-builder-client.nix`.
- The `nix-cache` host imports `modules/nix-cache/server.nix`. - The `nix-cache` host imports `modules/nix-cache/server.nix`.
## Binary cache signing keys (on nix-cache) ## Binary cache signing key
`modules/nix-cache/client.nix` hardcodes every client's trust in one
specific public key (`cache.local-1:usoWYanY3Kpq2+kDIS2nhWoLZiRxanmdysdzqCFBHW4=`).
That means whichever host is currently playing the `nix-cache` role has to
use that *exact* keypair — not a freshly generated one — or no client will
accept substitutes from it (they'd just silently fall back to building
from source). So unlike most per-host secrets, this one can't be
self-generated on first boot; it's managed via sops-nix like every other
secret in this repo, sourced from `secrets/nix-cache.yaml`'s
`cache-priv-key` entry (`modules/nix-cache/server.nix`).
**Adding or rotating the value:**
```bash ```bash
sudo install -d -m 0700 /etc/nix nix-shell -p sops --run 'sops secrets/nix-cache.yaml'
sudo nix-store --generate-binary-cache-key nix-cache-1 /etc/nix/cache-priv.pem /etc/nix/cache-pub.pem
sudo chmod 0600 /etc/nix/cache-priv.pem
sudo chmod 0644 /etc/nix/cache-pub.pem
cat /etc/nix/cache-pub.pem
``` ```
Do not commit private keys. Add (or replace) a `cache-priv-key` entry with the private key file's exact
Do not commit new password hashes or live credentials. Existing committed hashes contents. If you don't have it yet, generate a keypair once:
should be rotated and moved to host-local secret management.
```bash
nix-store --generate-binary-cache-key nix-cache-1 cache-priv.pem cache-pub.pem
```
— paste `cache-priv.pem`'s contents into the `cache-priv-key` entry above,
delete both local files afterward, and update
`trusted-public-keys` in `modules/nix-cache/client.nix` (and every already-built
client) to match `cache-pub.pem` if this is a genuine rotation rather than
a first-time bootstrap. Any `nixos-configurations.*-nix-cache` host picks
the new key up automatically on next activation — no more manual
`/etc/nix/cache-priv.pem` install step.
## Remote builder SSH keys ## Remote builder SSH keys
+11 -9
View File
@@ -1,18 +1,20 @@
{ config, pkgs, vars, ... }: { config, pkgs, vars, ... }:
{ {
# Generate the binary cache key pair on the nix-cache host: # nix-serve's signing key has to be the *same* key on every host that
# sudo install -d -m 0700 /etc/nix # ever plays the nix-cache role -- modules/nix-cache/client.nix hardcodes
# sudo nix-store --generate-binary-cache-key nix-cache-1 \ # every client's trust in one specific public key ("cache.local-1:..."),
# /etc/nix/cache-priv.pem \ # so a freshly self-generated key here wouldn't be trusted by anyone.
# /etc/nix/cache-pub.pem # Managed via sops-nix like every other secret in this repo instead of
# sudo chmod 0600 /etc/nix/cache-priv.pem # the old manual `nix-store --generate-binary-cache-key` step -- see
# sudo chmod 0644 /etc/nix/cache-pub.pem # "Binary cache signing key" in docs/nix-cache.md for how to add/rotate
# cat /etc/nix/cache-pub.pem # the value in secrets/nix-cache.yaml.
sops.secrets."cache-priv-key".sopsFile = ../../secrets/nix-cache.yaml;
services = { services = {
nix-serve = { nix-serve = {
enable = true; enable = true;
secretKeyFile = "/etc/nix/cache-priv.pem"; secretKeyFile = config.sops.secrets."cache-priv-key".path;
}; };
nginx = { nginx = {
+3 -2
View File
@@ -1,4 +1,5 @@
beszel-token: ENC[AES256_GCM,data:meuzUP/6wCssJDVTgbC0XwiLZPMGyDl55HEIiON9xOXCD9k6,iv:TDqWcp+8Mxd8wN09r5otQRQXq3XTeQphaTWxvvuLTAs=,tag:cRPZQGlwB/dTguBAheWPQg==,type:str] beszel-token: ENC[AES256_GCM,data:meuzUP/6wCssJDVTgbC0XwiLZPMGyDl55HEIiON9xOXCD9k6,iv:TDqWcp+8Mxd8wN09r5otQRQXq3XTeQphaTWxvvuLTAs=,tag:cRPZQGlwB/dTguBAheWPQg==,type:str]
cache-priv-key: ENC[AES256_GCM,data:6vQKIf7eS0WNL2Eptoi4VWr18SRMZfN/H/aFUUtXdMYQY5LLyBp2EHRKqZcGFuh1nZhUdAxUztq/CVXx+QFxKW+ElHxCxUSp0QqI1fdSkBkKZb8hlit5SoX9JtLzZGg0HBNM3nJu,iv:0J+xmrPJhInHhFR/c41ACjuTfaIoMkQFSfbL2KkgFa8=,tag:f4s9Szs5oprVVRSyXaX48A==,type:str]
sops: sops:
age: age:
- enc: | - enc: |
@@ -28,7 +29,7 @@ sops:
2SEMsctdGC+5E3ilPXvPpZ5RONZHbXxn6kQRBlBv6AJERpGDzsfgfA== 2SEMsctdGC+5E3ilPXvPpZ5RONZHbXxn6kQRBlBv6AJERpGDzsfgfA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age164px2a8e48ptsf9ngtan38aa6jls4jdl26mzrgzf6sn3vcvt49hqjrgr8w recipient: age164px2a8e48ptsf9ngtan38aa6jls4jdl26mzrgzf6sn3vcvt49hqjrgr8w
lastmodified: "2026-07-19T02:30:40Z" lastmodified: "2026-07-19T23:30:21Z"
mac: ENC[AES256_GCM,data:7+FeT6aeCGn+JFBXbPO0qP4BJ1nHPSennewv1kWkG+hOTIqs1ymuswUK1Hyfi6Z9h2umFX9HvK+o3qtmYvk6k7BUNe6w6QUHTNwm6lmNqrb7sgAE3iFVI2p9m14NGhgoTfnXx1M4JIZ1iuNYhCukpENI4+svIe+r7x5YeE5Evac=,iv:4AKAPI6upyAvHBr8BLWX7R/NupmJdcXdqiN8e0ZQ3ls=,tag:TkpfpCaLhn+Mx5cQZZuMdA==,type:str] mac: ENC[AES256_GCM,data:kLGE2xawQT7mx+sfw68hmGk5nCEGiEjZrqTEl9B1dtQmTrMwmoVr/1RISi4LfJrwxy31mDgff4lcIL4wIJuM373uk3X8j4RNyYQNTfKEkORT6r8NHeepNs267O77pKGd7OmcM4MT/BqOnB8ELS7Wlf2ect7CAlvUUVyc8icxgZE=,iv:EYLDsHYHZ1XOQXafOTqHHWpk/OBNq/R6IJnOBYV33E4=,tag:thxrCPC5oGvDjhK7Dz87YA==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.13.1 version: 3.13.1