rc script to start & stop a local ssh SOCKS server, following on from a thread discussing
how to obtain the PID of a ssh process.
[Edit 16/09/2015] Re-uploaded, somewhat longer version, but should behave more reliably as a rc script.
[Edit 16/09/2015] My own conclusion at this point at least I think is assuming the control socket can be closed after the connection has been made, it might be preferable to obtain the PID with a control socket
rather than, e.g., 'pgrep -P $$ -x ssh'. The parent process can still be double checked, e.g., 'ps -o ppid= -p PID', likewise a process timestamp cached ('ps -o lstart= -p PID ').
[Edit 16/09/2015] The freshly uploaded version of the script implements the latter (i.e., control socket approach), however with the caveat that there is an issue requesting "the master to stop accepting further multiplexing requests" (i.e., the control socket itself is not closed after making the connection and reading the PID; take a look at the commented code in the 'sshsocks_start' function if anyone thinks they may be able to sort the problem out). Tested on a FreeBSD 10.2 install.
Code:
cat >/usr/local/rc.conf.d/sshsocks <<EOT
sshsocks_user=...
sshsocks_server=...
sshsocks_fwd_addr=... # default localhost
sshsocks_fwd_port=... # default 1080
EOT
Code:
#!/bin/sh
#
# PROVIDE: sshsocks socksproxy
# REQUIRE: NETWORK_UP_PLACEHOLDER
# KEYWORD: nostart
name="sshsocks"
. /etc/rc.subr
#set -x
# ssh control socket
test -d "${HOME}"/.ssh || { echo "Script requires a configured ~/.ssh directory to run."; exit 1; } #todo check perms
control_path="${HOME}"/.ssh/controlmasters
test -d "${control_path}" || mkdir "${control_path}"
# status command PID file
test -d "/var/run/${name}" || mkdir "/var/run/${name}"
pidfile="/var/run/${name}/${name}.pid"
restart_cmd="${name}_restart"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
extra_commands="check status"
check_cmd="${name}_check"
status_cmd="${name}_status"
sshsocks_start()
{
echo "Starting ssh SOCKS proxy..." >/dev/stderr
sshsocks_check_running
if [ $? -eq 0 ]
then
echo "Unable to start ssh, process already running" >/dev/stderr
exit 1
else
# Connect to server
ssh -CN -f -o "ExitOnForwardFailure=yes" -o "ControlMaster=yes" -o "ControlPath=${control_path}/%r@%h:%p" "${sshsocks_user}@${sshsocks_server}"
# Read process PID
ssh -O check -S "${control_path}/${sshsocks_user}@%h:%p" "$sshsocks_server" >"$pidfile".check.out 2>&1
if [ $? -ne 0 ]
then
echo "Error starting the ssh process" >/dev/stderr
cat "$pidfile".check.out # any error messages will be here
sshsocks_cleanup
#todo check if ssh process has been left running?
exit 1
else
cat "$pidfile".check.out | sed -n '1s/^.*(pid=\([0-9]*\)).*$/\1/p' >"$pidfile"
#todo check a valid PID is actually returned
rm "$pidfile".check.out
ps -p "$(cat "$pidfile")" -o lstart='' >"$pidfile".timestamp
fi
# Start forwarding
ssh -O forward -D "${sshsocks_fwd_addr}:${sshsocks_fwd_port}" -S "${control_path}/${sshsocks_user}@%h:%p" "$sshsocks_server"
if [ $? -ne 0 ]
then
echo "Error starting ssh forwarding" >/dev/stderr
sshsocks_stop # close the connection with kill
exit 1
fi
# Close control socket
#ssh -O stop -S "${control_path}/${sshsocks_user}@%h:%p" "$sshsocks_server" #>/dev/null 2>&1
#if [ $? -ne 0 ]
#then
# echo "Error closing the control socket" >/dev/stderr
# sshsocks_stop # close the connection with kill if open
# exit 1
#fi
fi
}
sshsocks_restart()
{
sshsocks_stop
sshsocks_start
}
sshsocks_stop()
{
echo "Stopping ssh SOCKS proxy..." >/dev/stderr
sshsocks_check_running
if [ $? -ne 0 ]
then
echo "Unable to stop ssh, process is not running" >/dev/stderr
exit 1
else
kill "$(cat "$pidfile")"
if [ $? -ne 0 ]
then
echo "Error attempting to kill ssh process" >/dev/stderr
exit 1
fi
rm "$pidfile"
rm "$pidfile".timestamp
fi
}
sshsocks_status()
{
sshsocks_check_running
if [ $? -eq 0 ]
then
cat "$pidfile"
fi
}
sshsocks_check()
{
sshsocks_check_running
if [ $? -ne 0 ]
then
exit 1
else
exit 0
fi
}
sshsocks_check_running()
{
if [ ! -f "$pidfile" -a ! -f "${pidfile}.timestamp" ]
then
return 1 ## SOCKS server not yet started
elif [ ! -f "$pidfile" -o ! -f "${pidfile}.timestamp" ]
then
sshsocks_cleanup
return 1 ## system crash of some sort
else
ps -p "$(cat "$pidfile")" >/dev/null 2>&1
if [ $? -ne 0 ]
then
sshsocks_cleanup
return 1 ## PID no longer exists
fi
ps -o lstart= -p "$(cat "$pidfile")" >"$pidfile".timestamp.cmp
cmp "$pidfile".timestamp "$pidfile".timestamp.cmp
if [ $? -ne 0 ]
then
sshsocks_cleanup
return 1 ## a process with the same PID but not ours
else
return 0 ## our ssh process is still up and running
fi
fi
}
sshsocks_cleanup()
{
echo "Cleaning up after system failure..."
test ! -f "$pidfile" || rm "$pidfile"
test ! -f "$pidfile".check.out || rm "$pidfile".check.out
test ! -f "${pidfile}.timestamp" || rm "$pidfile".timestamp
test ! -f "${pidfile}.timestamp.cmp" || rm "$pidfile".timestamp.cmp
}
load_rc_config $name
test -n "$sshsocks_user" || { echo "ssh user not configured"; exit 1; }
test -n "$sshsocks_server" || { echo "ssh server not configured"; exit 1; }
: ${sshsocks_port:=22}
: ${sshsocks_fwd_addr:=localhost}
: ${sshsocks_fwd_port:=1080}
run_rc_command "$1"
exit 0