#!/bin/bash FILELOGLEVEL=DEBUG # loggerfactory LANG=C for f in logerror logwarning loginfo logdebug logtrace ENTRY EXIT SCRIPTENTRY SCRIPTEXIT; do if LANG=C type -t $f 2> /dev/null| grep -q 'function'; then : else fx=${f//log/} echo create function $f for $fx >&2 eval "$f () { \ local loglevels; \ declare -a loglevels; \ case $fx in error) loglevels=() ;; warn) loglevels=(\"ERROR\") ;; info) loglevels=(\"ERROR\" \"WARN\" ) ;; debug) loglevels=( \"ERROR\" \"WARN\" \"INFO\" ) ;; trace|ENTRY|EXIT|SCRIPTENTRY|SCRIPTEXIT) loglevels=( \"ERROR\" \"WARN\" \"INFO\" \"DEBUG\" ) ;; esac if [[ ! \${loglevels[*]} =~ \"\${LOGLEVEL-\${LOGLEVEL_DEFAULT}}\" ]];then \ echo "\$@" >&2 fi; \ }" unset fx fi done unset f usage(){ cat << EOF Usage: ssh-agent-start-or-restart [[-c]|[--create-only]]|[[-t]|[--token-only]]|[[-k]|[--key-only]]|[[-r]|[-f]|[--readd]|[--force]] [] If started only with , the script looks up in configured identity-path \$SSH_IDENTITIES_DIR (${SSH_IDENTITIES_DIR}) if it can find a directory named after . If no is given, the identity is set to \$SSH_DEFAULT_IDENTITY ($SSH_DEFAULT_IDENTITY) configured via Environment. IF \$SSH_DEFAULT_IDENTITY is also not set, default is the SSH_DEFAULT_IDENTITY The output is the name of the file, where ssh-agent infomations are hold to load it to current shell for further actions. Use "$ eval \$()", if you want to load the SSH_AUTH_SOCK and SSH_AGENT_PID in current shell or shorter "$ loadagent []" -c|--create-only Create or restart only the agent. Do not load any key or token in it. The Output is used for loading the agent in the current shell. (loadagent ) -t|--token-only To add or renew only configured pkcs11-hardware-token configured in ${SSH_IDENTITIES_DIR}/, just use this. -k|--key-only To add or renew only configured keys configured in ${SSH_IDENTITIES_DIR}/, just use this. -r|-f|--readd-token|--force remove all in ${SSH_IDENTITIES_DIR}/ configured keys and tokens and readd them again. Depends on -t an -k Option to select wheter only keys or tokens only. If no -t and -k is given, all keys and token are removed and readded again. Just to be asked for password again, if you plugged off hardware-token and plugged it in again. --rm|--remove remove keys and token instead of adding them. -h|--info Show this info EOF } check_token(){ # it's the same as "token" in functions.sh # defined here also, to work also in environments, where functions.sh couldn't be sourced [ -z "${P11M:+x}" ] && { P11M=$PKCS11_MODULE; export P11M; } # If DISPLAY is set, ssh-add calls ssh-askpass, and if its in remote-terminal, it wont work # So remember and unset DISPLAY, and set it at the end again, if it was set before [ $DISPLAY ] && local DISPLAY_ORIG=$DISPLAY [ $DISPLAY ] && logtrace "unset DISPLAY: $DISPLAY" [ $DISPLAY ] && unset DISPLAY # Write public keys of all in agent stored keys to a temporary file local tmppubkey="$(mktemp -p ${XDG_RUNTIME_DIR} pubkey.XXXXXX.pub)" logtrace "tmppubkey: $tmppubkey" loginfo "$(ssh-add -L > $tmppubkey)" # Check if public-keys in tmppubkey are working. They are not working, if you removed and add back hardware-token. loginfo "$(ssh-add -T ${tmppubkey}|| { ssh-add -e $P11M; ssh-add -s $P11M; } )" logdebug "$(rm "${tmppubkey}")" logdebug "$(ssh-add -l )" [ $DISPLAY_ORIG ] && logtrace "reset DISPLAY=$DISPLAY_ORIG" [ $DISPLAY_ORIG ] && export DISPLAY=$DISPLAY_ORIG } createonly=false tokenonly=false readd=false keyonly=false remove=false while :; do case $1 in -c|--create-only) createonly=true shift ;; -t|--token-only) tokenonly=true shift ;; -k|--key-only) keyonly=true shift ;; -r|-f|--readd-token|--force) readd=true shift ;; --rm|--remove) remove=true shift ;; -h|--info) usage exit 0 ;; -*) echo "Unknown urgument: »$1«" exit 1 ;; *) #ssh_identity=${1-${SSH_DEFAULT_IDENTITY}} ssh_identity=${1} break ;; esac done SCRIPTENTRY [ -z "${SSH_IDENTITIES_DIR+x}" ] && { SSH_IDENTITIES_DIR="${SSH_IDENTITIES_DEFAULT_DIR-${HOME}/.ssh/identities}"; export SSH_IDENTITIES_DIR; } [ -z "${SSH_AGENTS_DIR+x}" ] && { SSH_AGENTS_DIR=${SSH_AGENTS_DEFAULT_DIR-~/.ssh/agents}; export SSH_AGENTS_DIR; } [ -z "${SSH_AGENT_SOCKETS_DIR+x}" ] && { SSH_AGENT_SOCKETS_DIR=${SSH_AGENT_SOCKETS_DEFAULT_DIR-~/.ssh/agents}; export SSH_AGENT_SOCKETS_DIR; } [ -z "${SSH_AGENT_OPTIONS+x}" ] && { SSH_AGENT_OPTIONS=${SSH_AGENT_DEFAULT_OPTIONS--t 7200 }; export SSH_AGENT_OPTIONS; } logtrace " SSH_AGENTS_DIR: $SSH_AGENTS_DIR" logtrace "SSH_AGENT_SOCKETS_DIR: $SSH_AGENT_SOCKETS_DIR" logtrace " SSH_IDENTITIES_DIR: $SSH_IDENTITIES_DIR" [ -z "${SSH_AGENTS_DIR-x}" ] || mkdir -vp "$SSH_AGENTS_DIR" [ -z "${SSH_AGENT_SOCKETS_DIR-x}" ] || mkdir -vp "$SSH_AGENT_SOCKETS_DIR" [ -z "${SSH_IDENTITIES_DIR-x}" ] || mkdir -vp "$SSH_IDENTITIES_DIR" agent-start-or-restart () { # If DISPLAY is set, ssh-add calls ssh-askpass, and if its in remote-terminal, it wont work # So remember and unset DISPLAY, and set it at the end again, if it was set before [ $DISPLAY ] && local DISPLAY_ORIG=$DISPLAY [ $DISPLAY ] && logtrace "unset DISPLAY: $DISPLAY" [ $DISPLAY ] && unset DISPLAY ENTRY local ssh_identity local agentfile local agentsocket local ret if [ -n "${1+x}" ]; then ssh_identity="${1}" identitydir=${SSH_IDENTITIES_DIR}/${ssh_identity} loginfo "ssh-identität: ${ssh_identity}" >&2 if [ -d ${identitydir} ]; then [ -e "${identitydir}/config" ] && . "${identitydir}/config" agentfile="${SSH_AGENTS_DIR}/agent-${ssh_identity}-$(hostname)" agentsocket="${SSH_AGENT_SOCKETS_DIR}/socket-${ssh_identity}-$(hostname)" logtrace "agentfile for ${ssh_identity}: $agentfile" logtrace "agentsocket for ${ssh_identity}: $agentsocket" if (! $keyonly && ! $tokenonly ) && $remove ; then logdebug "delete keys and tokens in this ssh-agent" logdebug "$(ssh-runinagent $agentfile ssh-add -D 2>&1)" createonly=true else if [ -e $agentfile ]; then local msg # TODO make in runinagent msg="$(/bin/sh -c "unset SSH_AUTH_SOCK SSH_AGENT_PID; . $agentfile >/dev/null 2>&1; ssh-add -l 2>&1")" local ret=$? logtrace "Output from check for running: $msg" case $ret in 0) logdebug "agent is running" ;; 1) logdebug "agent is running, but:" logwarning "$msg" ;; 2) logdebug "former agent is not running -> start it" logdebug "SSH_AGENT_OPTIONS: $SSH_AGENT_OPTIONS" [ -e $agentsocket ] && { logdebug -n "remove socketfile: $( rm -v "$agentsocket" )"; } logtrace "$(ssh-agent -a $agentsocket ${SSH_AGENT_OPTIONS} > $agentfile )" logdebug "agent started" ;; esac else logdebug "agent did not exist -> create it" logtrace "ssh-agent -a $agentsocket \> $agentfile" logtrace "$(ssh-agent -a $agentsocket $SSH_AGENT_OPTIONS > $agentfile )" logdebug "agent started" fi fi #logdebug "ssh-agent for identity »$ssh_identity«: $agentfile" $createonly && logtrace "current loaded keys after action: $(ssh-runinagent $agentfile ssh-add -l)" echo $agentfile ret=0 else logwarning "ssh-identity »$ssh_identity« is not configured. Please create $identitydir and add keys" ret=2 fi else if which gnome-keyring-daemon >/dev/null 2>&1; then logdebug "no identity given -> gnome-keyrings ssh-agent" agentfile="${SSH_AGENTS_DIR}/agent-gnome_session-$(hostname)" agentsocket="${SSH_AGENT_SOCKETS_DIR}/socket-gnome-session-$(hostname)" gnome-keyring-daemon -s > $agentfile logdebug "$(cat $agentfile)" logdebug "ssh-agent for identity »$ssh_identity«: $agentfile" # logdebug "currently loaded keys after action: #$(ssh-runinagent $agentfile ssh-add -l)" echo $agentfile ret=0 else logwarning "no identity given -> exit" ret=1 fi fi [ $DISPLAY_ORIG ] && logtrace "reset DISPLAY=$DISPLAY_ORIG" [ $DISPLAY_ORIG ] && export DISPLAY=$DISPLAY_ORIG EXIT return $ret } agent-load-identity-keys () { ENTRY # If DISPLAY is set, ssh-add calls ssh-askpass, and if its in remote-terminal, it wont work # So remember and unset DISPLAY, and set it at the end again, if it was set before [ $DISPLAY ] && local DISPLAY_ORIG=$DISPLAY [ $DISPLAY ] && logtrace "unset DISPLAY: $DISPLAY" [ $DISPLAY ] && unset DISPLAY local ssh_identity local agentfile local agentsocket local fingerprints declare -a fingerprints local pubkeysonly declare -a pubkeysonly local fingerprint local tokenfingerprint if [ -n "${1+x}" ]; then ssh_identity="${1}" identitydir=${SSH_IDENTITIES_DIR}/${ssh_identity} if [ -d ${identitydir} ]; then [ -e "${identitydir}/config" ] && . "${identitydir}/config" agentfile="${SSH_AGENTS_DIR}/agent-${ssh_identity}-$(hostname)" agentsocket="${SSH_AGENT_SOCKETS_DIR}/socket-${ssh_identity}-$(hostname)" logdebug "SSH_ADD_OPTIONS: $SSH_ADD_OPTIONS" logtrace "agentfile: $agentfile" logtrace "agentsocket: $agentsocket" logtrace "identitydir: $identitydir" fingerprints=( $(ssh-runinagent $agentfile "ssh-add -l|awk '{print \$2}'") ) logdebug "fingerprints from loaded keys before action:" for f in ${fingerprints[*]};do logdebug "$f" done if ! $tokenonly ; then # load keys for key in $(ls ${SSH_IDENTITIES_DIR}/${ssh_identity}/id_*|grep -v "pub$\|so$\|config$\|public$"); do fingerprint=$(ssh-keygen -l -f $key|awk '{print $2}') if [[ ${fingerprints[*]} =~ "$fingerprint" ]]; then logdebug "key: $(basename $key) (with fp $fingerprint) is loaded" if $readd || $remove ; then $readd && logdebug "re-add key $key" $remove && logdebug "remove key $key" logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} -d ${key} 2>&1)" $remove || logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} ${key} 2>&1)" fi else logdebug "key: $key is not loaded -> load it" #logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} -d ${key} 2>&1)" $remove || logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} ${key} 2>&1)" fi done fi if ! $keyonly ; then # load tokens for pubkey in $(ls ${SSH_IDENTITIES_DIR}/${ssh_identity}/id_*|grep "pub$\|public$"|grep -v "cert.pub"); do if $( ls ${pubkey%.pub} 1>/dev/null 2>&1);then echo "pubkey with privkey: $pubkey" else echo "pubkey without privkey: $pubkey" pubkeysonly+=($pubkey) fi if [ -e "${pubkey%.pub}-cert.pub" ]; then #logwarning "${pubkey%.pub}-cert.pub: $(date +%s -d $(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}'))" #logwarning "now: $(date +%s -d NOW)" cert_exp_date=$(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}') #[ $(date +%s -d $(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}')) -gt $(date +%s -d NOW) ] \ [ $(date +%s -d $cert_exp_date) -gt $(date +%s -d NOW) ] \ || logwarning "CERTIFICATE IS NOT VALID ANYMORE: ${pubkey%.pub}-cert.pub" #[ $(date +%s -d $(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}')) -lt $(date +%s -d "$SSH_CERT_VALIDITY_WARN_SEC") ] \ [ $(date +%s -d $cert_exp_date) -lt $(date +%s -d "$SSH_CERT_VALIDITY_WARN_SEC") ] \ && logwarning "CERTIFICATE expires in $(echo "scale=0; ( `date -d $cert_exp_date +%s` - `date -d now +%s`) / (24*3600)" | bc -l) days: ${pubkey%.pub}-cert.pub" fi done loginfo "pubkeysonly: ${pubkeysonly[@]} (count: ${#pubkeysonly[*]})" for key in $(ls ${SSH_IDENTITIES_DIR}/${ssh_identity}/*|grep "\.so$"); do echo "key: $key" logdebug "P11M1: ${P11M:-not set} - key: $key" #[ -e "${P11M-x}" ] || [ "$(readlink - f $key)" == "$P11M" ] || key="$P11M" #[ "$(readlink - f $key)" == "$P11M" ] || key="$P11M" [ "$(readlink -f $key)" != "$P11M" -a "${P11M:-x}" != "x" ] && key="$P11M" logdebug "P11M2: ${P11M:-not set} - key: $key" echo "P11M: ${P11M:-not set} - key: $key" tokenfingerprint="$(ssh-keygen -l -D $key|tr -s ' '|awk '{print $2}')" if [[ ${fingerprints[*]} =~ "$tokenfingerprint" ]]; then logdebug "token: $key ($tokenfingerprint) is loaded" check-pubkeysonly if $readd || $remove ; then $readd && logdebug "re-add token $key" $remove && logdebug "remove token $key" echo agentfile1 $agentfile \$SSH_ADD_OPTIONS $key >&2 logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} -e "${key}" 2>&1)" ssh-runinagent $agentfile echo $SSH_AUTH_SOCK >&2 $remove || logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} -s "${key}" 2>&1)" fi else logdebug "token: $key is not loaded -> load it" logdebug "$(ssh-runinagent $agentfile ssh-add -v ${SSH_ADD_OPTIONS} -e ${key} 2>&1)" $remove || logdebug "$(ssh-runinagent $agentfile ssh-add ${SSH_ADD_OPTIONS} -s ${key} 2>&1)" fi done fi loginfo "currently loaded keys after action: $(ssh-runinagent $agentfile ssh-add -l|wc -l)" logdebug "$(ssh-runinagent $agentfile ssh-add -l)" else logwarning "ssh-identity $ssh_identity is not configured. Please create $identitydir and add keys" fi fi [ $DISPLAY_ORIG ] && logtrace "reset DISPLAY=$DISPLAY_ORIG" [ $DISPLAY_ORIG ] && export DISPLAY=$DISPLAY_ORIG EXIT } function check-pubkeysonly () { if [ ${#pubkeysonly[*]} -gt 0 ] ; then for p in ${pubkeysonly[@]}; do ssh-runinagent $agentfile ssh-add -T ${p} 2>&1 || { $remove || readd=true; break; } done else logwarning "obviously there is no pubkey for the token in ${SSH_IDENTITIES_DIR}/${ssh_identity}/" logwarning "you can add the pubkey with" logwarning " ssh-add -L > ${SSH_IDENTITIES_DIR}/${ssh_identity}/id_etoken.pub" logwarning "make sure, only the token is loaded into ssh-agent with" logwarning " ssh-add -l" logwarning "only one line should be the output" fi logdebug "readd: $readd" } ssh-runinagent () { ENTRY local agentfile local command local agentfile=${1} shift local sshcommand=${@} logtrace "run command »$sshcommand« in agent $agentfile" if [ -e "$agentfile" ]; then /bin/sh -c "unset SSH_AUTH_SOCK SSH_AGENT_PID; . $agentfile >/dev/null 2>/dev/null; $sshcommand" ret=$? else logwarning "agentfile not existent" ret=99 fi EXIT return $ret } if [[ $SSH_TTY || $X2GO_SESSION ]] ; then logdebug "run with forwarded ssh-agent" check_token res=1 else logdebug "run with local ssh-agent" agent-start-or-restart $ssh_identity ! $createonly && agent-load-identity-keys $ssh_identity res=0 fi SCRIPTEXIT exit $res