83 lines
3.0 KiB
Nix
83 lines
3.0 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
age-tpm = pkgs.writeShellApplication {
|
|
name = "age-tpm";
|
|
runtimeInputs = with pkgs; [
|
|
age
|
|
age-plugin-tpm
|
|
];
|
|
# The point is for "age" to have "age-plugin-tpm" in PATH.
|
|
text = "exec age \"$@\"";
|
|
};
|
|
|
|
# Determine which classes of secrets exist so we only add activation dependencies if
|
|
# sops-nix is actually going to generate the respective activation scripts.
|
|
hasRegularSecrets = lib.any (s: !s.neededForUsers) (lib.attrValues config.sops.secrets);
|
|
hasUserSecrets = lib.any (s: s.neededForUsers) (lib.attrValues config.sops.secrets);
|
|
in
|
|
{
|
|
options.fw.tpm = {
|
|
hostPath = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.either lib.types.path lib.types.str);
|
|
default = null;
|
|
description = "Path to the host directory containing tpm.id and secrets/ssh_host_ed25519_key.age";
|
|
};
|
|
};
|
|
|
|
config = {
|
|
boot.initrd.kernelModules = [
|
|
"tpm_crb" # NUC built-int TPM
|
|
"tpm_tis" # SuperMicro TPM
|
|
];
|
|
|
|
# Q: Why agenix/sops instead of systemd-creds aka LoadCredential?
|
|
# A: To read and edit credentials on developer VM, even if target-host is
|
|
# unavailable, or was reinstalled.
|
|
# Q: Why both agenix and sops-nix?
|
|
# A: Decrypting 20 secrets using TPM is too slow, so we use two-stage setup.
|
|
# We put master key (SSH host key) into agenix which uses TPM, then
|
|
# decrypt all other secrets using sops and master key.
|
|
age = lib.mkIf (config.fw.tpm.hostPath != null) {
|
|
ageBin = "${age-tpm}/bin/age-tpm";
|
|
|
|
# Generate .id file on target-host using:
|
|
# nix run nixpkgs#age-plugin-tpm -- --generate -o $(hostname).id
|
|
# then copy locally and add to repo. The contained blob is useless without
|
|
# TPM itself, and can therefore be checked into a Git repo.
|
|
#
|
|
# Interpolation forces file to be copied to target host.
|
|
identityPaths = [ "${config.fw.tpm.hostPath + "/tpm.id"}" ];
|
|
|
|
secrets.ssh_host_ed25519_key = {
|
|
file = config.fw.tpm.hostPath + /secrets/ssh_host_ed25519_key.age;
|
|
path = "/etc/ssh/ssh_host_ed25519_key";
|
|
};
|
|
};
|
|
|
|
# sops needs SSH key, which is decrypted by "agenixInstall". We can't depend
|
|
# on "agenix" because that happens after users have been set up (to chown).
|
|
system.activationScripts.setupSecrets = lib.mkIf hasRegularSecrets {
|
|
deps = lib.mkAfter [ "agenixInstall" ];
|
|
};
|
|
system.activationScripts.setupSecretsForUsers = lib.mkIf hasUserSecrets {
|
|
deps = lib.mkAfter [ "agenixInstall" ];
|
|
};
|
|
|
|
sops.secrets.root-password = lib.mkIf (config.fw.tpm.hostPath != null) {
|
|
# Having a TPM allows us to share root user password. It is useful for
|
|
# recovering from systemd "emergency" mode.
|
|
sopsFile = (dirOf config.fw.tpm.hostPath) + /common/secrets/root-password.bin;
|
|
format = "binary";
|
|
neededForUsers = true;
|
|
};
|
|
users.users.root.hashedPasswordFile = lib.mkIf (
|
|
config.fw.tpm.hostPath != null
|
|
) config.sops.secrets.root-password.path;
|
|
};
|
|
}
|