Differences between version 6 and predecessor to the previous major change of KnowledgeBase/callhome.sh.
Other diffs: Previous Revision, Previous Author
Newer page: | version 6 | Last edited on Monday, 23 March 2015 13:37:52 | by CyberLeo | Revert |
Older page: | version 5 | Last edited on Tuesday, 5 August 2014 23:03:20 | by CyberLeo | Revert |
@@ -194,8 +194,26 @@
remote_kill || true
fi
done
</code>
+
+Systemd callhome.service
+<code>
+[Unit]
+Description = CallHome script
+Requires = network.target
+
+[Service]
+Type = simple
+Environment=HOME=/root
+ExecStart = /root/bin/callhome
+Restart = always
+RestartSec = 5s
+
+[Install]
+WantedBy = multi-user.target
+</code>
+
----
This bit is FreeBSD-specific; but you can port it in much the same way as callhome.sh (see ssh_pid() ).<br>
Server component: force_cmd
<code brush="bash">
version 6
Client component: callhome.sh
#!/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
Systemd callhome.service
[Unit] Description = CallHome script Requires = network.target [Service] Type = simple Environment=HOME=/root ExecStart = /root/bin/callhome Restart = always RestartSec = 5s [Install] WantedBy = multi-user.target
This bit is FreeBSD-specific; but you can port it in much the same way as callhome.sh (see ssh_pid() ).
Server component: force_cmd
#!/bin/sh -e # Read in config ident="${1}" port="${2}" kill_current_mapping_for_port() { local port="${1}" sockstat -P tcp -lp "${port}" | tail -n +2 | while read user command pid fd proto local foreign do [ "${user}" = "${USER}" -a "${command}" = "sshd" ] || continue kill -TERM "${pid}" echo "Killed ${pid}: ${command}" >&2 done } logit() { echo "$(date): ${*}" >> "${HOME}/callhome.log" } logit "${ident}:${port} requested '${SSH_ORIGINAL_COMMAND}' from ${SSH_CLIENT}" case "$SSH_ORIGINAL_COMMAND" in config) echo "ident: ${ident}" echo "port: ${port}" ;; loop) echo "Keepalive loop started; break to terminate." exec sh -ec 'while sleep 300; do printf "."; done'" # ${ident} ${port}" ;; kill) kill_current_mapping_for_port "${port}" ;; ping) echo "pong" ;; *) echo "Rejected" exit 1 ;; esac
Place name and port number into force command to configure the client. Give each client its own distinct name and port number. Sample authorized_keys entry:
command="/usr/home/callhome/.ssh/force_cmd phoenix 22004" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJDg49aa0ObN2Xmi2CxkQO9bKNu6SR+n0zzbnl+6CayqIdsBhm56RVnOPr3bAAMj+36ygdjqp2CEeGS4g0S5B9FcGZkrWxyy9t/ixhOWo2qB+Yx55M0C8uB0ie1aN7UV+k8nBy8ahxQ7NVQ6uPdJ8F/XlhuYPMkJtuLGDv/GjDWhAvv11J2Ff9p3Wl1AIVkyvvIil9BaBBg+XmnwQAAdKf5rb7+ouG4tnQ3Mo2OEkj61B10vvDQNAD1VqGAm+xskjNyoM4ETS3ehA/j4yPaF+vr6fcLL2UhA2MJm3Y8QP3xq1JzTEnmBrJM0pxBsy0NvsKchSyyH3GmgzaB/rxn6uAnQn87nZXoL1V7wghr0HylA/Dj5xq+34zcnv8osmz7B63xFujRxZuxSFjcHr4k+PUx4vjhvIDT9oXeckif71QfTulsBxnMyL2W7alY2eku00ATLOhr+QBIeURzrCSYSXgEm5CRFJ/9T7EDYJPwx2MeD8NRKgdjTiWbLaPYylLLD0= cyberleo@phoenix-lan