View Single Post
  #1   (View Single Post)  
Old 15th September 2015
gso gso is offline
Port Guard
 
Join Date: Nov 2014
Posts: 35
Default rc script to start and stop a local ssh socks connection

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

Last edited by gso; 10th October 2015 at 04:25 PM. Reason: minor bug fix
Reply With Quote