Last active
May 20, 2023 16:34
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[url "s.github.com.arrjay.github.com"] | |
insteadof = github.com | |
insteadof = https://github.com/ | |
insteadof = git@github.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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