Revert commits after 089e185bd8

This commit is contained in:
2026-06-25 15:54:09 +09:00
parent 5cb560f3d1
commit 93aba545ed
6 changed files with 69 additions and 448 deletions
+3
View File
@@ -8,4 +8,7 @@
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";
};
}
+20 -152
View File
@@ -1,156 +1,32 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.git;
hmCfg = config.my.applications.git.homeManager;
sshCfg = config.my.applications.ssh;
gitSshSigningKeyCommand = pkgs.writeShellScript "git-ssh-signing-key" ''
set -u
expand_path() {
case "$1" in
"~")
printf '%s\n' "$HOME"
;;
"~/"*)
printf '%s\n' "$HOME/''${1#"~/"}"
;;
*)
printf '%s\n' "$1"
;;
esac
}
fido_present() {
${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null \
| ${pkgs.gnugrep}/bin/grep -q .
}
is_ssh_public_key() {
case "$1" in
ssh-*|ecdsa-*|sk-*)
return 0
;;
*)
return 1
;;
esac
}
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)
printf '%s\n' "$identity_file"
;;
*)
printf '%s.pub\n' "$identity_file"
;;
esac
}
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_git_key "$key"
}
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
# 3. 最後に通常のローカル鍵を agent に追加して使う。
${lib.concatMapStringsSep "\n" (
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 key found" >&2
exit 1
'';
in
{
options.my.applications.git.homeManager = {
enable = lib.mkEnableOption "git home-manager configuration";
signingKey = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "~/.ssh/id_ed25519_sk_rk.pub";
example = "~/.ssh/id_ed25519.pub";
description = "SSH public key path used for Git commit and tag signing.";
};
};
config = lib.mkIf hmCfg.enable {
assertions = [
{
assertion = hmCfg.signingKey != null;
message = "my.applications.git.homeManager.signingKey must be set per host.";
}
];
home-manager.sharedModules = [
{
programs.git = {
@@ -162,6 +38,12 @@ in
"!.envrc.example"
];
signing = {
key = hmCfg.signingKey;
format = "ssh";
signByDefault = true;
};
settings = {
user.name = cfg.userName;
user.email = cfg.userEmail;
@@ -173,20 +55,6 @@ in
log.date = "iso";
merge.conflictStyle = "diff3";
# SSH signing
gpg.format = "ssh";
# Git の SSH signing backend はデフォルトでも ssh-keygen だが、
# store path に固定して PATH 依存を避ける。
gpg.ssh.program = "${pkgs.openssh}/bin/ssh-keygen";
# user.signingKey は固定しない。
# 署名時にこの command が key::ssh-ed25519 ... を返す。
gpg.ssh.defaultKeyCommand = "${gitSshSigningKeyCommand}";
commit.gpgSign = true;
tag.gpgSign = true;
alias = {
br = "branch --sort=-committerdate";
co = "checkout";
-1
View File
@@ -16,7 +16,6 @@ in
PermitRootLogin = "no";
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
AllowAgentForwarding = true;
PubkeyAuthentication = "yes";
};
};
+15 -54
View File
@@ -3,7 +3,6 @@
config,
...
}:
let
cfg = config.my.applications.ssh;
in
@@ -14,71 +13,33 @@ in
];
options.my.applications.ssh = {
enable = lib.mkEnableOption "OpenSSH client and agent configuration";
enable = lib.mkEnableOption "OpenSSH client";
defaultIdentityFiles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"~/.ssh/id_ed25519"
];
description = ''
Default local SSH identity files.
These are used as the normal fallback identities when no agent key
is accepted, or when no forwarded agent is available.
'';
};
fidoIdentityFile = lib.mkOption {
defaultIdentityFile = lib.mkOption {
type = lib.types.str;
default = "~/.ssh/id_ed25519_sk_rk";
description = ''
Local FIDO2 resident-key SSH identity handle.
This file is only added to SSH identity candidates when a FIDO2
device is actually visible. Do not use file existence to decide
whether this key is usable.
'';
};
githubIdentityFiles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
Extra GitHub-specific SSH identity files.
Leave this empty if GitHub should use the normal agent, FIDO key,
and default identity fallback order.
'';
default = "~/.ssh/id_ed25519";
description = "Default SSH identity file";
};
addKeysToAgent = lib.mkOption {
type = lib.types.str;
default = "no";
example = "1h";
description = ''
Value for OpenSSH AddKeysToAgent.
Recommended default is "no" for this setup, because FIDO resident-key
handle files should not be added to the agent accidentally.
'';
description = "Add keys to SSH agent";
};
matchBlocks = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
type = lib.types.attrs;
default = { };
description = ''
Additional Home Manager OpenSSH settings blocks.
Use this for host-specific options such as ForwardAgent = true.
'';
example = lib.literalExpression ''
{
"proxmox-* *.home.arpa *.internal" = {
ForwardAgent = true;
description = "SSH match blocks";
};
}
'';
githubIdentityFiles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"~/.ssh/id_ed25519_sk_rk"
"~/.ssh/id_ed25519"
];
description = "SSH identity files for GitHub (tried in order)";
};
};
+11 -216
View File
@@ -4,146 +4,9 @@
config,
...
}:
let
cfg = config.my.applications.ssh;
hmCfg = config.my.applications.ssh.homeManager;
shellPathExpr =
path:
if lib.hasPrefix "~/" path then
''"$HOME/${lib.removePrefix "~/" path}"''
else
lib.escapeShellArg path;
fidoIdentityExpr = shellPathExpr cfg.fidoIdentityFile;
hasFidoDevice = pkgs.writeShellScript "ssh-has-fido-device" ''
${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null \
| ${pkgs.gnugrep}/bin/grep -q .
'';
sshYubikeyAgentSync = pkgs.writeShellApplication {
name = "ssh-yubikey-agent-sync";
runtimeInputs = with pkgs; [
openssh
libfido2
coreutils
gnugrep
gawk
];
text = ''
set -u
force=0
case "''${1:-}" in
"")
;;
"--force")
force=1
;;
*)
printf '%s\n' "usage: ssh-yubikey-agent-sync [--force]" >&2
exit 2
;;
esac
if [ -z "''${XDG_RUNTIME_DIR:-}" ]; then
exit 0
fi
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"
if [ ! -S "$SSH_AUTH_SOCK" ]; then
exit 0
fi
fido_identity=${fidoIdentityExpr}
fido_public_key="$fido_identity.pub"
state_dir="$XDG_RUNTIME_DIR/ssh-yubikey-agent-sync"
state_file="$state_dir/fido-device-state"
mkdir -p "$state_dir"
fido_present() {
fido2-token -L 2>/dev/null | grep -q .
}
fido_state() {
fido2-token -L 2>/dev/null | sha256sum | awk '{ print $1 }'
}
pub_fingerprint() {
[ -r "$1" ] || return 1
ssh-keygen -lf "$1" -E sha256 2>/dev/null | awk '{ print $2 }'
}
agent_has_public_key() {
public_key_file="$1"
fingerprint="$(pub_fingerprint "$public_key_file")" || return 1
ssh-add -l -E sha256 2>/dev/null \
| grep -Fq "$fingerprint"
}
remove_fido_from_agent() {
ssh-add -d "$fido_identity" >/dev/null 2>&1 || true
ssh-add -d "$fido_public_key" >/dev/null 2>&1 || true
}
add_fido_to_agent() {
[ -r "$fido_identity" ] || exit 0
[ -r "$fido_public_key" ] || exit 0
remove_fido_from_agent
ssh-add -q -t "''${SSH_YUBIKEY_AGENT_LIFETIME:-24h}" "$fido_identity" >/dev/null 2>&1 || exit 0
}
if fido_present; then
new_state="$(fido_state)"
old_state="$(cat "$state_file" 2>/dev/null || true)"
if [ "$force" -eq 1 ] \
|| ! agent_has_public_key "$fido_public_key" \
|| [ "$new_state" != "$old_state" ]; then
add_fido_to_agent
printf '%s\n' "$new_state" > "$state_file"
fi
else
remove_fido_from_agent
rm -f "$state_file"
fi
'';
};
userMatchBlockNames = lib.attrNames (cfg.matchBlocks or { });
userMatchBlocks = lib.mapAttrs (
_name: value: lib.hm.dag.entryBefore [ "my-github" "my-default" ] value
) (cfg.matchBlocks or { });
githubBlock = {
header = "Host github.com";
HostName = "github.com";
User = "git";
IdentityAgent = "SSH_AUTH_SOCK";
IdentitiesOnly = false;
ForwardAgent = false;
AddKeysToAgent = cfg.addKeysToAgent;
}
// lib.optionalAttrs (cfg.githubIdentityFiles != [ ]) {
IdentityFile = cfg.githubIdentityFiles;
};
in
{
options.my.applications.ssh.homeManager = {
@@ -151,100 +14,33 @@ in
};
config.home-manager.sharedModules = [
(
{ lib, ... }:
{
config = lib.mkIf hmCfg.enable {
home.packages = [
sshYubikeyAgentSync
pkgs.openssh
];
systemd.user.services.ssh-yubikey-agent-sync = {
Unit = {
Description = "Synchronize YubiKey SSH key with ssh-agent";
};
systemd.user.sockets.gcr-ssh-agent.Install.WantedBy = lib.mkForce [ ];
Service = {
Type = "oneshot";
services.ssh-agent.enable = true;
# NixOS programs.ssh.startAgent の socket。
Environment = [
"SSH_AUTH_SOCK=%t/ssh-agent"
"SSH_YUBIKEY_AGENT_LIFETIME=24h"
];
ExecStart = "${sshYubikeyAgentSync}/bin/ssh-yubikey-agent-sync";
};
};
systemd.user.timers.ssh-yubikey-agent-sync = {
Unit = {
Description = "Periodically synchronize YubiKey SSH key with ssh-agent";
};
Timer = {
OnBootSec = "5s";
OnUnitActiveSec = "10s";
AccuracySec = "2s";
Unit = "ssh-yubikey-agent-sync.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
home.sessionVariables = {
SSH_AUTH_SOCK = "\${XDG_RUNTIME_DIR}/ssh-agent";
};
programs.ssh = {
enable = true;
enableDefaultConfig = false;
settings = userMatchBlocks // {
"my-local-fido-sk-rk" =
lib.hm.dag.entryBefore
(
[
"my-github"
"my-default"
]
++ userMatchBlockNames
)
{
header = ''Match exec "${hasFidoDevice}"'';
IdentityFile = cfg.fidoIdentityFile;
# FIDO key の agent 登録は ssh-yubikey-agent-sync に任せる。
#
# ここで AddKeysToAgent を有効にすると、
# YubiKey 抜き差し後に stale な agent entry が残りやすい。
AddKeysToAgent = "no";
settings = cfg.matchBlocks // {
"github.com" = {
IdentityFile = cfg.githubIdentityFiles;
AddKeysToAgent = cfg.addKeysToAgent;
};
"my-github" = lib.hm.dag.entryBefore [ "my-default" ] githubBlock;
"my-default" =
lib.hm.dag.entryAfter
(
[
"my-local-fido-sk-rk"
"my-github"
]
++ userMatchBlockNames
)
{
header = "Host *";
IdentityAgent = "SSH_AUTH_SOCK";
IdentitiesOnly = false;
# default deny。
# agent forwarding したい host だけ cfg.matchBlocks 側で true にする。
ForwardAgent = false;
IdentityFile = cfg.defaultIdentityFiles;
"*" = {
IdentityFile = cfg.defaultIdentityFile;
AddKeysToAgent = cfg.addKeysToAgent;
SetEnv = {
TERM = "xterm";
};
@@ -253,6 +49,5 @@ in
};
};
}
)
];
}
+2 -7
View File
@@ -15,14 +15,9 @@ in
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [
openssh # OpenSSH client and server
libfido2 # FIDO2 support for SSH
];
programs.ssh = {
startAgent = true;
agentTimeout = "24h";
};
programs.gnupg.agent.enableSSHSupport = lib.mkForce false;
services.gnome.gcr-ssh-agent.enable = lib.mkForce false;
programs.ssh.startAgent = false;
services.gnome.gcr-ssh-agent.enable = false;
};
}