feat: FIDO2 key type auto-detection, fallback chain, and --reset-signing
FIDO2 hardware key generation now handles diverse security keys: - Detect ed25519-sk vs ecdsa-sk support via ykman/fido2-token, with automatic fallback chain: ed25519-sk → ecdsa-sk → ecdsa-sk -O resident - Detect FIDO hardware by HID usage page (0xF1D0) instead of hardcoded Yubico vendor ID — works with SoloKeys, Titan, Nitrokey, etc. - Check libfido2 availability via ldconfig, dpkg-query, and rpm - Warn on Qubes OS vhci_hcd USB passthrough (corrupts CTAP2 messages) - Suppress admin recommendations when dependency is missing so install instructions stay visible - Accept ecdsa-sk keys in signing key filter New --reset-signing flag clears git signing config, removes allowed_signers entries, and moves/deletes key files so a fresh key can be generated. Bump version to 0.3.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
303
git-harden.sh
303
git-harden.sh
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# git-harden.sh — Audit and harden global git configuration
|
# git-harden.sh — Audit and harden global git configuration
|
||||||
# Usage: git-harden.sh [--audit] [-y] [--help]
|
# Usage: git-harden.sh [--audit] [-y] [--reset-signing] [--help]
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
@@ -10,7 +10,7 @@ IFS=$'\n\t'
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Constants
|
# Constants
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
readonly VERSION="0.2.3"
|
readonly VERSION="0.3.0"
|
||||||
readonly BACKUP_DIR="${HOME}/.config/git"
|
readonly BACKUP_DIR="${HOME}/.config/git"
|
||||||
readonly HOOKS_DIR="${HOME}/.config/git/hooks"
|
readonly HOOKS_DIR="${HOME}/.config/git/hooks"
|
||||||
readonly ALLOWED_SIGNERS_FILE="${HOME}/.config/git/allowed_signers"
|
readonly ALLOWED_SIGNERS_FILE="${HOME}/.config/git/allowed_signers"
|
||||||
@@ -38,6 +38,7 @@ fi
|
|||||||
# Mode flags (mutable — set by parse_args)
|
# Mode flags (mutable — set by parse_args)
|
||||||
AUTO_YES=false
|
AUTO_YES=false
|
||||||
AUDIT_ONLY=false
|
AUDIT_ONLY=false
|
||||||
|
RESET_SIGNING=false
|
||||||
PLATFORM=""
|
PLATFORM=""
|
||||||
|
|
||||||
# Audit counters
|
# Audit counters
|
||||||
@@ -57,6 +58,10 @@ DETECTED_CRED_HELPER=""
|
|||||||
HAS_YKMAN=false
|
HAS_YKMAN=false
|
||||||
HAS_FIDO2_TOKEN=false
|
HAS_FIDO2_TOKEN=false
|
||||||
|
|
||||||
|
# Set when a dependency is missing — suppresses trailing output so install
|
||||||
|
# instructions remain visible
|
||||||
|
MISSING_DEPENDENCY=false
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -155,6 +160,10 @@ parse_args() {
|
|||||||
AUDIT_ONLY=true
|
AUDIT_ONLY=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--reset-signing)
|
||||||
|
RESET_SIGNING=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
@@ -179,6 +188,7 @@ Audit and harden your global git configuration.
|
|||||||
Options:
|
Options:
|
||||||
--audit Run audit only (no changes), exit 0 if all OK, 2 if issues found
|
--audit Run audit only (no changes), exit 0 if all OK, 2 if issues found
|
||||||
-y, --yes Auto-apply all recommended settings (no prompts)
|
-y, --yes Auto-apply all recommended settings (no prompts)
|
||||||
|
--reset-signing Remove signing key config and optionally delete key files
|
||||||
--help, -h Show this help message
|
--help, -h Show this help message
|
||||||
--version Show version
|
--version Show version
|
||||||
|
|
||||||
@@ -1042,11 +1052,11 @@ detect_existing_keys() {
|
|||||||
|
|
||||||
pub_path="${identity_path}.pub"
|
pub_path="${identity_path}.pub"
|
||||||
if [ -f "$pub_path" ]; then
|
if [ -f "$pub_path" ]; then
|
||||||
# Only use ed25519 or ed25519-sk keys for signing
|
# Only use ed25519, ed25519-sk, or ecdsa-sk keys for signing
|
||||||
local key_type_str
|
local key_type_str
|
||||||
key_type_str="$(head -1 "$pub_path" 2>/dev/null || true)"
|
key_type_str="$(head -1 "$pub_path" 2>/dev/null || true)"
|
||||||
case "$key_type_str" in
|
case "$key_type_str" in
|
||||||
ssh-ed25519*|sk-ssh-ed25519*)
|
ssh-ed25519*|sk-ssh-ed25519*|sk-ecdsa-sha2*)
|
||||||
SIGNING_KEY_FOUND=true
|
SIGNING_KEY_FOUND=true
|
||||||
|
|
||||||
SIGNING_PUB_PATH="$pub_path"
|
SIGNING_PUB_PATH="$pub_path"
|
||||||
@@ -1079,16 +1089,17 @@ detect_fido2_hardware() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Linux: check /sys for FIDO HID devices (Yubico vendor 1050)
|
# Linux: check hidraw report descriptors for the FIDO HID usage page (0xF1D0).
|
||||||
|
# Bytes 06 d0 f1 at the start of the descriptor = HID usage page 0xF1D0.
|
||||||
|
# This works for any FIDO key vendor (Yubico, SoloKeys, Google Titan, etc.).
|
||||||
if [ "$PLATFORM" = "linux" ]; then
|
if [ "$PLATFORM" = "linux" ]; then
|
||||||
if [ -d /sys/bus/hid/devices ]; then
|
local rdesc
|
||||||
for dev_dir in /sys/bus/hid/devices/*; do
|
for rdesc in /sys/class/hidraw/hidraw*/device/report_descriptor; do
|
||||||
[ -d "$dev_dir" ] || continue
|
[ -f "$rdesc" ] || continue
|
||||||
case "$(basename "$dev_dir")" in
|
if od -A n -t x1 -N 3 "$rdesc" 2>/dev/null | grep -qi '06 d0 f1'; then
|
||||||
*1050:*) return 0 ;;
|
return 0
|
||||||
esac
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -1113,7 +1124,7 @@ signing_wizard() {
|
|||||||
# Offer key generation options
|
# Offer key generation options
|
||||||
printf '\n Signing key options:\n' >&2
|
printf '\n Signing key options:\n' >&2
|
||||||
printf ' 1) Generate a new ed25519 SSH key (software)\n' >&2
|
printf ' 1) Generate a new ed25519 SSH key (software)\n' >&2
|
||||||
printf ' 2) Generate a new ed25519-sk SSH key (FIDO2 hardware key)\n' >&2
|
printf ' 2) Generate a hardware-backed SSH key (FIDO2/U2F security key)\n' >&2
|
||||||
printf ' s) Skip signing setup\n' >&2
|
printf ' s) Skip signing setup\n' >&2
|
||||||
|
|
||||||
local choice
|
local choice
|
||||||
@@ -1140,6 +1151,73 @@ signing_wizard() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_signing() {
|
||||||
|
print_header "Reset Signing Configuration"
|
||||||
|
|
||||||
|
local signing_key
|
||||||
|
signing_key="$(git config --global --get user.signingkey 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [ -n "$signing_key" ]; then
|
||||||
|
printf ' Current signing key: %s\n' "$signing_key" >&2
|
||||||
|
|
||||||
|
# Remove git config entries
|
||||||
|
git config --global --unset user.signingkey 2>/dev/null || true
|
||||||
|
git config --global --unset commit.gpgsign 2>/dev/null || true
|
||||||
|
git config --global --unset tag.gpgsign 2>/dev/null || true
|
||||||
|
git config --global --unset tag.forceSignAnnotated 2>/dev/null || true
|
||||||
|
print_info "Removed signing configuration from git config"
|
||||||
|
|
||||||
|
# Remove allowed_signers entry if the key file exists
|
||||||
|
local key_path="${signing_key/#\~/$HOME}"
|
||||||
|
if [ -f "$key_path" ] && [ -f "$ALLOWED_SIGNERS_FILE" ]; then
|
||||||
|
local pub_key
|
||||||
|
pub_key="$(cat "$key_path")"
|
||||||
|
local tmpfile
|
||||||
|
tmpfile="$(mktemp -t git-harden-signers.XXXXXX)"
|
||||||
|
grep -vF "$pub_key" "$ALLOWED_SIGNERS_FILE" > "$tmpfile" 2>/dev/null || true
|
||||||
|
mv "$tmpfile" "$ALLOWED_SIGNERS_FILE"
|
||||||
|
print_info "Removed key from $ALLOWED_SIGNERS_FILE"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_info "No signing key in git config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all signing key files that would block fresh generation
|
||||||
|
local key_files=()
|
||||||
|
local candidate
|
||||||
|
for candidate in \
|
||||||
|
"${SSH_DIR}/id_ed25519_sk" "${SSH_DIR}/id_ed25519_sk.pub" \
|
||||||
|
"${SSH_DIR}/id_ecdsa_sk" "${SSH_DIR}/id_ecdsa_sk.pub" \
|
||||||
|
"${SSH_DIR}/id_ed25519" "${SSH_DIR}/id_ed25519.pub"; do
|
||||||
|
[ -f "$candidate" ] && key_files+=("$candidate")
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#key_files[@]} > 0 )); then
|
||||||
|
local backup_suffix
|
||||||
|
backup_suffix=".bak.$(date +%Y%m%dT%H%M%S)"
|
||||||
|
|
||||||
|
printf '\n Key files found:\n' >&2
|
||||||
|
local kf
|
||||||
|
for kf in "${key_files[@]}"; do
|
||||||
|
printf ' %s\n' "$kf" >&2
|
||||||
|
done
|
||||||
|
|
||||||
|
if prompt_yn "Delete these key files? (No = keep as .bak)"; then
|
||||||
|
for kf in "${key_files[@]}"; do
|
||||||
|
rm -f "$kf"
|
||||||
|
done
|
||||||
|
print_info "Key files deleted"
|
||||||
|
else
|
||||||
|
for kf in "${key_files[@]}"; do
|
||||||
|
mv "$kf" "${kf}${backup_suffix}"
|
||||||
|
done
|
||||||
|
print_info "Key files backed up with suffix ${backup_suffix}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_info "No signing key files found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Enable signing with a given public key path. Sets signingkey, gpgsign,
|
# Enable signing with a given public key path. Sets signingkey, gpgsign,
|
||||||
# and forceSignAnnotated in one step (no individual prompts).
|
# and forceSignAnnotated in one step (no individual prompts).
|
||||||
enable_signing() {
|
enable_signing() {
|
||||||
@@ -1187,14 +1265,57 @@ generate_ssh_key() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detect_fido2_sk_type() {
|
||||||
|
# Determine whether the security key supports ed25519-sk (FIDO2) or only
|
||||||
|
# ecdsa-sk (FIDO U2F). Prints "ed25519-sk" or "ecdsa-sk" to stdout.
|
||||||
|
#
|
||||||
|
# Detection order:
|
||||||
|
# 1. ykman — checks for FIDO2 application support (vs U2F-only)
|
||||||
|
# 2. fido2-token — probes device for ed25519 algorithm support
|
||||||
|
# 3. Default to ed25519-sk — ssh-keygen will fail fast if unsupported
|
||||||
|
if [ "$HAS_YKMAN" = true ]; then
|
||||||
|
local ykman_out
|
||||||
|
ykman_out="$(ykman info 2>/dev/null || true)"
|
||||||
|
if printf '%s' "$ykman_out" | grep -qi 'FIDO2'; then
|
||||||
|
printf 'ed25519-sk'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if printf '%s' "$ykman_out" | grep -qi 'FIDO\|U2F'; then
|
||||||
|
printf 'ecdsa-sk'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$HAS_FIDO2_TOKEN" = true ]; then
|
||||||
|
local device
|
||||||
|
device="$(fido2-token -L 2>/dev/null | head -1 | cut -d: -f1-2 || true)"
|
||||||
|
if [ -n "$device" ] && fido2-token -I "$device" 2>/dev/null | grep -qi 'ed25519'; then
|
||||||
|
printf 'ed25519-sk'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ -n "$device" ]; then
|
||||||
|
printf 'ecdsa-sk'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Default — try ed25519-sk; generate_fido2_key handles the fallback
|
||||||
|
printf 'ed25519-sk'
|
||||||
|
}
|
||||||
|
|
||||||
generate_fido2_key() {
|
generate_fido2_key() {
|
||||||
local key_path="${SSH_DIR}/id_ed25519_sk"
|
# Check for existing hardware-backed keys (both types)
|
||||||
|
local key_path_ed="${SSH_DIR}/id_ed25519_sk"
|
||||||
|
local key_path_ec="${SSH_DIR}/id_ecdsa_sk"
|
||||||
|
|
||||||
if [ -f "$key_path" ]; then
|
if [ -f "$key_path_ed" ]; then
|
||||||
print_warn "$key_path already exists. Not overwriting."
|
print_warn "$key_path_ed already exists. Not overwriting."
|
||||||
SIGNING_KEY_FOUND=true
|
SIGNING_KEY_FOUND=true
|
||||||
|
SIGNING_PUB_PATH="${key_path_ed}.pub"
|
||||||
SIGNING_PUB_PATH="${key_path}.pub"
|
return
|
||||||
|
fi
|
||||||
|
if [ -f "$key_path_ec" ]; then
|
||||||
|
print_warn "$key_path_ec already exists. Not overwriting."
|
||||||
|
SIGNING_KEY_FOUND=true
|
||||||
|
SIGNING_PUB_PATH="${key_path_ec}.pub"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1212,6 +1333,61 @@ generate_fido2_key() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# On Linux, ssh-keygen needs libfido2 for hardware-backed keys.
|
||||||
|
# Check ldconfig cache first, then fall back to dpkg/rpm query.
|
||||||
|
if [ "$PLATFORM" = "linux" ]; then
|
||||||
|
local has_libfido2=false
|
||||||
|
if ldconfig -p 2>/dev/null | grep -q libfido2; then
|
||||||
|
has_libfido2=true
|
||||||
|
elif command -v dpkg-query >/dev/null 2>&1 && dpkg-query -W libfido2-1 >/dev/null 2>&1; then
|
||||||
|
has_libfido2=true
|
||||||
|
elif command -v rpm >/dev/null 2>&1 && rpm -q libfido2 >/dev/null 2>&1; then
|
||||||
|
has_libfido2=true
|
||||||
|
fi
|
||||||
|
if [ "$has_libfido2" = false ]; then
|
||||||
|
print_warn "libfido2 is not installed (required for hardware-backed SSH keys)."
|
||||||
|
printf ' Install it with:\n' >&2
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
printf ' sudo apt-get install libfido2-1\n' >&2
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
printf ' sudo dnf install libfido2\n' >&2
|
||||||
|
elif command -v pacman >/dev/null 2>&1; then
|
||||||
|
printf ' sudo pacman -S libfido2\n' >&2
|
||||||
|
else
|
||||||
|
printf ' Install the libfido2 package for your distribution\n' >&2
|
||||||
|
fi
|
||||||
|
printf ' Then re-run this script.\n' >&2
|
||||||
|
MISSING_DEPENDENCY=true
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Qubes OS: USB passthrough (vhci_hcd) corrupts CTAP2 protocol messages.
|
||||||
|
# Keys with a FIDO2 PIN require CTAP2, which fails over vhci_hcd.
|
||||||
|
# Warn the user — PIN-protected keys won't work without a functioning
|
||||||
|
# qubes-ctap proxy.
|
||||||
|
if [ "$PLATFORM" = "linux" ] && [ -d /sys/bus/hid/devices ]; then
|
||||||
|
local has_vhci=false
|
||||||
|
local dev_dir
|
||||||
|
for dev_dir in /sys/class/hidraw/hidraw*; do
|
||||||
|
[ -d "$dev_dir" ] || continue
|
||||||
|
if grep -q 'vhci_hcd' "${dev_dir}/device/uevent" 2>/dev/null; then
|
||||||
|
has_vhci=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$has_vhci" = true ]; then
|
||||||
|
print_warn "Qubes OS detected — FIDO2 key is attached via USB passthrough (vhci_hcd)."
|
||||||
|
printf ' CTAP2 (PIN-protected keys) may fail over USB passthrough.\n' >&2
|
||||||
|
printf ' Keys without a PIN (U2F-only) should work.\n' >&2
|
||||||
|
printf ' For PIN-protected keys, generate on the host and copy the key pair,\n' >&2
|
||||||
|
printf ' or ensure qubes-ctap-proxy is fully configured.\n' >&2
|
||||||
|
if ! prompt_yn "Continue anyway?"; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# On macOS, the system ssh-keygen lacks FIDO2 support. Homebrew's openssh
|
# On macOS, the system ssh-keygen lacks FIDO2 support. Homebrew's openssh
|
||||||
# bundles ssh-sk-helper and builds FIDO2 into its own ssh-keygen binary.
|
# bundles ssh-sk-helper and builds FIDO2 into its own ssh-keygen binary.
|
||||||
# Detect by checking for ssh-sk-helper (NOT by running ssh-keygen, which
|
# Detect by checking for ssh-sk-helper (NOT by running ssh-keygen, which
|
||||||
@@ -1239,12 +1415,15 @@ generate_fido2_key() {
|
|||||||
printf ' Install Homebrew OpenSSH (includes built-in FIDO2):\n' >&2
|
printf ' Install Homebrew OpenSSH (includes built-in FIDO2):\n' >&2
|
||||||
printf ' brew install openssh\n' >&2
|
printf ' brew install openssh\n' >&2
|
||||||
printf ' Then re-run this script.\n' >&2
|
printf ' Then re-run this script.\n' >&2
|
||||||
|
MISSING_DEPENDENCY=true
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
keygen_cmd="$brew_keygen"
|
keygen_cmd="$brew_keygen"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf ' Generating ed25519-sk SSH key (touch your security key when prompted)...\n' >&2
|
# Detect best key type for this hardware
|
||||||
|
local sk_type
|
||||||
|
sk_type="$(detect_fido2_sk_type)"
|
||||||
|
|
||||||
local email
|
local email
|
||||||
email="$(git config --global --get user.email 2>/dev/null || true)"
|
email="$(git config --global --get user.email 2>/dev/null || true)"
|
||||||
@@ -1256,16 +1435,87 @@ generate_fido2_key() {
|
|||||||
mkdir -p "$SSH_DIR"
|
mkdir -p "$SSH_DIR"
|
||||||
chmod 700 "$SSH_DIR"
|
chmod 700 "$SSH_DIR"
|
||||||
|
|
||||||
|
# Build an ordered list of key generation attempts as parallel arrays.
|
||||||
|
# Each index holds one attempt: type, path, and whether to use -O resident.
|
||||||
|
local attempt_types=() attempt_paths=() attempt_resident=()
|
||||||
|
if [ "$sk_type" = "ecdsa-sk" ]; then
|
||||||
|
attempt_types+=("ecdsa-sk") attempt_paths+=("$key_path_ec") attempt_resident+=(false)
|
||||||
|
attempt_types+=("ecdsa-sk") attempt_paths+=("$key_path_ec") attempt_resident+=(true)
|
||||||
|
else
|
||||||
|
attempt_types+=("ed25519-sk") attempt_paths+=("$key_path_ed") attempt_resident+=(false)
|
||||||
|
attempt_types+=("ecdsa-sk") attempt_paths+=("$key_path_ec") attempt_resident+=(false)
|
||||||
|
attempt_types+=("ecdsa-sk") attempt_paths+=("$key_path_ec") attempt_resident+=(true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
local key_path="" key_type_label="" resident=""
|
||||||
|
local keygen_stderr keygen_rc
|
||||||
|
local attempt_num=0 i
|
||||||
|
|
||||||
|
for i in "${!attempt_types[@]}"; do
|
||||||
|
key_type_label="${attempt_types[$i]}"
|
||||||
|
key_path="${attempt_paths[$i]}"
|
||||||
|
resident="${attempt_resident[$i]}"
|
||||||
|
|
||||||
|
attempt_num=$((attempt_num + 1))
|
||||||
|
if (( attempt_num > 1 )); then
|
||||||
|
local fallback_desc="$key_type_label"
|
||||||
|
if [ "$resident" = true ]; then
|
||||||
|
fallback_desc="${key_type_label} (-O resident)"
|
||||||
|
fi
|
||||||
|
print_warn "Falling back to ${fallback_desc}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local label="$key_type_label"
|
||||||
|
if [ "$resident" = true ]; then
|
||||||
|
label="${key_type_label} resident"
|
||||||
|
fi
|
||||||
|
printf ' Generating %s SSH key (touch your security key when prompted)...\n' "$label" >&2
|
||||||
|
|
||||||
# Do NOT suppress stderr — per AC-7
|
# Do NOT suppress stderr — per AC-7
|
||||||
"$keygen_cmd" -t ed25519-sk -C "$email" -f "$key_path" </dev/tty
|
# Capture stderr to detect recoverable failures while still showing it
|
||||||
|
local tmpstderr keygen_args
|
||||||
|
tmpstderr="$(mktemp -t git-harden-keygen.XXXXXX)"
|
||||||
|
keygen_args=(-t "$key_type_label" -C "$email" -f "$key_path")
|
||||||
|
if [ "$resident" = true ]; then
|
||||||
|
keygen_args+=(-O resident)
|
||||||
|
fi
|
||||||
|
"$keygen_cmd" "${keygen_args[@]}" </dev/tty 2>"$tmpstderr" && keygen_rc=0 || keygen_rc=$?
|
||||||
|
keygen_stderr="$(cat "$tmpstderr")"
|
||||||
|
rm -f "$tmpstderr"
|
||||||
|
|
||||||
|
if [ -n "$keygen_stderr" ]; then
|
||||||
|
printf '%s\n' "$keygen_stderr" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Success
|
||||||
|
if (( keygen_rc == 0 )) && [ -f "${key_path}.pub" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for recoverable errors worth retrying with next attempt
|
||||||
|
if printf '%s' "$keygen_stderr" | grep -qi 'feature not supported\|unknown key type\|not supported\|invalid format'; then
|
||||||
|
# Clean up any partial files before next attempt
|
||||||
|
rm -f "$key_path" "${key_path}.pub"
|
||||||
|
# Brief pause to let the authenticator reset its CTAP2 state
|
||||||
|
# (back-to-back requests can cause spurious "invalid format")
|
||||||
|
sleep 1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Non-recoverable failure (user cancelled, wrong PIN, etc.)
|
||||||
|
break
|
||||||
|
done
|
||||||
|
|
||||||
if [ -f "${key_path}.pub" ]; then
|
if [ -f "${key_path}.pub" ]; then
|
||||||
SIGNING_KEY_FOUND=true
|
SIGNING_KEY_FOUND=true
|
||||||
|
|
||||||
SIGNING_PUB_PATH="${key_path}.pub"
|
SIGNING_PUB_PATH="${key_path}.pub"
|
||||||
print_info "Key generated: ${key_path}.pub"
|
print_info "Key generated: ${key_path}.pub"
|
||||||
else
|
else
|
||||||
print_warn "Key generation may have failed — ${key_path}.pub not found"
|
print_warn "Key generation failed. Common causes:"
|
||||||
|
printf ' • Security key firmware does not support SSH key enrollment\n' >&2
|
||||||
|
printf ' • Container/VM without full USB passthrough to the FIDO device\n' >&2
|
||||||
|
printf ' • Outdated libfido2 — try updating to the latest version\n' >&2
|
||||||
|
printf ' You can generate a software ed25519 key instead (option 1).\n' >&2
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1429,6 +1679,11 @@ main() {
|
|||||||
detect_platform
|
detect_platform
|
||||||
check_dependencies
|
check_dependencies
|
||||||
|
|
||||||
|
if [ "$RESET_SIGNING" = true ]; then
|
||||||
|
reset_signing
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Audit phase ---
|
# --- Audit phase ---
|
||||||
AUDIT_OK=0
|
AUDIT_OK=0
|
||||||
AUDIT_WARN=0
|
AUDIT_WARN=0
|
||||||
@@ -1452,7 +1707,9 @@ main() {
|
|||||||
# If everything is already OK, nothing to do
|
# If everything is already OK, nothing to do
|
||||||
if [ "$audit_exit" -eq 0 ]; then
|
if [ "$audit_exit" -eq 0 ]; then
|
||||||
print_info "All settings already match recommendations. Nothing to do."
|
print_info "All settings already match recommendations. Nothing to do."
|
||||||
|
if [ "$MISSING_DEPENDENCY" = false ]; then
|
||||||
print_admin_recommendations
|
print_admin_recommendations
|
||||||
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1471,7 +1728,9 @@ main() {
|
|||||||
apply_global_gitignore
|
apply_global_gitignore
|
||||||
apply_signing_config
|
apply_signing_config
|
||||||
apply_ssh_config
|
apply_ssh_config
|
||||||
|
if [ "$MISSING_DEPENDENCY" = false ]; then
|
||||||
print_admin_recommendations
|
print_admin_recommendations
|
||||||
|
fi
|
||||||
|
|
||||||
print_info "Hardening complete. Re-run with --audit to verify."
|
print_info "Hardening complete. Re-run with --audit to verify."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user