#!/bin/sh -e export PATH="/usr/local/bin:/usr/bin:/bin" config="${HOME}/.ssh/callhome.config" key="${HOME}/.ssh/callhome.key" lock="${HOME}/.ssh/callhome.pid" local_port="22222" timeout="60" ssh_config() { cat <<EOCF # No Agent or X11 forwards needed ForwardAgent no ForwardX11 no ForwardX11Trusted no # Timeout after a sensible amount of time # Hosts with volatile uplinks need this to know when to relink ConnectTimeout 30 ServerAliveInterval 20 ServerAliveCountMax 3 # This profile is used to transmit the newly generated key and port config Host callhome-setup HostName alba.cyberleo.net Port 22 User cyberleo # This profile is used to connect Host callhome HostName alba.cyberleo.net Port 22 User callhome IdentityFile ${key} # Do not use SSH-Agent identities IdentitiesOnly yes # Do not prompt for passwords PreferredAuthentications publickey EOCF } setup_config() { mkdir -p "$(dirname "${config}")" ssh_config > "${config}" } callhome_ssh() { ssh -F "${config}" "${@}" } setup_key() { echo "Generating remote-access SSH key..." >&2 mkdir -p "$(dirname "${key}")" ssh-keygen -q -b 3072 -P "" -f "${key}" echo "" >&2 echo "Transmitting public key; please log in." >&2 echo "" >&2 callhome_ssh callhome-setup "/bin/mkdir -p ~/.ssh && /bin/cat >> ~/.ssh/pending_authorized_keys" < "${key}.pub" || { echo "Key transmission failed. Check for any errors and correct them, then try again." >&2 echo "----8<----" >&2 cat "${key}.pub" echo "----8<----" >&2 return 1 } echo "Key transmitted. You can configure this key on the server now." >&2 return 0 } regexpize_allowed_symbols() { echo "${*}" | sed -Ee ' s/^[[:space:]]*/^\\(/; s/[[:space:]]*$/\\):/; s/[[:space:]]+/\\|/g; s/$/ / ' } remote_config() { allowed_symbols="ident port" allowed_symbols_regexp="$(regexpize_allowed_symbols "${allowed_symbols}")" while ! callhome_ssh callhome config do sleep 1 done | while read line do echo "${line}" | grep "${allowed_symbols_regexp}" || echo "Ignoring invalid config line: ${line}" >&2 done | sed -e "s/: /='/; s/$/'/" } remote_kill() { callhome_ssh callhome kill } remote_loop() { callhome_ssh -fn -o ExitOnForwardFailure=yes -L${local_port}:127.0.0.1:${port} -R${port}:127.0.0.1:22 callhome loop } case "$(uname -s)" in FreeBSD) # FreeBSD version ssh_pid() { sockstat -P tcp -lp "${local_port}" | tail -n +2 | while read user command pid fd proto local foreign do [ "${user}" = "${USER}" -a "${command}" = "ssh" ] || continue echo "${pid}" done | sort -u } ;; Linux) # Linux version ssh_pid() { netstat --numeric-hosts --numeric-ports -eplt 2>&- | awk 'BEGIN{out=""}{if(out){print}}/^Proto/{out="yes"}' | while read proto recvq sendq local foreign state user inode pidcmd do pid="${pidcmd%%/*}" command="${pidcmd##*/}" [ "${user}" = "${USER}" -a "${command}" = "ssh" ] || continue echo "${pid}" done | sort -u } ;; Darwin) # Darwin version ssh_pid() { me=$(id -u) /usr/sbin/lsof -i -l -n -P | grep '(LISTEN)' | grep " ${me} " | grep 'ssh' | grep ":${local_port} " | while read command pid user fd type device size node name state do [ "${user}" = "${me}" -a "${command}" = "ssh" ] || continue echo "${pid}" done | sort -u } ;; *) echo "Unsupported OStype $(uname -s)" >&2; exit 1 ;; esac ssh_up() { [ "$(ssh_pid)" ] } local_kill() { pids="$(ssh_pid)" [ -z "${pids}" ] || kill -TERM ${pids} } checkup() { nc -w 20 127.0.0.1 "${local_port}" < /dev/null | grep -q ^SSH } terminate_cleanup() { trap - EXIT HUP INT TERM KILL local_kill } trap "terminate_cleanup" EXIT HUP INT TERM KILL [ -f "${config}" -a -s "${config}" ] || setup_config [ -f "${key}" ] || { setup_key; exit; } eval $(remote_config) [ "${ident}" -a "${port}" ] || { echo "Configuration download failed" >&2 exit 1 } printf "Calling home: I am %s (port %s)\n" "${ident}" "${port}" while true do # Avoid looping more than once per second during quick failures [ "$(date +%s)" -ne "${lastloop:-0}" ] || sleep 1 lastloop="$(date +%s)" # Busy loop as long as everything is working # Check that ssh is running every 20 seconds # Poke the port every 60 seconds while checkup do for iter in 1 2 3 do ssh_up || break sleep 20 done done # If it's not working, try and make it work # Kill any local processes local_kill || true # Attempt to establish connection if ! remote_loop then # If it doesn't work, try to kill remote and fall through for another loop remote_kill || true fi done
Note: You are viewing an old version of this page. View the current version.