A Full ppp-ssh VPN script
Author
Discussion
In ../ppp-ssh VPN, toward the bottom, I note that the one liner featured there,
pppd nodetach 10.0.3.1:10.0.3.2 pty "ssh -l root remotehost pppd notty"
would typically be embedded in a much larger script.
This article presents my best shot at that much larger script.
Full Text of the Script
#!/bin/bash
# A scenario involving one server and three clients.
#
# Note that:
# id_rsa1.pub is client1's root user's public ssh key
# id_rsa2.pub is client2's root user's public ssh key
# id_rsa3.pub is client3's root user's public ssh key
# on server 10.10.0.1:
# vlan.sh install vlanuser vlangroup id_rsa1.pub vlan1 10.0.1.1 10.0.1.2
# vlan.sh install vlanuser vlangroup id_rsa2.pub vlan2 10.0.2.1 10.0.2.2
# vlan.sh install vlanuser vlangroup id_rsa3.pub vlan3 10.0.3.1 10.0.3.2
# to connect from client1 10.10.0.2:
# vlan.sh client vlan1 vlanuser 10.10.0.1 10.0.1.1 10.0.1.2 10 10.0.0.0 255.255.0.0
# to connect from client2 10.10.0.3:
# vlan.sh client vlan2 vlanuser 10.10.0.1 10.0.1.1 10.0.1.2 10 10.0.0.0 255.255.0.0
# to connect from client3 10.10.0.4:
# vlan.sh client vlan3 vlanuser 10.10.0.1 10.0.1.1 10.0.1.2 10 10.0.0.0 255.255.0.0
#
PPPD=/usr/sbin/pppd
IFCONFIG=/sbin/ifconfig
VLAN=/usr/local/sbin/vlan.sh
config_file=/etc/vlan-server.conf
# time to sleep after starting pppd and before calling route
route_delay=10
function vlan_uninstall {
local vlanuser=$1
local vlangroup=$2
if [ -z "$2" ] ; then
echo "usage: <vlanuser> <vlangroup>"
exit 1
fi
hdir=/home/${vlanuser}
userdel $vlanuser
groupdel $vlangroup
if [ -d "$hdir" ] ; then
rm -rf $hdir
fi
rm $config_file
echo "done"
echo "you will have to remove the entry from sudoers yourself"
echo "it should look like this:"
echo "${vlanuser} ALL=NOPASSWD $VLAN server *"
}
function vlan_install {
local vlanuser=$1
local vlangroup=$2
local rsa_pub=$3
local name=$4
local server_ip=$5
local client_ip=$6
if [ -z "$5" ] ; then
echo "usage: <vlanuser> <vlangroup> <rsa public key file> <name> <server_ip> <client_ip>"
exit 1
fi
groupadd $vlangroup
mkdir /home/${vlanuser}
useradd $vlanuser
# you need to set the password string or the account is considered "locked"
usermod -p "X" -s /bin/bash -g $vlangroup $vlanuser
chown ${vlanuser}.${vlangroup} /home/${vlanuser}/
echo "${vlanuser} ALL=NOPASSWD: $VLAN server *" >> /etc/sudoers
mkdir /home/${vlanuser}/.ssh
cat $rsa_pub >> /home/${vlanuser}/.ssh/authorized_keys
chown ${vlanuser}.${vlangroup} /home/${vlanuser}/.ssh
chown ${vlanuser}.${vlangroup} /home/${vlanuser}/.ssh/authorized_keys
touch $config_file
echo "$name $client_ip $server_ip" >> $config_file
}
function test_ppp_address {
ipv4=$1
if [ -z "$1" ] ; then
echo "usgae: <ipv4 address>"
return 1
fi
if ! grep "ppp" /proc/net/dev > /dev/null 2>&1 ; then
echo "no ppp interfaces found"
return 1
fi
ppp_ifaces=$(grep "ppp" /proc/net/dev | cut -d: -f1)
for ppp_iface in $ppp_ifaces; do
ppp_address=$($IFCONFIG $ppp_iface | grep "inet addr" | cut -d: -f2 | cut -d" " -f1)
echo "$ppp_iface $ppp_address"
if [ "$ipv4" = "$ppp_address" ] ; then
return 0
fi
done
return 1
}
function get_ppp_pid {
ipv4=$1
if [ -z "$1" ] ; then
echo "usgae: <ipv4 address>" 1>&2
return 1
fi
if ! grep "ppp" /proc/net/dev > /dev/null 2>&1 ; then
echo "no ppp interfaces found" 1>&2
return 1
fi
ppp_ifaces=$(grep "ppp" /proc/net/dev | cut -d: -f1)
for ppp_iface in $ppp_ifaces; do
ppp_address=$($IFCONFIG $ppp_iface | grep "inet addr" | cut -d: -f2 | cut -d" " -f1)
if [ "$ipv4" = "$ppp_address" ] ; then
cat /var/run/${ppp_iface}.pid
return 0
fi
done
return 1
}
function get_ppp_pid2 {
while ! get_ppp_pid "$@" 2>/dev/null ; do
sleep 1
done
}
# why does killall pppd kill the vlan.sh processes that spawned the pppd?
# because nodetach keeps the pppd as part of the same threadgroup as vlan.sh which called it
# and when pppd exits it calls exit_group(), which kills the whole threadgroup.
# I need to get rid of the nodetach argument and figure out another way to capture the pid
# of the particular pppd spawned.
#
# one way might be to use /var/run/ppp<iface name>.pid file that pppd creates.
#
function vlan_client {
NAME=$1
REMOTE_USER=$2
REMOTE_HOST=$3
REMOTE_IP=$4
LOCAL_IP=$5
DELAY=$6
NETWORK=$7
NETMASK=$8
if [ -z "$8" ]; then
echo "usage: <name> <remote_user> <remote_host> <remote_virtual_ip> <local_virtual_ip> <delay> <network> <netmask>"
exit 1
fi
pppd_pid=""
$PPPD pty "ssh -o StrictHostKeyChecking\ no -l $REMOTE_USER ${REMOTE_HOST} \"sudo $VLAN server ${NAME}\""
pppd_pid=$(get_ppp_pid2 $LOCAL_IP)
sleep $route_delay
route add -net $NETWORK netmask $NETMASK gw $REMOTE_IP
sleep $DELAY
while true; do
if ! test_ppp_address $LOCAL_IP >/dev/null 2>&1 ; then
echo "$(date) ppp address failed. setting up ssh-ppp tunnel"
if ! [ -z "$pppd_pid" ] ; then
kill $pppd_pid
fi
$PPPD pty "ssh -o StrictHostKeyChecking\ no -l $REMOTE_USER ${REMOTE_HOST} \"sudo $VLAN server ${NAME}\""
pppd_pid=$(get_ppp_pid2 $LOCAL_IP)
sleep $route_delay
route add -net $NETWORK netmask $NETMASK gw $REMOTE_IP
sleep $DELAY
else
true
# echo "remote host is already up"
fi
sleep $DELAY
done
}
function vlan_sclient {
NAME=$1
REMOTE_USER=$2
REMOTE_HOST=$3
REMOTE_IP=$4
LOCAL_IP=$5
DELAY=$6
NETWORK=$7
NETMASK=$8
if [ -z "$8" ]; then
echo "usage: <name> <remote_user> <remote_host> <remote_virtual_ip> <local_virtual_ip> <delay> <network> <netmask>"
exit 1
fi
if ! test_ppp_address $LOCAL_IP >/dev/null 2>&1 ; then
$PPPD pty "ssh -o StrictHostKeyChecking\ no -l $REMOTE_USER ${REMOTE_HOST} \"sudo $VLAN server ${NAME}\""
sleep $route_delay
route add -net $NETWORK netmask $NETMASK gw $REMOTE_IP
else
echo "connection already up"
fi
}
function vlan_server {
vlan_name="$1"
if [ -z "$1" ] ; then
echo "vlan-client <vlan_name>"
exit 1
fi
if ! grep $vlan_name $config_file > /dev/null 2>&1 ; then
echo "name not recognized"
exit 1
fi
# gather ip addresses from config file
tfile=$(mktemp)
grep $vlan_name $config_file > $tfile
read name ip_client ip_server < $tfile
rm $tfile
$PPPD notty nodetach ${ip_server}:${ip_client}
}
command=$1
if [ -z "$1" ] ; then
echo "usage: <command>"
echo " install"
echo " uninstall"
echo " sclient"
echo " client"
echo " server"
exit 1
fi
shift
if [ "$command" == "install" ] ; then
vlan_install "$@"
elif [ "$command" == "uninstall" ] ; then
vlan_uninstall "$@"
elif [ "$command" == "sclient" ] ; then
vlan_sclient "$@"
elif [ "$command" == "client" ] ; then
vlan_client "$@"
elif [ "$command" == "server" ] ; then
vlan_server "$@"
elif [ "$command" == "getpid" ] ; then
get_ppp_pid "$@"
fi
