Skip to content

Instantly share code, notes, and snippets.

@arrjay
Last active May 20, 2023 16:34
Show Gist options
  • Save arrjay/e0e06878156d139435f0484fc723c9eb to your computer and use it in GitHub Desktop.
Save arrjay/e0e06878156d139435f0484fc723c9eb to your computer and use it in GitHub Desktop.
transient ssh agent proxy, loading keys from pass, *and* ssh configuration to trivialize it
[url "s.github.com.arrjay.github.com"]
insteadof = github.com
insteadof = https://github.com/
insteadof = git@github.com
# with my script, and a pass item stored as s/github.com/arrjay
# I can start ssh as `ssh s.github.com.arrjay.github.com`
Host *.github.com
HostName github.com
# we can't use %C in proxycommand, which...is really what I want. ohwell.
IdentityAgent ~/.ssh/transient_agent-%n-%h%p%r
ProxyCommand ~/.ssh/transient_id_proxy %h %p %n ~/.ssh/transient_agent-%n-%h%p%r
ControlMaster no
User git
Host github.com
User git
ControlMaster no
#!/usr/bin/env bash
#set -x
# the password store items are probably a bit idiosyncratic to *me*, and the whole script could be more robust.
# no warannty, no guarantees, no problems
# my keys are stored something like s/github.com/arrjay, and just have a big ol' chunk of key material in 'em
# and if there is a passphrase, it's prefixed with ssh_passphrase:
# so we latch on to the sections between the begin and end private key lines, write that out.
# if we don't find a key, we attemp to transparently connect to the native ssh agent, with socat.
# start a ssh-agent with the key for the host we want loaded and unlocked via pass
# $1 - hostname
# $2 - port
# $3 - original hostname from ssh request
# $4 - identityagent path
host="${1}"
port="${2}"
original_host="${3}"
agentpath="${4}"
munge_twodots () {
# hand in a ssh original hostname, and component to transform
local pathy="${1}"
local hostdest="${2}"
local compo="${2//.//}" # we just turn all dots into slashes here
local res="${pathy%.*.*}" # remove the last two dotted components
res="${res//.//}" # turn all dots into slashes
# munge the final component, if found, back into the dotted form
local res_pre="${res%"${compo}"*}"
local res_suf="${res#"${res_pre}${compo}"}"
printf '%s%s%s' "${res_pre}" "${hostdest}" "${res_suf}"
}
# unfortunately, the only input we can mess with is the "original" hostname.
# so, turn that into a path we can try and pull a pass item out of.
case "${original_host}" in
*.github.com)
pass_check="$(munge_twodots "${original_host}" "github.com")"
;;
*.gitlab.com)
pass_check="$(munge_twodots "${original_host}" "gitlab.com")"
;;
*)
pass_check="${original_host}"
;;
esac
# check for password item
pass_data="$(mktemp /dev/shm/pdata-XXXXXXX)"
pass ls "${pass_check}" > "${pass_data}" 2> /dev/null ; rc="${?}"
# if we got pass data, try to pull the ssh key out here.
[ "${rc}" == 0 ] && {
ssh_key="$(mktemp /dev/shm/tssh-XXXXXXXX)"
awk '/-----BEGIN OPENSSH PRIVATE KEY-----/{f=1}/-----END OPENSSH PRIVATE KEY-----/{print;f=0}f' "${pass_data}" > "${ssh_key}"
}
# check for ssh_passphrase: in pass item and set SSH_ASKPASS if present
grep -qF "ssh_passphrase: " "${pass_data}" && {
askpass_exec="$(mktemp /dev/shm/sshp-XXXXXXXX)"
{
printf '%s\n' '#!/usr/bin/env bash'
# shellcheck disable=SC2016
printf "awk '%s' < %s\n" '$1 == "ssh_passphrase:" { print $2 ; }' "${pass_data}"
} > "${askpass_exec}"
chmod +x "${askpass_exec}"
# this is exported for ssh-add to pick up. it's gonna call awk and spit out the ssh_passphrase.
export SSH_ASKPASS="${askpass_exec}"
}
# start a scratch agent, set a trap (if that worked) to kill it off when we're done
old_ssh_agent="${SSH_AUTH_SOCK}"
unset SSH_AGENT_PID SSH_AUTH_SOCK
# shellcheck disable=SC2046
[ "${rc}" == 0 ] && eval $(ssh-agent -a "${agentpath}") > /dev/null # do it quietly!
# if we *didn't* find a key, forward to the *standard* ssh agent, if we had one.
[ "${rc}" != 0 ] && { socat "UNIX-LISTEN:${agentpath},fork" "UNIX:${old_ssh_agent}" & sc_pid=$! ; }
# all the script cleanup happens here.
# shellcheck disable=SC2064
trap "{ [ ${SSH_AGENT_PID} ] && ssh-agent -k ; [ ${pass_data} ] && rm -f ${pass_data} ; [ ${ssh_key} ] && rm -f ${ssh_key} ; [ ${askpass_exec} ] && rm -f ${askpass_exec} ; [ ${sc_pid} ] && kill ${sc_pid} ; }" EXIT
# load the key if we have one into our fresh ssh agent.
[ "${rc}" == 0 ] && [ -s "${ssh_key}" ] && ssh-add "${ssh_key}"
# hand ourselves off to nc. we're not _really_ a proxy today.
nc "${host}" "${port}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment