Starting/Stopping an OpenVPN Tunnel and Routing Traffic Through It

The previous sections showed how to bring up an OpenVPN tunnel and optionally how to use iproute2 to route traffic through the VPN tunnel. However, the setting up or knocking down of a VPN tunnel is actually a lot more complicated than was alluded to in those sections. This section outlines all of the steps that are necessary to route traffic from one or more locally-connected systems through an OpenVPN tunnel on a router system and back.

Starting the VPN tunnel on the router itself is simple enough but if packets from other locally-connected systems are also to be routed through it, the router's kernel packet filters must be adjusted to masquerade the outbound packets so that they all appear to be coming from the tunneled client (i.e. the router). This is because the remote VPN server has no knowledge of the other locally-connected, routed systems, just the tunneled client.

However, if masquerading is used, confusion within the kernel code about the proper route being used for the masqueraded/tunneled reply packets causes the router to incorrectly apply reverse path filtering to all packets received on the tunnel interface and, hence, drop them as Martians. Consequently, reverse path filtering must be disabled on the tunnel interface, each time the tunnel is brought up, or response packets will just disappear. The VPN tunnel will appear to be set up correctly but no connections from the locally-connected systems will work (although traffic from/to the router itself will work fine).

In many instances, a VPN tunnel can be used to create a connection to a remote location that is wild and wooley (e.g. a connection to the Internet that is located in a faraway place, far from prying eyes). But, bringing up such a VPN tunnel, especially if reverse path filtering is disabled, can allow all sorts of nasty stuff into the local system. This makes it prudent to alter the firewall (iptables) rules, whenever the VPN tunnel is brought up, to do spoof checking and handle the inbound reception and forwarding of VPN packets.

Once the VPN tunnel is set up, as noted in "Routing Traffic Through a VPN Connection" iptables can also be used to figure out which packets should be routed through the VPN tunnel and so mark them. Then, iproute2 can be used to establish the routing tables that actually route the marked packets through the VPN tunnel and any replies back to the local systems. This means that the routing tables must set up and knocked down whenever the VPN tunnel is brought up or down.

Finally, it would be great if the operation of the VPN tunnel could be automated so that it is started on the router whenever the router system is brought up and stopped whenever it is shut down. Thus, we need a startup script that ties all of these steps together.

In preparation for running an OpenVPN tunnel, to begin with, if you haven't already done so, obtain the actual credentials that you will be using to make your OpenVPN tunnel connection and put them in the /etc/openvpn directory, as described in the "OpenVPN Client" section. You can test the tunnel connection manually before you proceed any furnther, if you wish. Its probably a good idea to keep things simple until the connection is known to be working OK.

The next preparatory step is to set up the "special" routing table that will be used by iproute2 to route packets through the VPN tunnel, as outlined in the "Routing Traffic Through a VPN Connection" section. The table itself will not be activated until the tunnel is actually brought up but it must be configured ahead of time.

The last preparatory step is to determine what criteria you will use to select the packets that will be routed through the VPN tunnel and decide how you will implement the selection. As we said in the section on "Routing Traffic Through a VPN Connection", we prefer to use iptables to mark the packets that are to be routed through the OpenVPN tunnel using iptables and then direct the marked packets to the "special" routing table and thence through the VPN tunnel.

You can certainly set the rules directly with the iptables command and then save/restore the configuration with iptables-save and iptables-restore. Since our preferred method of setting up iptables is the NARC firewall package, we instead add the rules to the /etc/narc/narc-custom.conf file and allow NARC to set them up when the firewall is brought up, presumably at system startup.

We now turn our attention to the tun-updown script which handles all of the alterations to the firewall rules and routing tables required when an OpenVPN tunnel is started or stopped. It includes code to: update the firewall rules in a manner that is consistent with the NARC firewall; masquerade the outbound traffic; and establish the routing of marked packets through the VPN tunnel. You may wish to put it in the /etc/openvpn directory which you already created to hold the OpenVPN credentials.

/etc/openvpn/tun-updown:

     #!/bin/bash
     #
     # Script to handle the bringup and shutdown of a VPN tunnel started by
     # OpenVPN.
     #
     # This script is called by the openvpn command as a result of its name being
     # passed to openvpn via the --up and --down parameters.  The openvpn command
     # should be invoked like this:
     #
     #      openvpn --route-noexec --script-security 2
     #              --up /etc/openvpn/tun-updown
     #              --down /etc/openvpn/tun-updown --down-pre ...
     #
     # Using these parameters, the openvpn command invokes tun-updown after the
     # "tun" device is configured and connected.  It also invokes tun-updown just
     # before the "tun" device is torn down, upon shutdown.  The environment
     # variable "script_type" is consulted to determine whether the tunnel is
     # being brought up or shut down, thereby allowing a single script to do all
     # of the work.
     #
     # Note that this script is meant only to be used with "tun" devices, not
     # "tap" devices.  If you are using a "tap" device for the tunnel, you will
     # need to alter this script (perhaps significantly).
     #
     # When OpenVPN runs this script, it calls it like this:
     #
     #      tun-updown tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_rmt_ip
     #                 {init|restart}
     #
     # As a consequence of being called by the "--up" parameter, this script will
     # add special rules to the iptables netfilter tables that will assist with
     # firewalling all traffic on the VPN tunnel.  In addition, the regular
     # firewall rules will still be in effect and, in conjunction with the
     # special rules, they will ensure that all inbound VPN tunnel traffic is
     # properly firewalled.
     #
     # This script will then add a rule to the routing tables that will send
     # all traffic marked as VPN traffic to a special routing table which will
     # be set up with a default gateway that points to the VPN tunnel.  This
     # will route all non-local traffic marked as VPN traffic out the VPN
     # tunnel.
     #
     # As a result of being called by the "--down" parameter, this script undoes
     # all of the work it did during startup.  It removes the route to the
     # special VPN routing table that it added to the system's routing table and
     # then removes all of the special rules from the iptables netfilter tables
     # that were added to assist with firewalling traffic on the VPN tunnel.
     #
     ##########################################################################
     #
     # We add rules to the netfilter tables in the same manner that NARC does
     # so we need to know where its config file is.
     #
     CONFIG="/etc/narc/narc.conf"
     #
     # Minimum acceptable NARC config file version.
     #
     MINCONF_VERSION=0.6.3
     #
     # Pick up the VPN interface that was created by OpenVPN.
     #
     VPN_INTERFACE=$1
     VPN_INTERFACE_IP=$4
     VPN_REMOTE_IP=$5
     ##########################################################################
     #
     # Routine to allow us to bail out of failed rules, etc.
     #
     abortexit()
         {
         echo "Failed to set up iptables and/or iproute2."
         exit 1
         }
     ##########################################################################
     #
     # Load the NARC Configuration file.
     #
     if test -f $CONFIG ; then
         . $CONFIG
     else
         echo "Cannot find the NARC config file $CONFIG."
         exit 1
     fi
     #
     # Check for the iptables binary.
     #
     if ! test -f "$IPTABLES" ; then
         echo "The iptables binary $IPTABLES is missing."
         exit 1
     else
         $IPTABLES -V >/dev/null 2>&1 || BADBIN='yes'
         if [ "$BADBIN" == 'yes' ] ; then
             echo "The iptables binary $IPTABLES exists but it failed to run."
             echo "Try `which iptables`."
             exit 1
         fi
     fi
     #
     # Check if the config file is compatible with this script.
     #
     if [ `expr $CONF_VERSION \< $MINCONF_VERSION` == 1 ] ; then
         echo "The NARC config file $CONFIG is at an incompatibile version."
         echo "The minimum vesion is $MINCONF_VERSION."
         echo "The config file is at $CONF_VERSION.  Please upgrade it."
         exit 1
     fi
     #
     # Set the SPOOF_CHK target.
     #
     if [[ "$LOG_DROPS" == 'yes' && "$LOG_SPOOF" == 'yes' ]] ; then
         SPOOF_TARGET='CUST_LOG'
     else
         SPOOF_TARGET='DROP'
     fi
     #
     # If the VPN tunnel is being brought up, set everything up.
     #
     if [ x"$script_type" == xup ] ; then
         #
         # Create the VPN_CHK chain.
         #
         # This chain does anti-spoofing checking for the reserved and private
         # IP addresses on the VPN tunnel.  It also checks for established
         # connections and lets their packets in.  All other packets are
         # dumped as invalid.
         #
         # This chain is hung off the INPUT chain for all inbound traffic on
         # the tunnel device.
         #
         $IPTABLES -N VPN_CHK || abortexit
         #
         # Check reserved networks.
         #
         if [ "$RESERVED_NETWORKS" != '' ] ; then
             echo -n "Enabling spoof checking on $VPN_INTERFACE for reserved \
                 network(s): "
             for network in $RESERVED_NETWORKS ; do
                 $IPTABLES -A VPN_CHK -s $network -i $VPN_INTERFACE \
                     -j $SPOOF_TARGET || abortexit
                 echo -n "$network "
             done
             echo "."
         fi
         #
         # Check private networks.
         #
         if [ "$PRIVATE_NETWORKS" != '' ] ; then
             echo -n "Enabling spoof checking on $VPN_INTERFACE for private \
                 network(s): "
             for network in $PRIVATE_NETWORKS ; do
                 IPVAL2=`echo $VPN_INTERFACE_IP | cut -d . -f 1`
                 NETVAL2=`echo $network | cut -d . -f 1`
              if [[ $IPVAL2 == 10 && $NETVAL2 == 10 ]] ; then
                  DONOTHING=1
              else
                  IPVAL=`echo $VPN_INTERFACE_IP | cut -d . -f 1,2`
                  NETVAL=`echo $network | cut -d . -f 1,2`
                  if [ $IPVAL != $NETVAL ] ; then
                      DONOTHING=0
                      $IPTABLES -A VPN_CHK -s $network -i $VPN_INTERFACE \
                         -j $SPOOF_TARGET || abortexit
                  else
                      DONOTHING=1
                  fi
              fi
              if [ "$DONOTHING" != 1 ] ; then
                  echo -n "$network "
              fi
          done
          echo "."
      fi
      #
      # Add the local IP address of the VPN tunnel too.
      #
      echo "Enabling spoof checking on $VPN_INTERFACE for tunnel IP: \
          $VPN_INTERFACE_IP."
      $IPTABLES -A VPN_CHK -s $VPN_INTERFACE_IP -i $VPN_INTERFACE \
          -j $SPOOF_TARGET || abortexit
      #
      # At this point, we can let responses to any packets that we sent out
      # back in through the tunnel.
      #
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -m state \
          --state RELATED,ESTABLISHED -j ACCEPT || abortexit
      #
      # Anything that makes it this far is logged as failing the VPN check
      # and then dropped.  For the VPN tunnel, only established sessions are
      # allowed.
      #
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -j LOG \
          --log-level $WARN_LOG_LEVEL --log-tcp-options --log-ip-options \
          --log-prefix "VPN_CHK " || abortexit
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -j DROP || abortexit
      #
      # Hang the VPN tunnel spoof checking chain off the INPUT filter
      # chain.
      #
      # This chain also accepts any established packets, which lets them
      # in if they are OK.
      #
      echo "Hooking VPN rules into the INPUT and FORWARD filter chains."
      $IPTABLES -I INPUT -i $VPN_INTERFACE -j VPN_CHK || abortexit
      #
      # Now that we are checking to make sure that all incoming packets on
      # the VPN tunnel are legit, we can allow forwarding from the tunnel
      # to our internal network.  This turns on the tunnel.
      #
      $IPTABLES -I FORWARD -i $VPN_INTERFACE -j ACCEPT || abortexit
      #
      # Packets sent out on the VPN tunnel must be masqueraded so that the
      # recipient at the other end can send the answers back to this system.
      # It will then forward them to the original sender.
      #
      # Note that we acutally use SNAT, which allows us to specify the IP
      # address to masquerade to, and which doesn't monitor the state of
      # the connection.  Otherwise, SNAT works just like MASQUERADE (or
      # vice versa).
      #
      $IPTABLES -A POSTROUTING -t nat -o $VPN_INTERFACE \
          -j SNAT --to $VPN_INTERFACE_IP || abortexit
      #
      # For some reason, when packets are masqueraded and sent down the
      # tunnel, the router gets confused and doesn't add the proper route
      # to its forwarding table.  This means that, if reverse path filtering
      # is turned on, replies to any packets sent out the VPN tunnel will be
      # dropped as Martians.  To fix this problem, we turn off reverse path
      # filtering on the VPN tunnel.
      #
      # Note that other firewall rules that we add herein should take care
      # of any real Martians.
      #
      # Also note that there's no need to undo this step upon shutdown, since
      # the rp_filter file is deleted when the VPN tunnel is torn down.
      #
      # However, beware that there is a VPN tunnel vulnerability that is
      # enabled by turning off reverse path filtering.  When it is turned
      # off, any applications (e.g. BitTorrent) that listen on an external
      # UDP port which is port forwarded from the edge router or otherwise
      # visible to the regular (i.e. not tunneled) Internet can be tricked
      # into revealing the actual IP address of its system, if its system
      # is routed through the VPN tunnel.
      #
      # This being the case, it is imperative that any systems whose packet
      # traffic is routed through the VPN tunnel do not have an
      # outward-facing port of any kind visible through the regular
      # Internet (i.e. not the VPN tunnel).  This especially includes the
      # system that is doing the routing and running the VPN tunnel.
      #
      # To ensure anonimity, do not route any of this system's packets
      # through the VPN tunnel, for any reason, and do not expose any ports
      # on any hidden system to the outside Internet via any kind of port
      # forwarding, mapping, proxying, etc.
      #
      echo 0 >/proc/sys/net/ipv4/conf/${VPN_INTERFACE}/rp_filter
      #
      # Use iproute2 to add a default route to the vpn.tunnel table to send
      # all packets bound for the WAN out through the VPN tunnel.  Then,
      # send all packets with the firewall (i.e. iptables) mark value of 1
      # to to this table.
      #
      # Note that sometimes, depending on the type of connection, OpenVPN
      # will assign the same IP address to the local end of the tunnel as
      # it does to the remote end of the tunnel.  When it does this, it
      # passes a remote IP address to us that looks like 255.255.255.0.
      # So, we need to check for this situation and use the local interface
      # IP address instead of the remote address.
      #
      if [ x"${VPN_REMOTE_IP:0:7}" == "x255.255" ]; then
          echo "Routing all marked packets to the VPN tunnel via local IP."
          /sbin/ip route add default via $VPN_INTERFACE_IP dev $VPN_INTERFACE \
              table vpn.tunnel
      else
          echo "Routing all marked packets to the VPN tunnel via remote IP."
          /sbin/ip route add default via $VPN_REMOTE_IP dev $VPN_INTERFACE \
              table vpn.tunnel
      fi
      /sbin/ip route flush cache
      /sbin/ip rule add fwmark 1 table vpn.tunnel

#
# Otherwise, shut everything down.
#
else

      #
      # Tell iproute2 not to do anything special with marked packets and
      # not to send WAN packets out through the VPN tunnel.
      #
      echo "Suspending routing of all marked packets to the VPN tunnel."
      /sbin/ip rule del fwmark 1 table vpn.tunnel
      /sbin/ip route del default table vpn.tunnel
      /sbin/ip route flush cache
      #
      # Turn off masquerading for the VPN tunnel packets.
      #
      $IPTABLES -D POSTROUTING -t nat -o $VPN_INTERFACE \
          -j SNAT --to $VPN_INTERFACE_IP
      #
      # Disallow forwarding from the VPN tunnel.
      #
      echo "Unhooking VPN rules from the INPUT and FORWARD filter chains."
      $IPTABLES -D FORWARD -i $VPN_INTERFACE -j ACCEPT
      #
      # Unhook the VPN tunnel spoof checking chain from the INPUT chain.
      #
      $IPTABLES -D INPUT -i $VPN_INTERFACE -j VPN_CHK
      #
      # Delete all of the rules from the VPN tunnel spoof checking chain and
      # then get rid of the chain itself.
      #
      echo "Deleting all rules from VPN tunnel spoof checking chain."
      FOUNDRULE=1
      while [ "$FOUNDRULE" != 0 ] ; do
          $IPTABLES -D VPN_CHK 1 >/dev/null 2>&1 || FOUNDRULE=0
      done
      #
      # Get rid of the now-empty chain.
      #
      $IPTABLES -X VPN_CHK

fi
#
# That's all she wrote.
#
exit 0

The next step is to create a startup script that will bring the OpenVPN tunnel up and down at system startup and shutdown. Or, if you prefer, you can simply use this script to start/stop the OpenVPN tunnel as required. While we're at it, we can set this script up to start/stop PPTP tunnels as well, since the process is basically the same. This yields a more versatile start/stop script.

/etc/init.d/vpnconnect:

     #!/bin/sh
     #
     # vpnconnect    This script starts or stops an OpenVPN or PPTP connection to
     #               a faraway land, via the primary server in the cluster, over
     #               the Internet.  Its purpose is to tunnel packets from/to
     #               certain local IP addresses (or even just certain types of
     #               traffic) transparently to those addresses' related local
     #               systems.  This lets the local systems send/receive packets
     #               from the remote, VPN-connected world without even knowing
     #               that a VPN tunnel is involved.  Also, packets from more
     #               than one local system can be tunneled to the remote world
     #               over a single VPN tunnel.
     #
     #               Since the VPN tunnel must run over a pre-existing
     #               Internet connection, this script must start after the WAN
     #               connection is brought up (i.e. after either the adsl or
     #               wanconnect script has run).  And, since starting up the
     #               VPN connection also alters the routing tables and changes
     #               the filetering done by iptales, this script must obviously
     #               be run and after the iptables script.
     #
     #               Aditionally, we may want the VPN connection to be up and
     #               running before we start any of the application-type
     #               services (e.g. sendmail, smb), if those services expect to
     #               be able to talk over the VPN tunnel to systems at the
     #               remote location.  Thus, we must carefully choose the
     #               sequence in which the VPN connection is started/stopped.
     #
     #               Finally, all of the real work is done by iptables, which
     #               marks the actual packets bound for the VPN tunnel with
     #
     # chkconfig: 2345 51 59
     # description: Connects to a remote system, via an OpenVPN or PPTP tunnel, \
     #              over a preexisting Internet connection, on the the primary \
     #              server in the cluster.
     #
     # Revision History:
     # ewilde      2011Nov6   Initial coding.
     # ewilde      2011Dec4   Add PPTP support.
     #
     #
     # Define the type of VPN connection.
     #
     VPNCONN=OpenVPN
     #VPNCONN=PPTP
     #
     # Define paths to the programs used herein.
     #
     OPENVPN=/usr/local/sbin/openvpn
     PPTP=/usr/sbin/pppd
     #
     # If logging is desired, define the path to the logfile here.  Otherwise,
     # set this variable to empty.
     #
     #LOG_FILE=""
     LOG_FILE="/var/log/vpnconnect"
     #
     # Start/stop scripts used to configure/deconfigure the OpenVPN tunnel
     # after it is brought up or before it is shut down.
     #
     # Typically, these scripts can add or remove rules with iptables to
     # control the flow of packets to/from the tunnel, for example by marking
     # packets from one or more IP addresses for tunneling or alternately only
     # by marking certain types of traffic (e.g. all sendmail traffic).  The
     # added rules can also be used to firewall the VPN tunnel, in case the
     # other end of the tunnel leads to a non-secure location.
     #
     # These scripts are also frequently tasked with setting up special routing
     # tables that direct marked packets down the VPN tunnel for deliver to the
     # remote location.
     #
     # Note that, through clever coding, you may be able to use a single script
     # to handle both the tunnel up and tunnel down events.
     #
     # Also note that there is an equivalent script to the OpenVPN script or
     # scripts chosen here but it is not chosen by parameters within this script.
     # Rather the PPP local start/stop scripts ip-up.local and ip-down.local call
     # it directly and its name is hard-coded therein.  By convention, the name
     # of the script is /etc/ppp/vpn-updown.
     #
     TUNUP="/etc/openvpn/tun-updown"
     TUNDOWN="/etc/openvpn/tun-updown"
     #
     # OpenVPN configuration file for the remote VPN server or servers.  This
     # file lists all of the tunnels that are possible and contains the
     # parameters that give the connection password, certificates and keys,
     # along with the tunnel configuration information.
     #
     # Note that this script assumes that the config file name ends with ".conf"
     # and that the full path name to the file is given below.
     #
     #TUNCONFIG="/etc/openvpn/Hostizzle.conf"
     TUNCONFIG="/etc/openvpn/Cryptocloud.conf"
     #
     # Name of PPTP tunnel.  This name actually selects the tunnel configuration
     # file from the /etc/ppp/peers directory.
     #
     VPNNAME=ipredator-tunnel
     #
     # Load the function library if it exists.
     #
     if [ -f /etc/rc.d/init.d/functions ]; then
         . /etc/rc.d/init.d/functions
     fi
     #
     # Source networking configuration.
     #
     if [ -f /etc/sysconfig/network ]; then
         . /etc/sysconfig/network
     else
         NETWORKING="no"
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ]; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Check that networking is up.
     #
     if [ ${NETWORKING} = "no" ]; then
         exit 0
     fi
     #
     # If this isn't the primary server in the cluster or this isn't a standalone
     # server, we're outta here.
     #
     if [ x"$SERVERROLE" != xPrimary ] && [ x"$SERVERROLE" != xStandalone ]; then
         exit 0
     fi
     #
     # Get the name of the VPN connection from the config file.
     #
     TUNNAME="generic"
     if [ x"$VPNCONN" == xOpenVPN ]; then
         TUNNAME=`echo ${TUNCONFIG} | /bin/grep -e "\/[-_0-9A-Za-z]\+\.conf" -o | \
             /bin/grep -e "\/[-_0-9A-Za-z]\+" -o`
         TUNNAME=${TUNNAME:1}
         PIDFILE="/var/lock/subsys/vpnconnect.${TUNNAME}"
     fi
     if [ x"$VPNCONN" == xPPTP ]; then
         TUNNAME=${VPNNAME}
      if echo ${TUNNAME} | /bin/grep -e "-tunnel\$" ; then
          TUNNAME=${TUNNAME:0:${#TUNNAME}-7}
      fi
      PIDFILE="/var/run/ppp-${TUNNAME}.pid"

fi
#
# Routine to start up the VPN connection. #
start()

      {
      if [ ! -f /var/lock/subsys/vpnconnect.${TUNNAME} ]; then
          #
          # Bring up the VPN connection.  We redirect the output depending
          # on whether the user wants a logfile or not.
          #
          echo -n "Bringing up a VPN connection to $TUNNAME "
          if [ -z "$LOG_FILE" ]; then
              #
              # Bring up an OpenVPN connection without a log file.
              #
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  ${OPENVPN} --route-noexec --script-security 2 \
                      --up ${TUNUP} --down ${TUNDOWN} --down-pre \
                      --config ${TUNCONFIG} >/dev/null 2>&1 &
              fi
              #
              # Bring up a PPTP connection without a log file.
              #
              if [ x"$VPNCONN" == xPPTP ]; then
                  ${PPTP} call mytunnel mtu 1435 mru 1435 \
                      linkname ${TUNNAME} >/dev/null 2>&1
              fi
          else
              #
              # Bring up an OpenVPN connection with a log file.
              #
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  ${OPENVPN} --route-noexec --script-security 2 \
                      --up ${TUNUP} --down ${TUNDOWN} --down-pre \
                      --config ${TUNCONFIG} >>${LOG_FILE} 2>&1 &
              fi
              #
              # Bring up a PPTP connection with a log file.
              #
              if [ x"$VPNCONN" == xPPTP ]; then
                  ${PPTP} call mytunnel mtu 1435 mru 1435 \
                      linkname ${TUNNAME} logfile ${LOG_FILE} >/dev/null 2>&1
              fi
          fi
          #
          # If everything went OK, create a PID file.
          #
          if [ $? = 0 ]; then
              echo_success
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  echo $! >${PIDFILE}
              fi
          else
              echo_failure
          fi
          echo ""
      fi
      }

#
# Routine to stop the VPN connection. #
stop()

      {
      #
      # If the VPN connection is up, shut it down.  Sending OpenVPN a SIGTERM
      # (signal 15) causes it to gracefully shut down.
      #
      if [ -f ${PIDFILE} ]; then
          echo -n "Shutting down the VPN connection to $TUNNAME "
          TUNPID=`cat ${PIDFILE}`
  #       kill -15 $TUNPID >/dev/null 2>&1
          killproc -p ${PIDFILE}
          #
          # If everything went OK, delete the PID file.
          #
          if [ $? = 0 ]; then
              rm -f ${PIDFILE} >/dev/null 2>&1
  #           echo_success
          #
          # Otherwise, if the PID doesn't exist, ditch the PID file.
          #
          else
              if ! checkpid $TUNPID ; then
                  rm -f ${PIDFILE} >/dev/null 2>&1
              fi
              echo_failure
          fi
          echo ""
      fi
      }

#
# Based on which operation we were asked to perform, have at it. #
case "$1" in

      #
      # Fire up the VPN connection.
      #
      start)
          start
          ;;
      #
      # Bye, bye VPN connection.
      #
      stop)
          stop
          ;;
      #
      # Refresh the VPN connection.
      #
      restart)
          echo "Restarting the VPN connection to $TUNNAME"
          stop
          start
          ;;
      #
      # Waaaaa 'sappenin'?
      #
      status)
          if [ -f ${PIDFILE} ]; then
              echo "Connected to $TUNNAME via VPN"
          else
              echo "No VPN connection to $TUNNAME"
          fi
          ;;
      #
      # Help text.
      #
      *)
          echo "Usage: vpnconnect {start|stop|restart|status}"
          exit 1

esac
#
# Heading home.
#
exit 0

Once we have the script created, we need to give it execute permissions and make sure that it is started/stopped at the proper runlevels:

     su
     chown root:root /etc/init.d/vpnconnect
     chmod ugo+x /etc/init.d/vpnconnect
     /sbin/chkconfig --add vpnconnect
     /sbin/chkconfig vpnconnect on
     /sbin/chkconfig --list vpnconnect

You should see a result that looks like this:

     vpnconnect      0:off   1:off   2:on    3:on    4:on    5:on    6:off

Incidentally, although this script will establish the OpenVPN tunnel properly and ensure the smooth flow of traffic, just in case you need them, here are a few useful commands for debugging or just observing the operation of the tunnel and the packet filters:

     /usr/local/sbin/openvpn --route-noexec --script-security 2 \
         --up /etc/openvpn/tun-updown --down /etc/openvpn/tun-updown \
         --down-pre --config /etc/openvpn/mytunnel.conf
     /sbin/iptables -v [-t (nat|filter|mangle)] -L [NAME]
     /sbin/iptables -v -t filter -L INPUT
     /sbin/iptables -v -t filter -L FORWARD
     /sbin/iptables -v -t filter -L VPN_CHK
     /sbin/iptables -v -t nat -L POSTROUTING
     /sbin/ip route list table vpn.tunnel
     cat /proc/sys/net/ipv4/conf/*/rp_filter
     dmesg | tail -20

Once everything is working properly and the OpenVPN tunnel is started and stopped automatically (you can verify this under real operating circumstances by rebooting the system or you can just check that "vpnconnect start" and "vpnconnect stop" work from the command line), the final step is to set up logrotate to rotate the logfiles.

You can add the log file named above to an individual configuration file in the logrotate directory or include the following lines in the /etc/logrotate.conf file itself. We prefer to use the individual configuration file so that setting up logrotate can be accomplished by simply dropping the file into the logrotate directory, just like the various install programs do.

/etc/logrotate.d/vpnconnect:

     /var/log/vpnconnect
         missingok
         notifempty
         copytruncate
     }