8.8 KiB
title, tags, sources, contributors, created, updated
| title | tags | sources | contributors | created | updated | ||
|---|---|---|---|---|---|---|---|
| git-harden.sh |
|
|
2026-03-27 | 2026-03-27 |
Design Specification
Summary
A single-file bash script that audits and hardens a developer's global git configuration with security-focused defaults. It runs an audit-first flow (color-coded report of current state), then interactively applies recommended settings covering object integrity, protocol restrictions, filesystem protection, hook control, SSH signing with FIDO2 support, SSH transport hardening, and credential security. A -y flag auto-applies all defaults, and --audit exits after the report for CI use.
Requirements
- REQ-1: The script must audit all git global config settings listed in the Architecture section and report each as
[OK](matches recommended),[WARN](set to non-recommended value), or[MISS](not configured), with color-coded output to stderr. - REQ-2: The script must apply hardening settings via
git config --globalin interactive mode (prompt per setting, default Y) or auto-apply mode (-y). - REQ-3: The script must back up the current global git config to
~/.config/git/pre-harden-backup-<timestamp>.txtbefore making any changes. - REQ-4: The script must detect the platform (macOS/Linux) and select the appropriate credential helper (
osxkeychainon macOS,libsecretorcacheon Linux). - REQ-5: The script must provide an SSH signing setup wizard with two tiers: software SSH key (
ed25519) and FIDO2 hardware key (ed25519-sk), detecting existing keys and hardware automatically. - REQ-6: In
-ymode, signing must only be enabled (commit.gpgsign,tag.gpgsign) if a valid signing key is detected and its public key file is readable. If no key exists, only non-breaking settings (gpg.format,gpg.ssh.allowedSignersFile) are configured. - REQ-7: The script must audit and optionally harden
~/.ssh/configwith secure defaults (StrictHostKeyChecking accept-new,HashKnownHosts yes,IdentitiesOnly yes,AddKeysToAgent yes, modernPubkeyAcceptedAlgorithms). - REQ-8: The script must print admin/org-level recommendations (branch protection, vigilant mode, force-push policy, token hygiene) as informational output without applying changes.
- REQ-9: The script must be compatible with Bash 3.2 (macOS default). No associative arrays, no
mapfile/readarray, no${var,,}case conversion, nodeclare -A. - REQ-10: The script must be idempotent — re-running it on an already-hardened system changes nothing.
- REQ-11: Exit codes must be: 0 (all OK or changes applied), 1 (error), 2 (audit found issues).
- REQ-12: The script must pass
shellcheckwith no errors or warnings.
Acceptance Criteria
- AC-1: Running
git-harden.sh --auditon a fresh git config prints a report with[MISS]for all 20+ hardening settings and exits with code 2. - AC-2: Running
git-harden.sh -yon a fresh config applies all settings; a subsequent--auditexits with code 0 (all[OK]). - AC-3: Running
git-harden.sh -ytwice produces identical git config output (idempotent). - AC-4: On macOS,
credential.helperis set toosxkeychain. On Linux with libsecret available, it uses the detected libsecret path; otherwisecache --timeout=3600. - AC-5: If
credential.helperis currentlystore, the audit reports[WARN]and interactive mode offers to replace it. - AC-6: The signing wizard detects existing
~/.ssh/id_ed25519and offers to use it as signing key. - AC-7: If a FIDO2 device is detected (via
ykman infoorfido2-token -L), the wizard offers Tier 2 (generateed25519-skkey). Thessh-keygenstderr is not suppressed. - AC-8: In
-ymode with no SSH key present,commit.gpgsignandtag.gpgsignare NOT set. A note is printed directing the user to run interactively. - AC-9:
~/.config/git/hooks/directory is created if missing.core.hooksPathis set with literal~(not expanded). - AC-10:
~/.ssh/configis created with mode600(and~/.ssh/with mode700) if they don't exist. SSH directives are only appended if not already present. - AC-11: The script runs without error on Bash 3.2.57 (macOS default) and Bash 5.x (Linux).
- AC-12:
shellcheck git-harden.shproduces zero errors and zero warnings. - AC-13: A config backup file is created at
~/.config/git/pre-harden-backup-<timestamp>.txtbefore any changes are made. - AC-14:
protocol.allowis set tonever; onlyhttps,ssh, andfile(asuser) are whitelisted.git://andext://are explicitly blocked. - AC-15: If
pull.rebaseis currently set, the audit phase reports a[WARN]about the conflict withpull.ff=only.
Architecture
The script is a single file git-harden.sh at the repo root, using #!/usr/bin/env bash with strict mode (set -o errexit, set -o nounset, set -o pipefail, IFS=$'\n\t').
Internal structure (functions)
All variables inside functions use local. Global constants use readonly UPPER_CASE. The script follows the conventions in AGENTS.md (Shell Script Development Standards v2.0).
Entry point and argument parsing:
main()— orchestrates the full flow: preflight, audit, apply, recommendationsparse_args()— handles-y,--audit,--helpflagsdie()— fatal error handler (prints to stderr, exits 1)
Platform detection:
detect_platform()— setsPLATFORMtomacosorlinuxviauname -scheck_dependencies()— verifiesgit>= 2.34.0,ssh-keygenpresent; detects optional tools (ykman,fido2-token, credential helpers)
Audit functions (read-only, no side effects):
audit_git_config()— checks each hardening setting againstgit config --global --getaudit_ssh_config()— checks~/.ssh/configfor recommended directivesaudit_signing()— checks signing config and key availabilityprint_audit_report()— renders the color-coded report to stderr
Apply functions (write operations):
apply_git_config()— sets each non-OK setting viagit config --globalapply_ssh_config()— appends missing directives to~/.ssh/configsigning_wizard()— interactive signing setup (tier selection, key generation/detection)detect_existing_keys()— scans~/.ssh/and~/.ssh/configIdentityFile directivesdetect_fido2_hardware()— checksykman infoandfido2-token -Lgenerate_ssh_key()— wrapsssh-keygen -t ed25519generate_fido2_key()— wrapsssh-keygen -t ed25519-skwith touch promptsetup_allowed_signers()— creates/updates~/.config/git/allowed_signersprint_admin_recommendations()— prints org-level advice to stderr
Helpers:
prompt_yn()— prompt with configurable default, respects-ymodeprint_ok(),print_warn(),print_miss()— color-coded status output to stderr
Git config settings applied
Object integrity: transfer.fsckObjects=true, fetch.fsckObjects=true, receive.fsckObjects=true
Protocol restrictions (default deny): protocol.allow=never, protocol.https.allow=always, protocol.ssh.allow=always, protocol.file.allow=user, protocol.git.allow=never, protocol.ext.allow=never
Filesystem protection: core.protectNTFS=true, core.protectHFS=true, core.fsmonitor=false
Hook control: core.hooksPath=~/.config/git/hooks
Repository safety: safe.bareRepository=explicit, submodule.recurse=false
Pull/merge hardening: pull.ff=only, merge.ff=only
Transport security: url."https://".insteadOf=http://, http.sslVerify=true
Credential storage: credential.helper (platform-detected)
Signing: gpg.format=ssh, user.signingkey (detected), commit.gpgsign=true, tag.gpgsign=true, tag.forceSignAnnotated=true, gpg.ssh.allowedSignersFile=~/.config/git/allowed_signers
Visibility: log.showSignature=true
Optional (interactive only): core.symlinks=false, merge.verifySignatures=true
SSH config directives appended to ~/.ssh/config
StrictHostKeyChecking accept-new, HashKnownHosts yes, IdentitiesOnly yes, AddKeysToAgent yes, PubkeyAcceptedAlgorithms ssh-ed25519,sk-ssh-ed25519@openssh.com,ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com
Dependencies
Required: git >= 2.34.0, ssh-keygen
Optional: ykman or fido2-token (FIDO2 detection), OS keychain (osxkeychain on macOS, libsecret on Linux)
Out of Scope
- GPG signing support — SSH signing covers the same use cases with far less complexity
- Server-side changes — the script only modifies the developer's local config
- Undo/restore command — the script is idempotent; devs can manually unset any setting with
git config --global --unset - Windows/WSL support
- Per-repo config modification — global config only
- Hook dispatcher scripts for projects using husky/lefthook/pre-commit — mentioned in admin recommendations but not implemented