xiringuito/xiringuito
2017-05-31 18:45:05 +02:00

222 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Obscene SSH-based VPN for poors ;)
#
set -e
function print_help() {
echo "Usage: ${0} [OPTIONS] [SSH_USER@]SSH_SERVER [NETWORK1, NETWORK2, ... NETWORKx]"
echo
echo "OPTIONS"
echo "-k PRIVATE_KEY_PATH use specific SSH private key"
echo "-f MAX_FAILED_PINGS fail connection after X ping failures"
echo
echo "-C do not check if we run outdated app version"
echo "-R do not reconnect after connection failure"
echo "-D do not fetch DNS config from server"
echo "-X do not run route discovery"
echo "-h show this extremely helpful message"
echo
}
declare -r KERNEL=$(uname -s | tr [A-Z] [a-z])
if [[ ${KERNEL} != linux && ${KERNEL} != darwin ]]; then
echo "Unsupported system: ${KERNEL}"
exit 2
fi
if [[ ${KERNEL} == darwin ]]; then
if [[ ! $(ls -1 /dev/tun[0-9]) ]]; then
echo "MacOSX Virtual Network Interface not installed!"
echo "Get it here: http://tuntaposx.sourceforge.net/"
exit 3
fi
fi
if [[ ${#} -lt 1 ]]; then
print_help
exit 1
fi
# We need to save executable path and arguments for reconnection functionality
export PATH="${PATH}:."
declare -r ORIGINAL_EXEC=${0}
declare -r ORIGINAL_ARGS=${@}
while getopts "k:f:CRDXrh" o; do
case ${o} in
k)
PRIVATE_KEY_PATH=${OPTARG}
if [[ ! -f "${PRIVATE_KEY_PATH}" ]]; then
echo "Key file does not exist: ${PRIVATE_KEY_PATH}"
exit 1
fi
unset SSH_AUTH_SOCK
SSH_PRIVATE_KEY_OPTS="-i ${PRIVATE_KEY_PATH}"
;;
f)
MAX_FAILED_PINGS=${OPTARG}
if [[ ! ${MAX_FAILED_PINGS} =~ ^[0-9]{1,}$ ]]; then
echo "'-f' option accepts only positive integer values"
exit 1
fi
if [[ ${MAX_FAILED_PINGS} -eq 0 ]]; then
echo "'-f' option value should be greater then zero"
exit 1
fi
;;
C)
DONT_CHECK_IF_OUTDATED=true
;;
R)
NO_RECONNECT=true
;;
D)
NO_DNS=true
;;
X)
NO_ROUTE_DISCOVERY=true
;;
r)
RECONNECTING=true
;;
h)
print_help
exit 0
;;
*)
print_help
exit 1
esac
done
if [[ ! ${DONT_CHECK_IF_OUTDATED} ]]; then
./scripts/client-check-if-outdated.sh 2>/dev/null &
fi
declare -r MAX_FAILED_PINGS=${MAX_FAILED_PINGS-10}
shift $((OPTIND-1))
cd $(dirname ${0})
./scripts/client-preexec.sh
echo -n "[ sudo check ] "; sudo true; echo
declare -r SSH_SERVER=${1}; shift
if [[ ${#} -gt 0 ]]; then
declare -r NETWORKS=${@}
elif [[ -x ./discover-routes && ! ${NO_ROUTE_DISCOVERY} ]]; then
declare -r NETWORKS=$(./discover-routes ${SSH_SERVER} | grep "^ROUTE:" | sed 's/.*://')
fi
declare -r IP_BASE=192.168.245
declare -r TUNNEL_ID_PATH=~/.xiringuito/tunnel_id
declare -r TUNNEL_ID_FILE=${TUNNEL_ID_PATH}/${SSH_SERVER}
if [[ ! -f ${TUNNEL_ID_FILE} ]]; then
mkdir -p ${TUNNEL_ID_PATH}
let GENERATED_ID=${RANDOM}%50+1
echo ${GENERATED_ID} >${TUNNEL_ID_FILE}
fi
declare -r TUNNEL_ID=$(cat ${TUNNEL_ID_FILE})
declare -r REMOTE_PATH="/tmp/xiringuito.${TUNNEL_ID}"
declare -r SSH_OPTS="-oLogLevel=${SSH_LOG_LEVEL:-ERROR} -oConnectionAttempts=3 -oConnectTimeout=10 ${SSH_PRIVATE_KEY_OPTS} ${SSH_EXTRA_OPTS}"
if [[ ${KERNEL} == linux ]]; then
declare -r LOCAL_TUNNEL_ID=${TUNNEL_ID}
else
declare -r LOCAL_TUNNEL_ID=$(./scripts/${KERNEL}/get-local-tunnel-id.sh)
fi
trap 'exit 130' INT
trap teardown EXIT
function teardown() {
if [[ ! ${SSH_PID} ]]; then
sudo ./scripts/${KERNEL}/client-teardown.sh ${$} 0 ${LOCAL_TUNNEL_ID}
fi
}
echo "TUNNEL ID: ${TUNNEL_ID} (local: ${LOCAL_TUNNEL_ID})"
if [[ ${KERNEL} == linux ]]; then
./scripts/${KERNEL}/client-setup.sh ${TUNNEL_ID} ${IP_BASE}
fi
ssh ${SSH_OPTS} ${SSH_SERVER} mkdir -p ${REMOTE_PATH}
scp ${SSH_OPTS} ./scripts/server-*.sh ${SSH_SERVER}:${REMOTE_PATH} >/dev/null
ssh ${SSH_OPTS} ${SSH_SERVER} ${REMOTE_PATH}/server-setup.sh ${TUNNEL_ID} ${IP_BASE}
sleep 1; echo -n "SERVER: ${SSH_SERVER} ... "
set +e
ssh ${SSH_OPTS} ${SSH_SERVER} pkill -f ${REMOTE_PATH}/server-execute.sh &>/dev/null
set -e
if [[ ${KERNEL} == linux ]]; then
SSH_TUNNEL_CMD="ssh"
else
SSH_TUNNEL_CMD="sudo -E ssh"
fi
${SSH_TUNNEL_CMD} ${SSH_OPTS} -oStrictHostKeyChecking=no -w ${LOCAL_TUNNEL_ID}:${TUNNEL_ID} ${SSH_SERVER} ${REMOTE_PATH}/server-execute.sh ${TUNNEL_ID} ${IP_BASE} ${MAX_FAILED_PINGS} &
SSH_PID=${!}
sudo -E ./scripts/${KERNEL}/client-teardown.sh ${$} ${SSH_PID} ${LOCAL_TUNNEL_ID} &
sleep 5
if [[ ${KERNEL} == darwin ]]; then
./scripts/${KERNEL}/client-setup.sh ${TUNNEL_ID} ${LOCAL_TUNNEL_ID} ${IP_BASE}
fi
set +e
for NETWORK in ${NETWORKS}; do
echo "> ROUTE: ${NETWORK}"
[[ -z "${RECONNECTING}" ]] && ./scripts/${KERNEL}/client-route.sh ${LOCAL_TUNNEL_ID} ${NETWORK}
done
set -e
if [[ ! ${NO_DNS} && ! -z "${NETWORKS}" ]]; then
echo
echo "* Will now replace your DNS config with one fetched from the SSH server."
echo "* Set enviromental variable 'NO_DNS', if you do not want this to happen."
REMOTE_RESOLV_CONF=$(ssh ${SSH_OPTS} ${SSH_SERVER} cat /etc/resolv.conf | grep -v "[#;]" )
if [[ "${REMOTE_RESOLV_CONF}" =~ nameserver ]]; then
echo "${REMOTE_RESOLV_CONF}" | ./scripts/client-update-resolv-conf.sh
if [[ ${KERNEL} == darwin ]]; then
./scripts/${KERNEL}/client-update-macosx-dns.sh
fi
fi
fi
set +e
FAILED_PINGS=0
while [[ ${FAILED_PINGS} -lt ${MAX_FAILED_PINGS} ]]; do
[[ $(ps -p ${SSH_PID} | wc -l) -eq 2 ]] || break
if [[ ${EXIT_AFTER_CONNECT} ]]; then
exit 0
fi
./scripts/client-ping-server.sh ${TUNNEL_ID} ${IP_BASE}
if [[ ${?} -ne 0 ]]; then
let FAILED_PINGS+=1
echo "* Failed to ping server-side tunnel endpoint... (${FAILED_PINGS}/${MAX_FAILED_PINGS})"
else
FAILED_PINGS=0
fi
sleep 1
done
teardown
if [[ -z "${NO_RECONNECT}" ]]; then
exec ${ORIGINAL_EXEC} -r ${ORIGINAL_ARGS}
fi