Files
nixos/modules/desktop-age-secrets.nix
Simon Gardling 26401f5316 yarn: rotate tpm identity after fTPM reset
BIOS 2423→4101 update on yarn required an fTPM reset, which broke the
sealed age identity at /var/lib/agenix/tpm-identity. Bootstrapped a new
identity against the new SRK and rotated yarn's recipient.

age-plugin-tpm 1.0+ emits age1tag1… (p256tag) recipients by default and
refuses to encrypt to legacy age1tpm1… ones, so rotated mreow's recipient
to the same encoding (same key, new bech32 HRP) and added an
age-plugin-tag→age-plugin-tpm symlink in the rage wrapper so rage's
plugin dispatch finds the binary under the new prefix. Stripped the
trailing host labels from the tpm recipient strings — rage's stricter
bech32 parser now rejects the trailing whitespace; labels live in
adjacent Nix comments instead.
2026-04-30 18:41:36 -04:00

92 lines
2.8 KiB
Nix

{
pkgs,
inputs,
...
}:
let
# age-plugin-tpm 1.0+ defaults to the new age1tag1… (p256tag) recipient
# encoding and refuses to encrypt to legacy age1tpm1… recipients. rage's
# plugin dispatch maps recipient prefixes to binaries (`age1tag1…` →
# `age-plugin-tag`), but nixpkgs only ships `age-plugin-tpm`. Provide a
# symlink so both prefixes resolve to the same binary.
age-plugin-tpm-with-tag = pkgs.symlinkJoin {
name = "age-plugin-tpm-with-tag";
paths = [ pkgs.age-plugin-tpm ];
postBuild = ''
ln -s age-plugin-tpm $out/bin/age-plugin-tag
'';
};
# Wrap rage so the plugin (under both names) is on PATH at activation time.
rageWithTpm = pkgs.writeShellScriptBin "rage" ''
export PATH="${age-plugin-tpm-with-tag}/bin:$PATH"
exec ${pkgs.rage}/bin/rage "$@"
'';
in
{
imports = [
inputs.agenix.nixosModules.default
];
# Expose the plugin + agenix CLI for interactive edits (`agenix -e …`).
environment.systemPackages = [
inputs.agenix.packages.${pkgs.system}.default
pkgs.age-plugin-tpm
];
age.ageBin = "${rageWithTpm}/bin/rage";
# Primary identity: TPM-sealed key, generated by scripts/bootstrap-desktop-tpm.sh.
# Fallback identity: admin SSH key. age tries paths in order, so if the TPM
# is wiped or the board is replaced the SSH key keeps secrets accessible until
# the TPM is re-bootstrapped. Both are encrypted recipients on every .age file.
age.identityPaths = [
"/var/lib/agenix/tpm-identity"
"/home/primary/.ssh/id_ed25519"
];
# Ensure the identity directory exists before agenix activation so a fresh
# bootstrap doesn't race the directory creation.
systemd.tmpfiles.rules = [
"d /var/lib/agenix 0700 root root -"
];
age.secrets = {
# Secureboot PKI bundle (db/KEK/PK keys + certs) consumed by lanzaboote
# via desktop-lanzaboote-agenix.nix at activation time.
secureboot-tar = {
file = ../secrets/desktop/secureboot.tar.age;
mode = "0400";
owner = "root";
group = "root";
};
# netrc for the private nix binary cache.
nix-cache-netrc = {
file = ../secrets/desktop/nix-cache-netrc.age;
mode = "0400";
owner = "root";
group = "root";
};
# yescrypt hash for the primary user.
password-hash = {
file = ../secrets/desktop/password-hash.age;
mode = "0400";
owner = "root";
group = "root";
};
# Master password for oo7-daemon's 'Login' keyring; the unit consumes it
# via systemd's ImportCredential machinery (see desktop-oo7-daemon.nix).
# Owner is `primary` so the user-scope systemd unit can LoadCredential it.
oo7-keyring-password = {
file = ../secrets/desktop/oo7-keyring-password.age;
mode = "0400";
owner = "primary";
group = "users";
};
};
}