#!/bin/bash FILELOGLEVEL=DEBUG DEFAULT_SOCKET_REMOTE=~/.ssh/ssh_from_remote_auth_sock . $(dirname $0)/../logging # loggerfactory LANG=C usage(){ cat << EOF Usage: $(basename $0) [[-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 } create_filenamebase() { printf "%s" "${ssh_identity}-$(hostname)" } create_agentfilename() { printf "%s" "${SSH_AGENTS_DIR}/agent-$(create_filenamebase)" } create_socketfilename() { printf "%s" "${SSH_AGENT_SOCKETS_DIR}/socket-$(create_filenamebase)" } ssh_runinagent() { local SSH_AUTH_SOCK local SSH_AGENT_PID 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 return $ret } set_and_load_identity_config() { # check if idendity directory an file config exists in it. # if it exist, source it ssh_identity="${1}" ssh_identity_dir="${SSH_IDENTITIES_DIR}/${ssh_identity}" loginfo "ssh-identity: ${ssh_identity_dir}" if [ -d ${ssh_identity_dir} ]; then loginfo "Directory identity-dir ${ssh_identity_dir} exists. Use it" else logwarning "Directory identity-dir ${ssh_identity_dir} does not exist. Use default-config" ssh_identity=default ssh_identity_dir="${SSH_IDENTITIES_DIR}/${ssh_identity}" fi if [ -e "${ssh_identity_dir}/config" ]; then logdebug "source ${ssh_identity_dir}/config" unset SSH_AUTH_SOCK SSH_AGENT_PID PKCS11_MODULE SSH_AGENT_ALLOW_FROM_REMOTE SSH_ADD_OPTION . "${ssh_identity_dir}/config" fi } start_or_restart_local_agent() { if [ -e "${ssh_agentfile}" ] then logdebug "agentfile ${ssh_agentfile} exists" eval $(<$ssh_agentfile) >/dev/null 2>&1 logdebug "SSH_AUTH_SOCK: ${SSH_AUTH_SOCK:-not set}" logdebug "SSH_AGENT_PID: ${SSH_AGENT_PID:-not set}" logdebug "PKCS11_MODULE: ${PKCS11_MODULE:-not set}" for i in $(pgrep -f ${SSH_AUTH_SOCK}) do logdebug "found pid: $i" [ -n "${SSH_AGENT_PID:+x}" ] \ && [ $i -eq ${SSH_AGENT_PID} ] \ || { logwarning "kill unused ssh-agent with pid $i"; kill $i; } done case $(pgrep -f ${SSH_AUTH_SOCK}|wc -l) in 0) logdebug "no ssh-agents for file ${ssh_socketfile}" ret=2 ;; 1) logdebug "one running agent for file ${ssh_socketfile}. Use it" msg="$(ssh_runinagent $ssh_agentfile "ssh-add -l 2>&1")" ret=$? ;; *) logdebug "more than one ssh-agents for file ${ssh_socketfile}" return 3 ;; esac logdebug "ret: $ret" #msg="$(ssh-add -l 2>&1)" logtrace "Output from check for running agent: $msg" case $ret in 0) logdebug "agent is running" ;; 1) logdebug "agent is running, but:" logwarning "$msg" ;; 2|99) logdebug "former agent is not running -> start it" logdebug "remove socketfile: $( rm -v -f "$ssh_socketfile" )" logdebug "remove agentfile: $( rm -v -f "$ssh_agentfile" )" ;; esac else logdebug "ssh_agentfile ${ssh_agentfile} does not exist" fi logdebug "SSH_AGENT_OPTIONS: $SSH_AGENT_OPTIONS" logtrace "$(ssh-agent -a $ssh_socketfile ${SSH_AGENT_OPTIONS} > $ssh_agentfile )" logdebug "PKCS11_MODULE: ${PKCS11_MODULE:-not set}" sed '/^PKCS11_MODULE/d' ${ssh_agentfile} [ -n "${PKCS11_MODULE:+x}" ] && logdebug "add PKCS11_MODULE to ${ssh_agentfile}" [ -n "${PKCS11_MODULE:+x}" ] && echo "PKCS11_MODULE=$PKCS11_MODULE; export PKCS11_MODULE" >> ${ssh_agentfile} logdebug "agent started" } check_and_link_socket() { if ${SSH_AGENT_ALLOW_FROM_REMOTE:-false} then logdebug "Remote agent allowed." if [ -e ${DEFAULT_SOCKET_REMOTE} ] then logdebug "Link remote socket to ${ssh_socketfile}" ln -f -s $(readlink -f ${DEFAULT_SOCKET_REMOTE}) ${ssh_socketfile} [ -e ~/.ssh/p11m ] && . ~/.ssh/p11m logdebug "Write new agentfile ${ssh_agentfile}" cat << EOF > ${ssh_agentfile} SSH_AUTH_SOCK=$ssh_socketfile; export SSH_AUTH_SOCK; ${P11M:+PKCS11_MODULE=${P11M}; export PKCS11_MODULE} EOF else logdebug "Use local agent" start_or_restart_local_agent fi else logdebug "Only local agent allowed" if [ -L ${ssh_socketfile} ] then logdebug "${ssh_socketfile} is symlinked to $(readlink -f ${ssh_socketfile}). Remove it" rm ${ssh_socketfile} else if [ -e ${ssh_socketfile} -a $(pgrep -f ${ssh_socketfile} >/dev/null 2>&2; echo $?) ] then logdebug "Socket and agent for ${ssh_socketfile} are up and a running agent exist. Use it" else logdebug "No agent for socket ${ssh_socketfile} or socket does not exist, remove both if possible" rm -f ${ssh_socketfile} pkill -f ${ssh_socketfile} fi fi start_or_restart_local_agent fi } load_or_reload_identities() { logdebug "Now load all identities for $ssh_identitiy from $ssh_identity_dir" logdebug "PKCS11_MODULE: ${PKCS11_MODULE:-not set}" } ################################################## SCRIPTENTRY createonly=false tokenonly=false readd=false keyonly=false remove=false while :; do case $1 in -c|--create-only) createonly=true shift ;; -h|--info) usage exit 0 ;; -*) echo "Unknown urgument: »$1«" >&2 exit 1 ;; *) #ssh_identity=${1-${SSH_DEFAULT_IDENTITY}} ssh_identity=${1} break ;; esac done [ -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" set_and_load_identity_config ${1} loginfo " SSH_ADD_OPTIONS=${SSH_ADD_OPTIONS:-not set}" loginfo "SSH_AGENT_ALLOW_FROM_REMOTE=${SSH_AGENT_ALLOW_FROM_REMOTE:-not set}" loginfo " PKCS11_MODULE=${PKCS11_MODULE:-not set}" loginfo " P11M=${P11M:-not set}" loginfo " SSH_AUTH_SOCK=${SSH_AUTH_SOCK:-not set}" loginfo " SSH_AUTH_SOCK_REMOTE=${SSH_AUTH_SOCK_REMOTE:-not set}" ssh_agentfile=$(create_agentfilename) ssh_socketfile=$(create_socketfilename) logdebug "AGENT: ${ssh_agentfile}" logdebug "SOCKET: ${ssh_socketfile}" [ -e $ssh_agentfile ] && eval $(<$ssh_agentfile) >&2 check_and_link_socket load_or_reload_identities $createonly && printf "%s" "${ssh_agentfile}" logdebug "cat ${ssh_agentfile} $(cat ${ssh_agentfile})" SCRIPTEXIT loginfo "return with $res" exit $res