What is a personal VPN?

Typically, the goal of a normal VPN is to give trusted clients (optional) access to a trusted network.

The goal of a personal VPN service is totally different:

The purpose of this is to bypass surveillance from the ISP, encrypt the last mile transmission, and prevent data retention of IP addresses.

This HOWTO will create a personal VPN service with the following properties:

Creating the certificates

There are many great tutorials on the internet for creating certificates for use with openvpn. The main openvpn.net howto is very useful as well.

For this setup, we don’t need client certificates, so you can skip that part. In brief, the process is:

Create the Certificate Authority

apt-get install openvpn
cp -r /usr/share/doc/openvpn/examples/easy-rsa/ /etc/openvpn
cd /etc/openvpn/easy-rsa
edit vars
. ./vars
./build-ca

This will create:

Create a server certificate

cd /etc/openvpn/easy-rsa
./build-key-server vpn.domain.org

Replace vpn.domain.org with the actual DNS of the vpn server. It must match, or the client will reject the certificate.

This will create:

Build diffie hellman parameters

The dh file is used to negotiate a shared secret over an insecure connection.

cd /etc/openvpn/easy-rsa
./build-dh 

This generates:

A minimal test setup

apt-get install openvpn

Create the file /etc/openvpn/personal-vpn.conf:

dev tun

ca /etc/openvpn/easy-rsa/keys/ca.crt
cert /etc/openvpn/easy-rsa/keys/vpn.domain.org.crt
key /etc/openvpn/easy-rsa/keys/vpn.domain.org.key
dh /etc/openvpn/easy-rsa/dh1024.pem

server 172.27.0.0 255.255.0.0

plugin /usr/lib/openvpn/openvpn-auth-pam.so login
client-cert-not-required
username-as-common-name

On the server run (as root):

export LD_PRELOAD=/lib/libpam.so.0
openvpn /etc/openvpn/personal-vpn.conf

(you can omit the LD_PRELOAD in debian ‘squeeze’ or later)

Now run ifconfig on the server to check that it created a tun0 network interface and set the ip to be 172.27.0.1.

On the client run (as root):

sudo openvpn --remote vpn.domain.org --client --dev tun --auth-user-pass --ca ca.crt

The file ca.crt is the one you created earlier. Login with any normal user account on the VPN server. Later we will use an SQL user database.

If it works, you can ping the VPN server, but you won’t yet be able to get out onto the internet over the VPN. That will come later.

A word on address space

It is good to use the 172 IP netblock because it is rarely used for home NAT. We have to make sure that the IP address we assign to clients does not conflict with any other networks they are on. So, it would be very bad to pick 192.168.0.0 as a VPN network, because this is the most common IP space used for local networks.

The possible reserved netblocks that can be used for your VPN:

IP range max addresses class max netmask
10.0.0.0 – 10.255.255.255 16,777,216 single class A 10.0.0.0/8 (255.0.0.0)
172.16.0.0 – 172.31.255.255 1,048,576 16 contiguous class Bs 172.16.0.0/12 (255.240.0.0)
192.168.0.0 – 192.168.255.255 65,536 256 contiguous class Cs 192.168.0.0/16 (255.255.0.0)

Routing all the client’s traffic through the VPN

For this HOWTO, we want all the client’s traffic to be routed through the VPN server. To tell the client to do this, just add these lines to the openvpn configuration:

push "redirect-gateway def1"
push "dhcp-option DNS 172.27.0.1"

Where 172.27.0.1 is the VPN IP address the VPN server.

We need to run a DNS server on the VPN server otherwise the client won’t have access to DNS anymore (since most ISPs will block access to their DNS from other networks, and once connected to the VPN you will appear to be coming from an external network).

Setting up a DNS server is beyond the scope of this HOWTO, but a simple one might look like:

apt-get install bind9

Edit /etc/bind/named.conf.options

acl "trusted" {
  127.0.0.1;
  localhost;
};

acl "vpnclients" {
  172.27.0.0/16;
};

options {
  allow-query {
    trusted;
    vpnclients;
  };
  allow-recursion {
    trusted;
    vpnclients;
  };
}

Network configuration

OK, so you have the VPN connection established and you can ping the VPN server from the client, and the client from the VPN server.

How do you get your traffic out to the internet? There are two methods listed here: either everyone can share a single IP or you can give each client their own public IP.

Option A: all clients share a single public IP address

This setup is fairly easy. It is just like if you were setting up a local network with a firewall and NAT. Run this once:

echo '1' > /proc/sys/net/ipv4/ip_forward
iptables --table nat --append POSTROUTING --source 172.27.0.0/16 --out-interface eth0 --jump MASQUERADE

To confirm the iptables are setup:

iptables --table nat --list

should produce:

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.27.0.0/16        anywhere

To remove this:

iptables --table nat --delete POSTROUTING --source 172.27.0.0/16 --out-interface eth0 --jump MASQUERADE

Option B: each client gets their own public IP address

For this setup, we will be generating a unique route for each client. These routes will be setup and torn down in special scripts called by openvpn when a new connection is created or destroyed.

In this example, we have available the public IPs 198.252.153.192/26 (ie .192 to .254) to use for the VPN. We will hand out private IPs in the range 172.27.0.192/26 and then set up a 1:1 mapping between the 172.27.0.192-254 block and the 198.252.153.192-254 block.

cd /etc/openvpn
touch client-connect.sh
touch client-disconnect.sh
chmod u+x *.sh

In /etc/openvpn/person-vpn.conf, add these lines:

script-security 2
client-connect /etc/openvpn/client-connect.sh
client-disconnect /etc/openvpn/client-disconnect.sh
topology subnet

The option topology subnet will make openvpn allocate a single IP for each client. The default is p2p, which will allocate a /30 subnet to each client. This is more compatible with older openvpn clients, but throws off our 1:1 mapping.

Create client-connect.sh:

#!/bin/sh

public_ip_prefix="198.252.153."
vpn_ip_prefix="172.27.0."
netmask="255.255.255.192"

vpn_ip=$ifconfig_pool_remote_ip
ip_suffix=${vpn_ip##$vpn_ip_prefix}
public_ip=${vpn_ip/$vpn_ip_prefix/$public_ip_prefix}

ifconfig eth0:$ip_suffix up $public_ip netmask $netmask

# rewrite incoming packets to use vpn ip
iptables --table nat --append PREROUTING --dst $public_ip --jump DNAT --to-destination $vpn_ip

# rewrite outoing packets to use public ip
iptables --table nat --append POSTROUTING --src $vpn_ip --jump SNAT --to-source $public_ip

exit 0

Create client-disconnect.sh:

#!/bin/sh

public_ip_prefix="198.252.153."
vpn_ip_prefix="172.27.0."
netmask="255.255.255.192"

vpn_ip=$ifconfig_pool_remote_ip
ip_suffix=${vpn_ip##$vpn_ip_prefix}
public_ip=${vpn_ip/$vpn_ip_prefix/$public_ip_prefix}

ifconfig eth0:$ip_suffix down
iptables --table nat --delete PREROUTING --dst $public_ip --jump DNAT --to-destination $vpn_ip
iptables --table nat --delete POSTROUTING --src $vpn_ip --jump SNAT --to-source $public_ip

exit 0

Database authentication

OK, we are almost done. The final thing is to use an external database for the user/password, instead of using the /etc/password file.

Change /etc/openvpn/personal-vpn.conf to use openvpn instead of login for pam authentication:

plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn

Then create the file /etc/pam.d/openvpn:

auth required /lib/security/pam_mysql.so host=127.0.0.1 user={dbuser} passwd={dbpass} db={db} table=users usercolumn=username passwdcolumn=password where=is_active=1 crypt=1
account sufficient pam_permit.so
session sufficient pam_permit.so

Replace {dbuser} and {dbpass} with whatever is appropriate for your database. In this example, we also limit access to records where is_active = 1.

Putting it all together

Modify /etc/default/openvpn:

AUTOSTART="all"
export LD_PRELOAD=/lib/libpam.so.0

The fully completed /etc/openvpn/personal-vpn.conf

##
## NETWORKING
## 

dev tun
keepalive 20 120
topology subnet

##
## SCRIPTS
##

#user nobody    \ well, this sucks, but the current setup needs to be able
#group nogroup  / to run ifconfig and iptables. so, for now, we run as root.

script-security 2
client-connect /etc/openvpn/client-connect.sh
client-disconnect /etc/openvpn/client-disconnect.sh

##
## KEYS and CERTIFICATES
##

ca    /etc/openvpn/easy-rsa/keys/ca.crt
cert  /etc/openvpn/easy-rsa/keys/vpn.domain.org.crt
key   /etc/openvpn/easy-rsa/keys/vpn.domain.org.key
dh    /etc/openvpn/easy-rsa/dh1024.pem

##
## SERVER MODE
##

server 172.27.0.192 255.255.255.192
push "redirect-gateway def1"
push "dhcp-option DNS 198.252.153.28"
# ^^ note: if using MASQUERADE, push the vpn ip for dns. If using SNAT, push the public ip.

##
## AUTHENTICATION
##

plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn
client-cert-not-required
username-as-common-name

Comments:

dev tun
easier than tap
keepalive 20 120
keeps the connection from dying due to inactivity.
topology subnet
allocates only a single ip per client, instead of 4.
script-security 2
allows client-connect scripts
client-connect /etc/openvpn/client-connect.sh
where the networking is setup for a client
client-disconnect /etc/openvpn/client-disconnect.sh
where the networking is taken down for a client
push “redirect-gateway def1”
makes the client send all their traffic to the server.
push “dhcp-option DNS 198.252.153.28”
tells the client to use our dns server. note: if using MASQUERADE, push the vpn ip for dns. If using SNAT, push the public ip.
client-cert-not-required
don’t use client certs. the clients are not ‘trusted’
plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn
use pam for password authentication, service named ‘openvpn’ (ie whatever is in /etc/pam.d/openvpn)
username-as-common-name
not sure of the benefit. we don’t have a common-name (because there is no client cert) so this seemed like a good idea.

Other commonly suggested server options:

persist-tun
persist-key

But I am not sure what the benefit is.

Configuring clients

You must download to each client the ca.crt file created earlier.

Network Manager (gnome)

apt-get install network-manager-openvpn
/etc/init.d/network-manager restart

If you move the location of the CA cert file, then the vpn will break.

After you have saved the configuration, you can click on the network manager applet and apply it. If it worked, you should see a lock in the corner of the applet. If it didn’t work, tail /var/log/syslog.

tunnelblick (mac)

Edit the configuration file:

client
dev tun
auth-user-pass
remote vpn.domain.org 1194
ca /path/to/ca.crt

You can easily build custom bundles with tunnelblick so that you can distribute a single program pre-configured for a particular vpn with the certificates already included. This way, no configuration is required, just download and double click.

openvpn command line (mac and linux)

requirements:

apt-get install resolvconf openvpn

required files:

running openvpn:

sudo openvpn --config /etc/openvpn/riseup/client.conf

Create the file /etc/openvpn/riseup/auth:

joehill
jghw82dfa1

The first line is your vpn username, the second line is your password. This can be commented out for a login prompt on startup

Create the file /etc/openvpn/riseup/client.conf:

# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client

# Use the same setting as you are using on
# the server.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
dev tun

# Are we connecting to a TCP or
# UDP server?  Use the same setting as
# on the server.
;proto tcp
proto udp

# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
# 
# choose whichever is closer for you
remote seattle.vpn.riseup.net 1194
;remote nyc.vpn.riseup.net 1194

# Keep trying indefinitely to resolve the
# host name of the OpenVPN server.  Very useful
# on machines which are not permanently connected
# to the internet such as laptops.
resolv-retry infinite

# Most clients don't need to bind to
# a specific local port number.
nobind

# Downgrade privileges after initialization (non-Windows only)
user nobody
group nogroup

# Try to preserve some state across restarts.
persist-key
persist-tun

# If you are connecting through an
# HTTP proxy to reach the actual OpenVPN
# server, put the proxy server/IP and
# port number here.  See the man page
# if your proxy server requires
# authentication.
;http-proxy-retry # retry on connection failures
;http-proxy [proxy server] [proxy port #]

# Wireless networks often produce a lot
# of duplicate packets.  Set this flag
# to silence duplicate packet warnings.
mute-replay-warnings

# SSL/TLS parms.
# See the server config file for more
# description.  It's best to use
# a separate .crt/.key file pair
# for each client.  A single ca
# file can be used for all clients.
ca /etc/openvpn/riseup/riseup-ca-cert.pem
auth-user-pass /etc/openvpn/riseup/auth
auth-nocache

# Verify server certificate by checking
# that the certicate has the nsCertType
# field set to "server".  This is an
# important precaution to protect against
# a potential attack discussed here:
#  http://openvpn.net/howto.html#mitm
#
# To use this feature, you will need to generate
# your server certificates with the nsCertType
# field set to "server".  The build-key-server
# script in the easy-rsa folder will do this.
remote-cert-tls server

# If a tls-auth key is used on the server
# then every client must also have the key.
;tls-auth ta.key 1

# Select a cryptographic cipher.
# If the cipher option is used on the server
# then you must also specify it here.
;cipher x

# Set log file verbosity.
verb 3

# Silence repeating messages
mute 20

# Log-File
log /etc/openvpn/riseup/openvpn.log

# DNS
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf