diff --git a/modules/home-manager/git.nix b/modules/home-manager/git.nix index db23f88..e6f5b74 100644 --- a/modules/home-manager/git.nix +++ b/modules/home-manager/git.nix @@ -1,17 +1,38 @@ -{ config, pkgs, inputs, ... }: +{ config, lib, pkgs, inputs, ... }: +let + email = "aaron@0x29a.ch"; + + # public half of every machine's ~/.ssh/id_ed25519, each one verified as a + # signing key in gitea. the key a host signs with is picked up from the key + # file itself, this list only tells the local git which machines to trust + signingKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHRhwzo1oxaT3fEySSmILKNnu9v30cfjx5G7FKpmfqeX aaron@argon" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGDkhvvTUcBSQdtXjX+Mw2Bp8HHhtiBm8aJi4ZxiBgZR aaron@neon" + ]; +in { + # gitea verifies signatures against the account keys, this teaches the local + # git the same trust so `git log --show-signature` resolves as well + xdg.configFile."git/allowed_signers".text = + lib.concatMapStrings (key: "${email} ${key}\n") signingKeys; + programs.git = { enable = true; settings = { push = { autoSetupRemote = true; }; user = { name = "aaron"; - email = "aaron@0x29a.ch"; + email = email; }; + gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers"; }; signing = { - key = "7A830180A05DAC59CDE43B0677D2F5DB48184456"; + format = "ssh"; + # point at the *public* key so ssh-keygen signs through the ssh agent + # instead of reading the passphrase protected private key from disk + key = "${config.home.homeDirectory}/.ssh/id_ed25519.pub"; + signer = "${pkgs.openssh}/bin/ssh-keygen"; signByDefault = true; }; }; diff --git a/modules/nixos/gnupg.nix b/modules/nixos/gnupg.nix index 1e1f380..6e1233c 100644 --- a/modules/nixos/gnupg.nix +++ b/modules/nixos/gnupg.nix @@ -4,12 +4,28 @@ # enable gnupg agent programs.gnupg.agent = { enable = true; - enableSSHSupport = true; - pinentryPackage = pkgs.pinentry-curses; + # a graphical pinentry never grabs the tty, so a passphrase prompt can no + # longer take over a terminal that a coding agent is driving + pinentryPackage = pkgs.pinentry-qt; + settings = { + # keep the key unlocked for a full working day instead of 10 minutes, + # so signing commits asks at most once per session + default-cache-ttl = 86400; + max-cache-ttl = 86400; + # fail an unattended signature instead of blocking on a prompt forever + pinentry-timeout = 120; + }; }; - environment.systemPackages = with pkgs; [ + environment.systemPackages = with pkgs; [ gnupg + pinentry-qt + # fallback for sessions without a display, switch pinentryPackage to use it pinentry-curses + # prime the passphrase cache on demand, e.g. before an agent session + (writeShellScriptBin "gpg-unlock" '' + echo | ${config.programs.gnupg.package}/bin/gpg --clearsign --output /dev/null + echo "gpg key unlocked" + '') ]; }