Pages

8.02.2012

A (functional) OpenVPN bridged server setup

OpenVPN is a handy tool for the mobile tech guy who like to have access to his/her home/office network or just prefers some privacy when browsing on-the-go.  OpenVPN is not the only solution out there for this sort of task, but it costs nothing and works on all platforms.

The problem I have run into in the past and very recently is that the documentation for configuring OVPN in bridged mode on the server side is very incomplete.  Most of the scripts on the internet simply do not work the way I need them to in my setup... My personal server has only one NIC and sits behind the router.  Additionally, most of the instructions do not include the facts that IP forwarding and default gateway settings need to be enabled in order to use the VPN to securely browse the Internet.


To get started, I highly recommend reading this guide.  Complete the installation and "Public Key Infrastructure Setup" sections, then come back here.  Refer back to that guide for client setup later on.
The OpenVPN documentation page also has excellent information on client and server setup for anything not covered here.  This guide will focus mostly on the network setup part, which was the most challenging part for me to accomplish.

What this guide covers:
  • Network setup for a single NIC server to run OpenVPN
  • Scripts to configure the network and OPVN services
What this guide does NOT cover:
  • Installing OpenVPN from source or repo
  • Installing OVPN on Windows
  • Nice GUIs to do all this for you
  • Setting up the PKI for the server
On my home server I chose to run Ubuntu 12.04 LTS server edition as the OS.  Keep in mind that it has no GUI out-of-the-box, so everything is done in terminal.  Also note that this configuration should be done locally, not over SSH remotely, as connections will inevitably fail upon changing the network configs.

Starting from a fresh install of OVPN from repository, we will make the script required to create a tap device, a network bridge, and bridge the tap and ethernet devices together.  
Copy the script below into a file called openvpn-bridge and place it inside the /etc/openvpn/ directory.  The items highlighted in pink are values that you will need to change according to your setup.   Make the script executable by running a chmod 755 openvpn-bridge inside the openvpn directory after you make the script.

##############################################################
#!/bin/bash

#################################
# Set up Ethernet bridge on Linux
# Requires: bridge-utils
#################################

# Define Bridge Interface
br="br0"

# Define list of TAP interfaces to be bridged,
# for example tap="tap0 tap1 tap2".
tap="tap0"

# Define physical ethernet interface to be bridged
# with TAP interface(s) above.
eth="eth0"

eth_ip="10.1.1.31"
eth_netmask="255.0.0.0"
eth_broadcast="10.255.255.255"
#The "gw" setting is crucial here.  This should be the IP address of your #network's router.  This setting makes it possible to reach the internet through #the VPN instead of just local resources.
gw="10.1.1.1"

case "$1" in
  start)
  for t in $tap; do
      openvpn --mktun --dev $t
  done

  brctl addbr $br
  brctl addif $br $eth

  for t in $tap; do
      brctl addif $br $t
  done

  for t in $tap; do
      ifconfig $t 0.0.0.0 promisc up
  done

  ifconfig $eth 0.0.0.0 promisc up

  ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast
  route add default gw $gw
  ;;
  stop)
  ifconfig $br down
  brctl delbr $br

  for t in $tap; do
      openvpn --rmtun --dev $t
  done
  ifconfig $eth $eth_ip netmask $eth_netmask broadcast $eth_broadcast
  route add default gw $gw
  ;;
  *)
  echo "usage openvpn-bridge {start|stop}"

  exit 1
  ;;
esac
exit 0
########################################################################


Next you will need to replace the default init script for OVPN with this one modified to include the bridge starting script.  You should backup the original init script just in case something goes wrong.  Run a cp /etc/init.d/openvpn /home/$USER/openvpn to backup the script.  ( $USER will be replaced with your username.)  After you backup the original script, then copy and paste this one in its place.

 I did not create the following script.  Credits for the script are included within the script itself.

#########################################################################
#!/bin/sh -e
#
# Original version by Robert Leslie
# <rob@mars.org>, edited by iwj and cs
# Modified for openvpn by Alberto Gonzalez Iniesta <agi@inittab.org>
# Modified for restarting / starting / stopping single tunnels by Richard Mueller <mueller@teamix.net>

# Modified to add bridge control by Josh Vickery <vickeryj@freeshell.org>

test $DEBIAN_SCRIPT_DEBUG && set -v -x

DAEMON=/usr/sbin/openvpn
DESC="virtual private network daemon"
CONFIG_DIR=/etc/openvpn
BRIDGE_CTL=/etc/openvpn/openvpn-bridge
test -x $DAEMON || exit 0
test -d $CONFIG_DIR || exit 0

# Source defaults file; edit that file to configure this script.
AUTOSTART="all"
STATUSREFRESH=10
if test -e /etc/default/openvpn ; then
  . /etc/default/openvpn
fi

start_vpn () {
    if grep -q '^[ ]*daemon' $CONFIG_DIR/$NAME.conf ; then
      # daemon already given in config file
      DAEMONARG=
    else
      # need to daemonize
      DAEMONARG="--daemon ovpn-$NAME"

    fi

    if grep -q '^[ ]*status ' $CONFIG_DIR/$NAME.conf ; then
      # status file already given in config file
      STATUSARG=""
    elif test $STATUSREFRESH -eq 0 ; then
      # default status file disabled in /etc/default/openvpn
      STATUSARG=""
    else
      # prepare default status file
      STATUSARG="--status /var/run/openvpn.$NAME.status $STATUSREFRESH"
    fi
    $BRIDGE_CTL start
    $DAEMON --writepid /var/run/openvpn.$NAME.pid \
            $DAEMONARG $STATUSARG --cd $CONFIG_DIR \
            --config $CONFIG_DIR/$NAME.conf || echo -n " FAILED->"

    echo -n " $NAME"
}
stop_vpn () {
   kill `cat $PIDFILE` || true
  rm $PIDFILE
  [ -e /var/run/openvpn.$NAME.status ] \
    && rm /var/run/openvpn.$NAME.status
  $BRIDGE_CTL stop
}

case "$1" in
start)
  echo -n "Starting $DESC:"

  # autostart VPNs
  if test -z "$2" ; then
    # check if automatic startup is disabled by AUTOSTART=none
    if test "x$AUTOSTART" = "xnone" -o -z "$AUTOSTART" ; then
      echo " Autostart disabled."

      exit 0
    fi
    if test -z "$AUTOSTART" -o "x$AUTOSTART" = "xall" ; then
      # all VPNs shall be started automatically
      for CONFIG in `cd $CONFIG_DIR; ls *.conf 2> /dev/null`; do
        NAME=${CONFIG%%.conf}
        start_vpn
      done
    else
      # start only specified VPNs
      for NAME in $AUTOSTART ; do
        if test -e $CONFIG_DIR/$NAME.conf ; then
          start_vpn
        else
          echo -n " (failure: No such VPN: $NAME)"

        fi
      done
    fi
  #start VPNs from command line
  else
    while shift ; do
      [ -z "$1" ] && break
      if test -e $CONFIG_DIR/$1.conf ; then
        NAME=$1
        start_vpn
      else
        echo -n " (failure: No such VPN: $1)"
      fi
    done
  fi
  echo "."

  ;;
stop)
  echo -n "Stopping $DESC:"

  if test -z "$2" ; then
    for PIDFILE in `ls /var/run/openvpn.*.pid 2> /dev/null`; do
      NAME=`echo $PIDFILE | cut -c18-`
      NAME=${NAME%%.pid}
      stop_vpn
      echo -n " $NAME"
    done
  else
    while shift ; do
      [ -z "$1" ] && break
      if test -e /var/run/openvpn.$1.pid ; then
        PIDFILE=`ls /var/run/openvpn.$1.pid 2> /dev/null`
        NAME=`echo $PIDFILE | cut -c18-`
        NAME=${NAME%%.pid}
        stop_vpn
        echo -n " $NAME"

      else
        echo -n " (failure: No such VPN is running: $1)"
      fi
    done
  fi
  echo "."
  ;;
# We only 'reload' for running VPNs. New ones will only start with 'start' or 'restart'.
reload|force-reload)
  echo -n "Reloading $DESC:"
  for PIDFILE in `ls /var/run/openvpn.*.pid 2> /dev/null`; do
    NAME=`echo $PIDFILE | cut -c18-`
    NAME=${NAME%%.pid}
# If openvpn if running under a different user than root we'll need to restart
    if egrep '^( |\t)*user' $CONFIG_DIR/$NAME.conf > /dev/null 2>&1 ; then
      stop_vpn
      sleep 1
      start_vpn
      echo -n "(restarted)"

    else
      kill -HUP `cat $PIDFILE` || true
    echo -n " $NAME"
    fi
  done
  echo "."
  ;;

restart)
  shift
  $0 stop ${@}
  sleep 1
  $0 start ${@}
  ;;
cond-restart)
  echo -n "Restarting $DESC:"
  for PIDFILE in `ls /var/run/openvpn.*.pid 2> /dev/null`; do
    NAME=`echo $PIDFILE | cut -c18-`
    NAME=${NAME%%.pid}
    stop_vpn
    sleep 1
    start_vpn
  done
  echo "."

  ;;
*)
  echo "Usage: $0 {start|stop|reload|restart|force-reload|cond-restart}" >&2
  exit 1
  ;;
esac

exit 0

# vim:set ai sts=2 sw=2 tw=0:
############################################################################

Lastly, configure the server.conf file found in /etc/openvpn/.  Mine looks like this.  You will of course need to provide your own values for the IP address fields highlighted.

###############################################
port 1194

proto udp

dev tap0

ca easy-rsa/ca.crt
cert easy-rsa/server.crt
key easy-rsa/server.key  # This file should be kept secret

dh easy-rsa/dh1024.pem

ifconfig-pool-persist ipp.txt
#The first IP address is your server's IP. The next is of course the subnet mask
#the final two addresses are the range of IP addresses your server will 
#assign to VPN clients.  These should be outside the normal DHCP range
#of your network.
server-bridge 192.168.17.20 255.255.255.0 192.168.17.50 192.168.17.100

;client-to-client
keepalive 10 120

comp-lzo

user nobody
group nogroup

persist-key
persist-tun

# Set the appropriate level of log
# file verbosity.
#
# 0 is silent, except for fatal errors
# 4 is reasonable for general usage
# 5 and 6 can help to debug connection problems
# 9 is extremely verbose
verb 3

# Silence repeating messages.  At most 20
# sequential messages of the same message
# category will be output to the log.
;mute 20

######################################################################

The operation to perform is to enable IP forwarding permanently.  Edit /etc/sysctl.conf and change the line net.ipv4.ip_forward = 0 from 0 to 1. If the line does not exist, add it.
Now, if everything was set up properly, you should be able to run OVPN by typing service openvpn start.  Check ifconfig to ensure that all the network adapters came online.  Yours should look like this if everything went right.  The network bridging script creates the tap0 device and the br0 device.

br0       Link encap:Ethernet  HWaddr 1e:27:63:75:bb:06  
          inet addr:192.168.17.20  Bcast:192.168.17.255  Mask:255.255.255.0
          inet6 addr: fe80::1c27:63ff:fe75:bb06/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:95060 errors:0 dropped:0 overruns:0 frame:0
          TX packets:63227 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:73829583 (73.8 MB)  TX bytes:26425634 (26.4 MB)

eth0      Link encap:Ethernet  HWaddr 6c:62:6d:40:59:40  
          inet6 addr: fe80::6e62:6dff:fe40:5940/64 Scope:Link
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
          RX packets:133544 errors:0 dropped:0 overruns:0 frame:0
          TX packets:73376 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:98037746 (98.0 MB)  TX bytes:27640334 (27.6 MB)
          Interrupt:41 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:7685 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7685 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:825616 (825.6 KB)  TX bytes:825616 (825.6 KB)

tap0      Link encap:Ethernet  HWaddr 1e:27:63:75:bb:06  
          inet6 addr: fe80::1c27:63ff:fe75:bb06/64 Scope:Link
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
          RX packets:10753 errors:0 dropped:0 overruns:0 frame:0
          TX packets:58635 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:1333362 (1.3 MB)  TX bytes:22906209 (22.9 MB)

Once you have verified the server's network setup, try connecting a client to it.  If the client is unable to connect with ~15 seconds, you may need to check the OVPN logs on the server and client. If your server is behind a router like mine, don't forget to forward port 1194 on the router to the LAN IP address of your server.  If you still have problems, be patient and ask Google for help.

Please email me with any suggestions, omissions, comments or concerns.

2 comments:

Anonymous said...

I got an error on line 21 for openvpn-bridge script. Syntax error: word unexpected (expecting "in")

Anonymous said...

Sorry it is line 26