#!/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 PKCS11_MODULE local agentfile local command local agentfile=${1} shift local sshcommand=${@} logdebug "agentfile: ${agentfile}" logtrace "run command »$sshcommand« in agent $agentfile" if [ -e "$agentfile" ]; then #/bin/sh -c "unset SSH_AUTH_SOCK SSH_AGENT_PID PKCS11_MODULE; eval $(<$agentfile) >/dev/null 2>&1; echo SSH_AUTH_SOCK ${SSH_AUTH_SOCK:-not set} >&2; $sshcommand" #unset SSH_AUTH_SOCK SSH_AGENT_PID PKCS11_MODULE . $agentfile >&2 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}" $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}" loginfo "ssh-identity_dir: ${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 "$(stat ${SSH_AUTH_SOCK})" logdebug "found pid: $i" logdebug "is SSH_AGENT_PID set?" [ -n "${SSH_AGENT_PID:+x}" ] logdebug "is $SSH_AGENT_PID same as found pid $i:" [ $i -eq ${SSH_AGENT_PID} ] ${REMOTE_UNUSED_AGENTS:-false} && { [ -n "${SSH_AGENT_PID:+x}" ] && [ $i -eq ${SSH_AGENT_PID} ] || { logwarning "kill unused ssh-agent with pid $i"; kill $i; }; } done if [ -e ${ssh_socketfile} ] then case $(pgrep -f ${SSH_AUTH_SOCK}|wc -l) in 0) logdebug "no ssh-agents for file ${ssh_socketfile}" ret=3 ;; 1) logdebug "one running agent for file ${ssh_socketfile}. Use it" msg="$(ssh_runinagent $ssh_agentfile "ssh-add -v -l 2>&1")" ret=$? logdebug "SSH_AUTH_SOCK: $SSH_AUTH_SOCK" ;; *) logdebug "more than one ssh-agents for file ${ssh_socketfile}" return 3 ;; esac else logdebug "remove socketfile: $( rm -v -f "$ssh_socketfile" )" msg="$(ssh_runinagent $ssh_agentfile "ssh-add -v -l 2>&1")" ret=$? fi 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|3|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" )" #logdebug "$(ssh-agent -k)" 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" ;; 4) logdebug "this is strange" ;; esac else logdebug "ssh_agentfile ${ssh_agentfile} does not exist" logdebug "remove socketfile: $( rm -v -f "$ssh_socketfile" )" logdebug "remove agentfile: $( rm -v -f "$ssh_agentfile" )" 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" fi } 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 [ -e ${ssh_agentfile} -a $(grep SSH_AUTH_SOCK ${ssh_agentfile}|echo $?) ] then logdebug "agentfile exists" else logdebug "agentfile is missing -> kill all ssh-agents for socket" pkill -f ${ssh_socketfile} fi 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 } check_pubkeys_and_certs() { 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 logdebug "pubkey with privkey: ${pubkey} | ${pubkey%.pub}" else logdebug "pubkey without privkey: $pubkey" pubkeysonly+=($pubkey) fi if [ -e "${pubkey%.pub}-cert.pub" ]; then cert_exp_date=$(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}') [ $(date +%s -d $cert_exp_date) -gt $(date +%s -d NOW) ] \ || logwarning "CERTIFICATE IS NOT VALID ANYMORE: ${pubkey%.pub}-cert.pub" [ $(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 logwarning "$(ssh-add -T ${pubkey})" done } load_or_reload_identities() { logdebug " ssh_idendity=${ssh_identity}" logdebug "Now load all identities for ${ssh_identity} from ${ssh_identity_dir}" logdebug "PKCS11_MODULE: ${PKCS11_MODULE:-not set}" local fingerprint declare -a fingerprints local pubkeysonly declare -a pubkeysonly fingerprints=( $(ssh-add -l|awk '{print $2}') ) logdebug "fingerprints from loaded keys before action:" for f in ${fingerprints[@]};do logdebug "$f" done 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" else logdebug "key: $key is not loaded -> load it" logdebug "$(ssh-add ${SSH_ADD_OPTIONS} ${key} 2>&1)" fi done 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 logdebug "pubkey with privkey: ${pubkey} | ${pubkey%.pub}" else logdebug "pubkey without privkey: $pubkey" pubkeysonly+=($pubkey) fi if [ -e "${pubkey%.pub}-cert.pub" ]; then cert_exp_date=$(ssh-keygen -L -f "${pubkey%.pub}-cert.pub"|awk '$1 == "Valid:"{print $5}') [ $(date +%s -d $cert_exp_date) -gt $(date +%s -d NOW) ] \ || logwarning "CERTIFICATE IS NOT VALID ANYMORE: ${pubkey%.pub}-cert.pub" [ $(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 for pubkey in ${pubkeysonly[@]} do logdebug "Pubkey: ${pubkey}" logdebug " SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" logwarning "$(ssh-add -T ${pubkey} 2>&1)" if [ $? -gt 0 ] then logdebug "Pubkey ${pubkey} is not usable" if [ -n "${PKCS11_MODULE:+x}" ] then logdebug "PKCS11_MODULE is set ${PKCS11_MODULE:-not set}" logdebug "$(ssh-add -e $PKCS11_MODULE)" && logdebug "$(ssh-add -s $PKCS11_MODULE)" else logdebug "No PKCS11_MODULE defined" fi else logdebug "Pubkey ${pubkey} is usable" if [ -n "${PKCS11_MODULE:+x}" ] then logdebug "PKCS11_MODULE is set ${PKCS11_MODULE:-not set}" else logdebug "No PKCS11_MODULE defined" fi fi done } ################################################## 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" logdebug "ssh_idendity: ${ssh_identity}" unset SSH_AUTH_SOCK SSH_AGENT_PID PKCS11_MODULE SSH_AGENT_ALLOW_FROM_REMOTE set_and_load_identity_config ${ssh_identity} logdebug " SSH_ADD_OPTIONS=${SSH_ADD_OPTIONS:-not set}" logdebug "SSH_AGENT_ALLOW_FROM_REMOTE=${SSH_AGENT_ALLOW_FROM_REMOTE:-false}" logdebug " PKCS11_MODULE=${PKCS11_MODULE:-not set}" logdebug " P11M=${P11M:-not set}" logdebug " SSH_AUTH_SOCK=${SSH_AUTH_SOCK:-not set}" logdebug " SSH_AUTH_SOCK_REMOTE=${SSH_AUTH_SOCK_REMOTE:-not set}" logdebug " ssh_idendity=${ssh_identity}" ssh_agentfile=$(create_agentfilename) ssh_socketfile=$(create_socketfilename) [ -e $ssh_agentfile ] && eval $(<$ssh_agentfile) >&2 check_and_link_socket logdebug "SSH_AGENT_FILE=${ssh_agentfile}" logdebug " SSH_AGENT_PID=${SSH_AGENT_PID}" logdebug " SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" logdebug " ssh_idendity=${ssh_identity}" $createonly || load_or_reload_identities $createonly && printf "%s" "${ssh_agentfile}" logtrace "cat ${ssh_agentfile} $(cat ${ssh_agentfile})" SCRIPTEXIT loginfo "return with $res" exit $res