From c68a4fe73536ef7105840e44f84fd0e50d59db3b Mon Sep 17 00:00:00 2001 From: moons-14 Date: Thu, 25 Jun 2026 15:18:53 +0900 Subject: [PATCH] fix --- hosts/x1g13/default.nix | 3 - modules/applications/git/home.nix | 122 +++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 38 deletions(-) diff --git a/hosts/x1g13/default.nix b/hosts/x1g13/default.nix index be9db17..1ef0617 100644 --- a/hosts/x1g13/default.nix +++ b/hosts/x1g13/default.nix @@ -8,7 +8,4 @@ boot.initrd.luks.devices.cryptroot.device = "/dev/disk/by-partuuid/311d0f9c-f35f-42e6-b6fc-a4d67dd21b2e"; - my.applications.git.homeManager = { - signingKey = "~/.ssh/id_ed25519_sk_rk.pub"; - }; } diff --git a/modules/applications/git/home.nix b/modules/applications/git/home.nix index 7aa9c43..4bd075f 100644 --- a/modules/applications/git/home.nix +++ b/modules/applications/git/home.nix @@ -28,67 +28,119 @@ let esac } - print_key_line() { - key="$1" + fido_present() { + ${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null \ + | ${pkgs.gnugrep}/bin/grep -q . + } - case "$key" in + is_ssh_public_key() { + case "$1" in ssh-*|ecdsa-*|sk-*) - printf 'key::%s\n' "$key" - exit 0 + return 0 + ;; + *) + return 1 ;; esac } - print_pub_from_identity_file() { + is_fido_public_key() { + case "$1" in + sk-*) + return 0 + ;; + *) + return 1 + ;; + esac + } + + print_git_key() { + key="$1" + + is_ssh_public_key "$key" || return 1 + + printf 'key::%s\n' "$key" + exit 0 + } + + pubkey_file_for_identity() { identity_file="$(expand_path "$1")" case "$identity_file" in *.pub) - public_key_file="$identity_file" + printf '%s\n' "$identity_file" ;; *) - public_key_file="$identity_file.pub" + printf '%s.pub\n' "$identity_file" ;; esac + } - if [ ! -r "$public_key_file" ]; then - return 1 - fi + print_pubkey_file_as_git_key() { + public_key_file="$1" + + [ -r "$public_key_file" ] || return 1 IFS= read -r key < "$public_key_file" || return 1 [ -n "$key" ] || return 1 - print_key_line "$key" + print_git_key "$key" } - # 1. まず現在の SSH agent を優先する。 - # - # ローカル端末なら NixOS の ssh-agent。 - # SSH agent forwarding 先なら forwarded agent。 - # - # Git はここで返した公開鍵に対応する秘密鍵を ssh-agent 経由で使う。 - if [ -n "''${SSH_AUTH_SOCK:-}" ] && [ -S "$SSH_AUTH_SOCK" ]; then - while IFS= read -r key; do - print_key_line "$key" - done </dev/null || true) - EOF + print_first_usable_agent_key() { + [ -n "''${SSH_AUTH_SOCK:-}" ] || return 1 + [ -S "$SSH_AUTH_SOCK" ] || return 1 + + has_fido=0 + if fido_present; then + has_fido=1 + fi + + ${pkgs.openssh}/bin/ssh-add -L 2>/dev/null \ + | while IFS= read -r key; do + is_ssh_public_key "$key" || continue + + # YubiKey が無いときに agent に残っている sk-* 鍵を選ぶと、 + # Git 署名時に "agent refused operation" になる。 + if is_fido_public_key "$key" && [ "$has_fido" -ne 1 ]; then + continue + fi + + print_git_key "$key" + done + } + + add_identity_to_agent_and_print_pubkey() { + identity_file="$(expand_path "$1")" + public_key_file="$(pubkey_file_for_identity "$1")" + + [ -r "$identity_file" ] || return 1 + [ -r "$public_key_file" ] || return 1 + + [ -n "''${SSH_AUTH_SOCK:-}" ] || return 1 + [ -S "$SSH_AUTH_SOCK" ] || return 1 + + ${pkgs.openssh}/bin/ssh-add -q "$identity_file" >/dev/null 2>&1 || return 1 + + print_pubkey_file_as_git_key "$public_key_file" + } + + # 1. agent にすでにある鍵を優先する。 + # ただし YubiKey が無い場合、stale な sk-* 鍵は無視する。 + print_first_usable_agent_key || true + + # 2. YubiKey が刺さっている場合だけ _sk_rk を追加して使う。 + if fido_present; then + add_identity_to_agent_and_print_pubkey ${lib.escapeShellArg sshCfg.fidoIdentityFile} || true fi - # 2. agent に使える鍵が無ければ、YubiKey が見えている場合だけ _sk_rk を使う。 - # - # _sk_rk ファイルの存在では判定しない。 - fido_devices="$(${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null || true)" - if [ -n "$fido_devices" ]; then - print_pub_from_identity_file ${lib.escapeShellArg sshCfg.fidoIdentityFile} || true - fi - - # 3. 最後に通常のローカル identity へ fallback する。 + # 3. 最後に通常のローカル鍵を agent に追加して使う。 ${lib.concatMapStringsSep "\n" ( - identityFile: "print_pub_from_identity_file ${lib.escapeShellArg identityFile} || true" + identityFile: "add_identity_to_agent_and_print_pubkey ${lib.escapeShellArg identityFile} || true" ) sshCfg.defaultIdentityFiles} - printf '%s\n' "git-ssh-signing-key: no usable SSH signing public key found" >&2 + printf '%s\n' "git-ssh-signing-key: no usable SSH signing key found" >&2 exit 1 '';