Friday, 25 September 2009

HOWTO: Setting up a vpn using ssh and pppd

For this we need two systems, one is designated as the server, the other the client. The server needs to have a static IP address or dynamic dns name from a free service such as Also, ensure that all firewalls are turned off or port 22 forwarding is enabled for both hosts.

Configuring the SSH accounts;

On the "server" machine;

Firstly, if you have not already installed ssh server do so now.
sudo apt-get install openssh-server

I use port 443 for VPN connections because this is usually the easiest port to get through a firewall that you don't control.

Edit your ssh server config;

sudo vi /etc/ssh/sshd_config

Change the line;

Port 22


Port 443

and restart your SSH server;

sudo /etc/init.d/ssh restart

Now, we create a user called "vpn";

sudo adduser --system --group vpn

The --system parameter sets vpn's shell to /bin/false but because the vpn user needs to log in via ssh, we must change this to /bin/bash in the /etc/passwd file.

sudo vi /etc/passwd

Here is an example;


The account password will only be used during this howto. You can choose a complex (secure) one now or a simpler temporary one and change it later.

Creating a password;

sudo passwd vpn

You should be able to login to the account from the client now;

ssh vpn@hostname

The next step is to create a ssh keypair for the root user on the client machine and place that public key in the vpn users authorized_keys file. Use this guide to configure passwordless ssh but remember to use the vpn user on the server instead of the root user as is shown in that guide.

Once you have passwordless SSH properly configured between root@client and vpn@server you should change the password to a more secure (random) one if you haven't already done so it will no longer be needed.

Time to set up the actual VPN.

Configuring the VPN;

The pppd daemon we will use needs to run as root, but we don't want to give our vpn user complete access to the system. To do that we configure sudo to give minimal access rights.

On the Server, open the visudo editor


Add these three lines to the end of the file

vpn ALL=NOPASSWD: /usr/sbin/pppd
vpn ALL=NOPASSWD: /sbin/iptables
vpn ALL=NOPASSWD: /sbin/route

This allows our vpn user to execute the pppd command to start the vpn and use the "route" command to set the return routes (if required).
If you are setting up a router<->router connection you will need to set the appropriate return routes to the client on the server.

To do this, create a script in the vpn user directory on the server.

vi /home/vpn/

Place the appropriate route commands to the subnet(s) at the clients end. If you don't want return routes then just don't enter any route commands. Here is mine;

sudo route add -net gw
sudo route add -net gw

This script must be executable

chmod +x /home/vpn/

and owned by the vpn user

chown vpn:vpn /home/vpn/

We can also check that the pppd permissions are set up properly by logging in as the vpn user and issuing this command;

sudo /usr/sbin/pppd noauth

You should see a bunch of hieroglyphics such as this.

~�}#�!}!}!} }4}"}&} } } } }%}&����}'}"}(}"��~

You can kill the process from another terminal or just wait 30 secs or so for it to finish on its own.

Now we can configure the client (logged in as root)
Firstly, we need to use a script to connect to the server. You can locate the script anywhere you like, I put it in /usr/local/bin

You can download a copy of the connect script or simply
copy and paste this text into a file

# SCRIPT: vpn-connect version 2.2
# LOCATION: /usr/local/bin/vpn-connect
# DESCRIPTION: This script initiates a ppp-ssh vpn connection.
# see the VPN PPP-SSH HOWTO on
# for more information.
# NOTES: This script uses port 443 so your VPN server should be
# configured to listen for ssh on Port 443
# revision history:
# 1.6 11-Nov-1996
# 1.7 20-Dec-1999
# 2.0 16-May-2001
# 2.2 27-Sep-2009
# You will need to change these variables...
# The host name or IP address of the SSH server that we are
# sending the connection request to:

# The username on the VPN server that will run the tunnel.
# For security reasons, this should NOT be root. (Any user
# that can use PPP can intitiate the connection on the client)

# The VPN network interface on the server should use this address:

# ...and on the client, this address:

# This tells ssh to use unprivileged high ports, even though it's
# running as root. This way, you don't have to punch custom holes
# through your firewall.


## required commands...


if ! test -f $PPPD ; then echo "can't find $PPPD"; exit 3; fi
if ! test -f $SSH ; then echo "can't find $SSH"; exit 4; fi

case "$1" in
# echo -n "Starting vpn to $SERVER_HOSTNAME: "
${PPPD} updetach noauth passive pty "${SSH} -p 443 ${LOCAL_SSH_OPTS} ${SERVER_HOSTNAME} -l${SERVER_USERNAME} -o Batchmode=yes sudo ${PPPD} nodetach notty noauth" ipparam vpn ${CLIENT_IFIPADDR}:${SERVER_IFIPADDR}
route add -net netmask gw $SERVER_IFIPADDR
# route add -net netmask gw $SERVER_IFIPADDR
# route add -net gw $SERVER_IFIPADDR

# echo -n "Stopping vpn to $SERVER_HOSTNAME: "
PID=`ps ax | grep "${SSH} -p 443 ${LOCAL_SSH_OPTS} ${SERVER_HOSTNAME} -l${SERVER_USERNAME} -o" | grep -v ' passive ' | grep -v 'grep ' | awk '{print $1}'`
if [ "${PID}" != "" ]; then
kill $PID
echo "disconnected."
echo "Failed to find PID for the connection"


echo "Usage: vpn {start|stop|config}"
exit 1
exit 0

You need to change the SERVER_HOSTNAME variable in the above script. You may also need to change SERVER_IFIPADDR and CLIENT_IFIPADDR depending on your existing network landscape.

Now we need to make the script executable

chmod +x /usr/local/bin/vpn-client

To start the vpn, at the client type

/usr/local/sbin/vpn-client start

You can check if it is up using the "ifconfig" command

ifconfig ppp0

Note: if you already have a ppp connection, such as to your ISP, then you may need to do "ifconfig ppp1". To see all your current ppp connections enter

ifconfig | grep ppp

If you want the vpn connection to be permanently up you can create a script to check the status and restart it if required.

vi /usr/local/sbin/vpn-check



if [ "$(/bin/pidof $DAEMON)" = "" ]; then
/usr/local/sbin/vpn-client start
if ! [ "$(/bin/pidof $DAEMON)" = "" ]; then
echo "VPN restarted $(date +%m-%d-%Y)"

Now, add an entry to the system crontab to run the script every minute

vi /etc/crontab

Add this line

* * * * * root /usr/local/sbin/vpn-check

Cron will automatically restart so we don't need to do that.

Now, assuming all has gone well if you issue the command

/usr/local/sbin/vpn-client stop

and wait for about a minute the vpn client should automatically reconnect!



administrator said...

dydns is not reliable.

(VPN Server)

Brett said...

I don't know about that. I've been using it (dyndns) daily for over a year and have yet to have an issue.