Next Previous Contents

5. Implementation

In this section, I explain step by step how to set up your VPN system. I'll start with the server, and then move on to the client. For the purposes of an example, I will invent a situation that would require a couple of different kinds of VPN set up.

5.1 Planning

Let's imagine that we have a company, called At our head office, we are using the reserved network, breaking the class B into 256 class C networks to allow routing. We have just set up two small remote offices, and want to add them to our network. We also want to allow employees who work from home to be able to use their DSL and cable modem connections instead of making them use dialup. To start, we need to plan things out a little.

I decide that I want to give each remote office a class C network range to allow them to expand as necessary. So, I reserve the and nets. I also decide that for home users, I've got enough numbers that I don't need to masquerade them on the VPN server side. Each client gets it's own internal IP. So, I need to reserve another class C for that, say The only thing that I must now do is to add these ranges to my router. Let's imagine that our company owns a small Cisco ( that handles all of the traffic through our OC1. Just set routes on the Cisco such that traffic headed to these reserved nets goes to our VPN server ( I put the VPN server into the home user's net for reasons that should become clear later. We'll name the external interface of the server, and the internal

As for external numbers, we don't need to know them explicitly. You should have your own numbers, supplied by your ISP.

5.2 Gather the tools

we're going to need a few pieces of software. Get the following packages, and install them where specified.

For the Server:

For the Client:

5.3 Server: Build the kernel

To start, you'll probably need to rebuild your kernel for the server. You need to make sure that the following kernel options are turned on in addition to basic networking and everything else that you might need. If you've never built your own kernel before, read the Kernel HOWTO.

For 2.0 kernels:

For 2.2 kernels:

5.4 Server: Configure Networking

If you are building a server that has only one network card, I suggest that you think about buying another, and rewiring your network. The best way to keep your network private is to keep it on it's own wires. So if you do have two network cards, you'll need to know how to configure both of them. We'll use eth0 for the external interface, and eth1 for the internal interface.

Configuring the interfaces

We first should configure the external interface of the server. You should already know how to do this, and probably already have it done. If you don't, then do so now. If you don't know how, go back and read the Networking HOWTO

Now we bring up the internal interface. According to the numbers that we've chosen, the internal interface of the server is so we have to configure that interface.

For 2.0 kernels, use the following:

# /sbin/ifconfig eth1 netmask broadcast
# /sbin/route add -net netmask dev eth1

For 2.2 kernels, use the following:

# /sbin/ifconfig eth1 netmask broadcast

That get's our basic interfaces up. You can now talk to machines on both local networks that are attached to the server.

Setting routes

We can now talk to machines on our local nets, but we can't get to the rest of our internal network. That requires a few more lines of code. In order to reach the other machines on other subnets, we need have a route that tells traffic to go to the Cisco router. Here's that line:

# /sbin/route add -net gw netmask dev eth1

That line tells the kernel that any traffic destined for the network should go out eth1, and that it should be handed off to the Cisco. Traffic for our local net still gets where it is supposed to because the routing tables are ordered by the size of the netmask. If we were to have other internal nets in our network, we would have a line like the above for each net.

Making filter rules

Ok, so we can reach every machine that we could need to. Now we need to write the firewall filtering rules that allow or deny access through the VPN server.

To set the rules with ipfwadm, run it like so:

# /sbin/ipfwadm -F -f
# /sbin/ipfwadm -F -p deny
# /sbin/ipfwadm -F -a accept -S -D
# /sbin/ipfwadm -F -a accept -b -S -D
# /sbin/ipfwadm -F -a accept -b -S -D

To set the rules with ipchains, run it like so:

# /sbin/ipchains -F forward
# /sbin/ipchains -P forward DENY
# /sbin/ipchains -A forward -j ACCEPT -s -d
# /sbin/ipchains -A forward -j ACCEPT -b -s -d
# /sbin/ipchains -A forward -j ACCEPT -b -s -d

This tells the kernel to deny all traffic except for the traffic that is coming from the network and destined for the network. It also tells the kernel that traffic going between the and nets is allowed, and the same for the net. These last two are bidirectional rules, this is important for getting the routing to work going both ways.


For the home users, everything will work just fine to here. However, for the remote offices, we need to do some routing. First of all, we need to tell the main router, or Cisco, that the remote offices are behind the VPN server. So specify routes on the Cisco that tell it to send traffic destined for the remote offices to the VPN server. Now that that is taken care of, we must tell the VPN server what to do with the traffic destined for the remote offices. To do this, we run the route command on the server. The only problem is that in order for the route command to work, the link must be up, and if it goes down, the route will be lost. The solution is to add the routes when the clients connects, or more simply, to run the route command frequently as it's not a problem to run it more than is necessary. So, create a script and add it to your crontab to be run every few minutes, in it, put the following:

/sbin/route add -net gw netmask
/sbin/route add -net gw netmask

5.5 Server: Configure pppd

Now we will configure pppd on the server to handle VPN connections. If you are already using this server to handle dialup users or even dialing out yourself, then you should note that these changes may affect those services. I go over how to avoid conflicts at the end of this section.


This directory may contain a number of files. You probably already have a file called options. This file holds all of the global options for pppd. These options cannot be overridden by pppd on the command line.


Your options file should contain at least the following:


The first two lines tell pppd to accept what the other end specifies for IP addresses. This is necessary when hooking up remote offices, but can be disabled if you are only connecting home users. It's ok to leave it on, as it does not prevent the server from assigning addresses, it only tells it that it's ok to accept what the client asks for.

The third line is very important. From the pppd man page:

       Add an entry to this system's ARP [Address  Resolu-
       tion  Protocol]  table  with  the IP address of the
       peer and the Ethernet address of this system.  This
       will  have  the effect of making the peer appear to
       other systems to be on the local ethernet.

This is important because if it is not done, local traffic will not be able to get back through the tunnel.

The last line is just as important. This tells pppd to allow connections without username and password. This is safe since authentication is already handled by sshd.

Avoiding conflicts

If you are handling other services with pppd, you should consider that the configurations for these other services may not be the same as what the VPN system needs. pppd is designed such that the options in the main options file /etc/ppp/options cannot be overridden by options specified at runtime. This is done for security reasons. In order to avoid conflict, determine which options cause the conflict, and move them from the main file into a separate options file that is loaded when the appropriate application of pppd is run.

5.6 Server: Configure sshd

The following is what my /etc/sshd_config file looks like. Yours should look the same or similar:

# This is the ssh server system wide configuration file.

Port 22
HostKey /etc/ssh_host_key
RandomSeed /etc/ssh_random_seed
ServerKeyBits 768
LoginGraceTime 600
KeyRegenerationInterval 3600
PermitRootLogin yes
IgnoreRhosts yes
StrictModes yes
QuietMode no
FascistLogging yes
CheckMail no
IdleTimeout 3d
X11Forwarding no
PrintMotd no
KeepAlive yes
SyslogFacility DAEMON
RhostsAuthentication no
RhostsRSAAuthentication no
RSAAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
UseLogin no

The important points to note are that password authentication is disabled as are all of the "r" services. I have also turned off mail checking and the message of the day as they can confuse pppd on the client side. I still allow root login, but as this can only be done with a key, it is adequately safe.

5.7 Server: Set up user accounts

Now we'll set up the user accounts.

Add vpn-users group

just run:

# /usr/sbin/groupadd vpn-users

Now cat the /etc/group file and look at the last line. It should be the entry for the vpn-users group. Note the third field. This is the group ID (GID). Write it down, as we'll need it in a minute. For this example, the GID is 101.

create the vpn-users home directory

We're going to use a single home directory for all of the users. So just run:

# mkdir /home/vpn-users

The .ssh directory

Now create the .ssh directory in the vpn-users home directory.

# mkdir /home/vpn-users/.ssh

Adding users

Now comes the fun part. We're going to edit the /etc/passwd file by hand. :) Normally you let the system handle this file, but for a wierd setup like this, it is easier to do it yourself. To start, let's open the /etc/passwd file and see what's in there. Here's an example of what you might find:

mwilson:x:1000:100:Matthew Wilson,,,:/home/mwilson:/bin/bash
joe:*:1020:101:Joe Mode (home),,,:/home/vpn-users:/usr/sbin/pppd
bill:*:1020:101:Bill Smith (home),,,:/home/vpn-users:/usr/sbin/pppd
frank:*:1020:101:Frank Jones (home),,,:/home/vpn-users:/usr/sbin/pppd

You'll find the first user on most any system. The second one is me. :) After that are a few made up vpn-users. The first field is the username, and the second is the password field. The third is user ID (UID) and the fourth is the group ID (GID). After that comes some info on who the people are in the fifth field. The sixth field is the user's home directory, and the last is their shell. As you can see, each field is separated by a colon. Look at the last three lines. The only difference between them is the username in the first field, and the user info in the fifth field. What we want to do is create lines like this for each user. Don't just use one user for all of the connections, you'll never be able to tell them apart if you do. So, copy the last line of this file and edit it so that it looks something like the above. Make sure that the second field has an asterisk (*). The second field should be unique to all the other IDs in the file. I used 1020. You should use a number above 1000, since those below are typically reserved for system use. The fourth field should be the group ID for vpn-users. I told you to write it down, now is the time that you need it. So put the group ID in there. Lastly, change the home directory to /home/vpn-users, and the shell to /usr/sbin/pppd. That's it. Now copy that line to make more users. Just edit the first the fifth fields and you're set.

5.8 Server: Administration

One of the advantages to using this system for user accounts is that you can take advantage of the UNIX user administration commands. Since each client is logged in as a user, you can use standard methods to get user statistics. The following are a few commands that I like to use to see what all is going on.


Prints the users currently logged in, as well as when they logged in, from where (name or IP), and on which port.


This command prints a more extensive listing of who is currently logged in. It also tells you uptime and load averages for the system. It also lists the user's current process (which should be -pppd for VPN clients) as well as idle time, and current CPU usage for all processes as well as the current process. Read the w man page for more info.

last [username]

This lists the login history for the specified user, or for all users if a username is not provided. It's most useful for finding out how well the tunnels are running as it prints the length of time that the user was logged in, or states that the user is still logged in. I should warn you that on a system that has been up a long time, this list can grow extremely long. Pipe is through grep or head to find out exactly what you want to know.

You can also control which users are allowed to connect by modifying the /home/vpn-users/.ssh/authorized_keys file. If you remove the user's public key line from this file, they won't be able to log in.

5.9 Client: Build the kernel

Now we move onto the client. First we must rebuild the kernel so that it can support all of the functions that we need. The minimum requirement is to have ppp in the kernel. After that, you will need forwarding, firewalling, and gatewaying only if you are going to allow other machines access to the tunnel. For this example, I will setup one of the remote office machines in my example layout. Add the following options to your kernel. Again, if you've never built a kernel before, read the Kernel HOWTO.

For 2.0 kernels:

For 2.2 kernels:

5.10 Client: Configure Networking

Now we should setup the networking on our client box. Let's assume that we've configured the external network and that it works. Now we will configure the internal interface of the client to service our intranet.


We need to first bring up the internal network interface. To do this, add the following to your /etc/rc.d/rc.inet1 (or equivalent) file:

For 2.0 Kernels:

/sbin/ifconfig eth1 broadcast netmask
/sbin/route add -net netmask dev eth1

For 2.2 Kernels:

/sbin/ifconfig eth1 broadcast netmask

Filter rules

For setting up the remote office, we will want to set up our filter rules that allow traffic to go both directions through the tunnel. Add the following lines to your /etc/rc.d/rc.inet1 (or equivalent) file:

For 2.0 kernels:

/sbin/ipfwadm -F -f
/sbin/ipfwadm -F -p deny
/sbin/ipfwadm -F -a accept -b -S -D

For 2.2 kernels:

/sbin/ipchains -F forward
/sbin/ipchains -P forward DENY
/sbin/ipchains -A forward -j ACCEPT -b -s -d

You may have noticed that these lines look like what we have on the server. That's because they are the same. These rules just say where traffic is allowed to go, that is, between these two networks.


The only extra routes that are needed are created by the script that bring the tunnel up.

5.11 Client: Configure pppd

You may not need to edit the client's /etc/ppp/options file at all. You will if the "auth" option is present, or some of the other priveledged options. Try it, and if it fails, a black /etc/ppp/options will work. just keep adding the options from the old file to figure out which one broke it (if it's not obvious) and see if you can get around that. Maybe you don't need them at all. You probably don't if you don't use pppd for anything else.

5.12 Client: Configure ssh

As root on the client, run the following lines:

# mkdir /root/.ssh
# ssh-keygen -f /root/.ssh/identity.vpn -P ""

This will create two files, identity.vpn and in the .ssh directory. The first is your private key, and should be kept such. NEVER SEND THIS OVER THE NET unless it is via an encrypted session. The second file is your public key, and you can send this anywhere you want, it only serves to allow you access to other systems, and cannot be used to get into your own. It is a text file with one line in it that is your actual key. At the end of the line is the comment field which you may change without fear of breaking the key. an example key looks something like this:

1024 35 1430723736674162619588314275167.......250872101150654839

It's actually a lot longer than that, but it wouldn't fit on the page if I showed the whole thing. Copy your key into the /home/vpn-users/.ssh/authorized_keys file on the server. Make sure that there is only one key per line, and that each key is not broken onto multiple lines. You may alter the comment field all that you like in order to help you remember which line goes with which user. I highly recommend doing so.

5.13 Client: Bring up the connection

Now we'll try to actually make the connection to the VPN server. First we'll need to make a single connection the set up ssh's known_hosts file. Run this:

# ssh

Answer ''yes'' when it asks you if you want to continue connecting. The server will tell you ''permission denied'', but that''s ok. It's important that you use the same name for the server that you are using in your connection scripts. Now run the following lines. You will obviously need to change most of the options to fit your setup.

# /usr/sbin/pty-redir /usr/bin/ssh -t -e none -o 'Batchmode yes' -c blowfish -i /root/.ssh/identity.vpn -l vpn-user > /tmp/vpn-device

        (now wait about 10 seconds)

# /usr/sbin/pppd `cat /tmp/vpn-device`

Note the IP addresses specified on the pppd line. The first is the address of the client end of the tunnel. The second is the address of the server end of the tunnel, which is set to the server's internal address. If all of that seemed to work, move on. If not, check that you have all of the options, and that they are spelled right. If something is still going wrong, check the Pitfalls section.

5.14 Client: Set the routes

Now set the route to send traffic through the tunnel. Just run this:

# /sbin/route add -net gw netmask

You should now be able to communicate with machines on the other side of the tunnel. Give it a try. Neat huh? If it doesn't work, try using ping and traceroute to figure out where your problem might be. If in fact it does work, move on to setting up scripts to do the work for you.

5.15 Client: Scripting

Use the vpnd script that I show <@@ref>vpn-scripthere. Only, you need to modify it a little. Make the following changes:

Keeping it running

While bash scripts are generally stable, they have been known to fail. In order to make sure that the vpnd script keeps running, add an entry to the client's crontab that runs the check-vpnd script. I run mine every 5 minutes or so. If vpnd is indeed running, check-vpnd doesn't use much CPU.

Next Previous Contents