JJJ's Blog

  • WordPress
  • GitHub
  • Twitter/X
  • Put your .ssh in a git repo

    Inspired by needing to make a quick change to my .ssh/config today I (finally?) decided to commit (some of) my .ssh directory into a local git repo.

    I started by cding into ~/.ssh and running:

    git init

    Next I thought it would be an extremely bad idea to version the keys in the Git database, so this is what my .gitignore looks like currently:

    # Common private key filenames
    id_*
    *_rsa
    *_dsa
    *_ecdsa
    *_ed25519
    *_sk
    
    # Common key/container formats
    *.pem
    *.key
    *.p12
    *.pfx
    *.ppk
    *.kdbx
    
    # Anything that looks like a key backup/export
    *private*
    *secret*
    *backup*
    *.bak
    
    # SSH runtime / multiplex / sockets / temp
    *.sock
    tmp/
    control-*
    *.ctl
    *.pid
    
    # Authorized keys can reveal infra shape
    authorized_keys
    
    # Known hosts churn + can reveal infra shape
    known_hosts
    known_hosts.old
    known_hosts2
    
    # SSH certificates (optional: keep ignored unless you explicitly want them tracked)
    *.pub
    *-cert.pub
    
    # OS/editor noise
    .DS_Store
    .vscode/
    .idea/
    *.swp
    *.swo
    
    # Other custom directories
    agent/
    old/
    pub/
    Sequel Ace/

    Side note: I have been using 1password’s SSH agent for years, so all of my keys already live outside of my .ssh directory, which I highly recommend doing (and customizing as needed…)

    Related to the above Git ignores, I also added a Git pre-commit hook that prevents me from naively committing anything that might unintentionally leak infrastructure shape into the Git database, by putting this into .ssh/git/hooks/pre-commit:

    #!/usr/bin/env bash
    set -euo pipefail
    
    # Block committing anything that looks like a private key or other sensitive material.
    # Runs only on staged content.
    
    die() {
      echo "✖ pre-commit: $*" >&2
      exit 1
    }
    
    # Files staged for commit (Added/Copied/Modified/Renamed)
    mapfile -t files < <(git diff --cached --name-only --diff-filter=ACMR)
    
    # 1) Block by filename patterns (fast fail)
    blocked_name_re='(^|/)(id_.*|.*_rsa|.*_dsa|.*_ecdsa|.*_ed25519|.*\.pem|.*\.key|.*\.p12|.*\.pfx|.*\.ppk)$'
    for f in "${files[@]}"; do
      if [[ "$f" =~ $blocked_name_re ]]; then
        die "Refusing to commit file that looks like a key: $f"
      fi
    done
    
    # 2) Block by content signatures (works even if renamed)
    # Check only text-ish blobs; if binary, git show may print nothing, which is fine.
    key_markers_re='BEGIN (OPENSSH|RSA|DSA|EC) PRIVATE KEY|BEGIN PRIVATE KEY|OPENSSH PRIVATE KEY|ENCRYPTED PRIVATE KEY'
    for f in "${files[@]}"; do
      # Read staged version
      blob="$(git show ":$f" 2>/dev/null || true)"
      if echo "$blob" | LC_ALL=C grep -Eqs "$key_markers_re"; then
        die "Refusing to commit: $f contains private key material"
      fi
    done
    
    # 3) Block accidental known_hosts commits (often noisy and revealing)
    for f in "${files[@]}"; do
      if [[ "$f" == "known_hosts" || "$f" == "known_hosts2" || "$f" == *"/known_hosts" ]]; then
        die "Refusing to commit known_hosts: $f"
      fi
    done
    
    exit 0

    Don’t forget to make it executable:

    chmod +x ~/.ssh/.git/hooks/pre-commit

    And so now this is what my .ssh/config mostly looks like:

    # ~/.ssh/config
    Include ~/.ssh/config.d/*.conf
    
    # Disable multiplexing (macOS 26.1 beta workaround)
    ControlMaster  no
    ControlPath    none
    ControlPersist no
    
    # Disconnect if no response
    ServerAliveInterval 60
    ServerAliveCountMax 5
    
    # Defaults for all hosts
    Host *
      IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
      ForwardAgent yes
      IPQoS none
      PreferredAuthentications publickey,password,keyboard-interactive

    And then, my various SSH configurations live in .ssh/config.d/ as different individual .conf files for each group/task/host, and they are automatically included at the top of the primary .ssh/config file.

    That’s pretty much it. Now when I need to change my SSH config, I open the directory in VSCode, make my edits, and commit them into the local Git repo.

    JJJ

    February 10, 2026
    Software
    macOS

Proudly Powered by WordPress