OpenVPN Cascading - Anyone ever done it?

This forum is for general conversation and user-user networking.
Post Reply
tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Mon Mar 08, 2021 6:59 pm

Hello @TinCanTech & community, hope your are doing alright and big thanks in advance! :)

I would like to enable clients of my OpenVPN server(s) to use cascading, both chained and nested.

Is there anything specific I need to add to my server configs, or can all clients achieve this by simply modifying their own configs?

As far as I understand, on Linux they would only have to connect via two separate terminals, while adding "route-nopull" & slightly changing the routes of the 2nd (subsequent) configs, but I am not sure how I can achieve this globally.

I found this script and will see if it works.


Script:

Code: Select all

[code]#!/bin/bash
#
# This script can be used as an up and down script for OpenVPN.
# It adds and removes the routes required for a VPN connection cascaded over
# multiple vpn servers
#
# Usage:
#   Start OpenVPN with the following options:
#
#   openvpn
#       --config <config file>
#       --script-security 2
#       --route remote_host
#       --persist-tun
#       --up updown.sh
#       --down updown.sh
#       --route-noexec
#       --setenv hopid <hop number>
#       --setenv prevgw <gateway>
#
#       config file
#           The *.conf config file, ie. "London.conf".
#       hop number
#           The number of the hop in the cascading chain, starting with 1.
#           For the first hop, you can omit the hop number (default: 1).
#           It is limited to 5, to limit the amount of total routes.
#       gateway
#          The gateway (internal server IP) of the previous hop as provided
#          in the log of the previous hop.
#          For the first hop ('hop number' = 1), you can omit the gateway.
#          In that case, the gateway of your local network will be used.
#
#   TL;DR: Just start openvpn with all parameters listed above except the last
#   two (--setenv). You will find the command line for the next hop in the log,
#	you will just need to change <config.conf> for the configuration file of 
#	the server of your choice.
#
# Example:
#   Hop #1:
#     sudo openvpn --config London.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#
#   Hop #2 (copy-paste this command line from the log of the hop #1):
#     sudo openvpn --config Rotterdam.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#         --setenv hopid 2 --setenv prevgw 10.1.13.1
#
#   Hop #3 (copy-paste from hop #2):
#     sudo openvpn --config Reykjavik.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#         --setenv hopid 3 --setenv prevgw 10.18.12.1
#
# NOTE:
#	We recommend using a dedicated terminal window for each hop. That way the log
#	outputs for each hop can be easily kept apart. It also guarantees that the 
#	routes can be added and removed again in the right order.
#
# Advanced Options:
#   --setenv disable_resolvconf 1
#      By default, this script executes '/etc/openvpn/update-resolv-conf'
#      to update your DNS settings. You can disable this behaviour by
#      setting the environment variable 'disable_resolvconf'.
#   --setenv redirect_output /path/to/file
#      Redirect stdout and stderr to file.
#
# 
#
# 
#
# Changelog:
#   1.0
#     * initial version
#   1.1
#     * nice log output and hint for the next connection
#     * you can omit the --setenv parameter for the first hop
#   1.2
#     * backwards compatible to openvpn version 2.2
#       (uses --up and --down instead of --route-up and --route-pre-down)
#     * two environment variables for hopid and prevgw instead of one
#     * use iproute2 (ip route add) instead of net-tools suite (route add)
#   1.3
#     * added /etc/openvpn/update-resolv-conf (can be disabled by
#       --setenv disable_resolvconf 1)
#   1.4
#     * added ability to redirect stdout and stderr to file
#       (--setenv redirect_output /path/to/file)
#   1.5
#     * added support for IPv6

VERSION="1.5"

# remember script name
script_name=$0

# maximum number of hops
# be careful: number of additional routes per hop: 2^hopid + 1
MAX_HOPID=5


# print $1 with prefix to STDOUT
function updown_print {
    echo "updown.sh: $1"
}


# print $1 with prefix to STDERR
function updown_print_error {
    echo "updown.sh: ERROR: $1">&2
}


# check whether $1 is an IPv4 address (exit on error)
# $1: the IP address to check
# $2: the environment variable name for error message
function check_ipv4_regex {
    regex_255="([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])" # 0-255
    regex_ipv4="^($regex_255.){3}$regex_255$"
    if ! [[ $1 =~ $regex_ipv4 ]]
    then
        updown_print_error "$2 ('$1') is not an IPv4 address"
        updown_print "ABORT"
        exit 1
    fi
}

# redirect output if $redirect_output is set
if ! [ "$redirect_output" == "" ]
then
    exec > "$redirect_output"
    exec 2>&1
fi

updown_print "STARTED"
updown_print "hop number:              $hopid (default: 1)"
updown_print "gateway of previous hop: $prevgw (default: local gateway)"
updown_print "local gateway:           $route_net_gateway"
updown_print "VPN: int. IP address:    $ifconfig_local"
updown_print "VPN: netmask:            $ifconfig_netmask"
updown_print "VPN: gateway:            $route_vpn_gateway"
updown_print "VPN: public IP address:  $route_network_1"

# if hopid is not set, assume it to be 1
if [[ ${hopid} == "" ]]
then
    updown_print "Notice: You didn't set 'hopid'. Assuming this to be the first hop (hopid=1)."
    hopid=1
fi

# check whether environment variable hopid is a number
regex_number="^[0-9]+$"
if ! [[ ${hopid} =~ $regex_number ]]
then
    updown_print_error "hopid ('$hopid') is not a number!"
    updown_print_error "See updown.sh for the usage of this script."
    updown_print "ABORT"
    exit 1
fi

# check whether hopid <= MAX_HOPID
if [[ ${hopid} -gt ${MAX_HOPID} ]]
then
    updown_print_error "You shouldn't use more than $MAX_HOPID hops. Otherwise it will result in a massive amount of routes."
    updown_print "ABORT"
    exit 1
fi

# check whether all environment variables needed are IPv4 addresses
check_ipv4_regex ${route_vpn_gateway} "route_vpn_gateway"
check_ipv4_regex ${route_network_1} "route_network_1"
vpn_server_ip=${route_network_1}

# make sure we have a valid gateway
# (prevgw is the route_vpn_gateway from the previous hop)
if [[ ${prevgw} == "" ]]
then
    if [[ ${hopid} -eq 1 ]]
    then
        # for the first hop, use the local gateway
        updown_print "Notice: You didn't set the previous gateway. The gateway of your local network ('$route_net_gateway') will be used."
        prevgw=${route_net_gateway}
    else
        updown_print_error "You didn't set the previous gateway."
        updown_print_error "See updown.sh for the usage of this script."
    fi
fi
check_ipv4_regex ${prevgw} "prevgw"

# determine whether to add or del our routes
if [[ "$script_type" == "up" ]]
then
    add_del="add"
elif [[ "$script_type" == "down" ]]
then
    add_del="delete"
else
    updown_print_error "script_type is not 'up' or 'down'!"
    updown_print_error "See updown.sh for the usage of this script."
    updown_print "ABORT"
    exit 1
fi

# add route to (next) vpn server via previous gateway
IP=$(which ip)
route_cmd="$IP route $add_del $vpn_server_ip via $prevgw"
updown_print "executing: '$route_cmd'"
eval ${route_cmd}

# calculate and execute IPv4 routes
for (( i=0; i < $((2 ** $hopid)); i++ ))
do
    net="$(( $i << (8 - $hopid) )).0.0.0/$hopid"
    route_cmd="$IP route $add_del $net via $route_vpn_gateway"
    updown_print "executing: '$route_cmd'"
    eval ${route_cmd}
done

# IPv6
for (( i=0; i < $((2 ** $hopid)); i++ ))
do
    net=$(printf "%X::/$((3 + $hopid))" $(( 0x2000 + ( $i << (13 - $hopid) ) )))
    route_cmd="$IP -6 route $add_del $net dev $dev"
    updown_print "executing: '$route_cmd'"
    eval ${route_cmd}
done

# print hint for the next connection (on up)
if [[ "$script_type" == "up" ]]
then
    if [[ ${hopid} -le ${MAX_HOPID} ]]
    then
        next_hop_number=$((hopid+1))
        next_gateway=${route_vpn_gateway}
        updown_print "HINT: For the next hop, start openvpn with the following options:"
        updown_print "HINT: openvpn --config <config.conf> --script-security 2 --route remote_host --persist-tun --up $script_name --down $script_name --route-noexec --setenv hopid $next_hop_number --setenv prevgw $next_gateway"
    else
        updown_print "Notice: Maximum numbers of hops reached. Don't start another connection."
    fi
fi

# update DNS settings
if ! [ "$disable_resolvconf" ]
then
    resolvconf_cmd="/etc/openvpn/update-resolv-conf"
    updown_print "execuding: '$resolvconf_cmd'"
    eval ${resolvconf_cmd}
fi

updown_print "FINISHED"
My configs are pretty straightforward:

Client:

Code: Select all

auth-user-pass auth.txt
remote  ***.***.***.***
ca auth_cert.crt
proto udp4
remote-cert-tls server
client
resolv-retry infinite
nobind
dev tun
persist-tun
persist-key
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
Server:

Code: Select all

proto udp
dev tun
ca auth_cert.crt
cert server.crt
key server.key 
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100
server-bridge
push "route 192.168.10.0 255.255.255.0"
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
tls-auth ta.key 0
compress lz4-v2
push "compress lz4-v2"
comp-lzo
persist-key
persist-tun
verb 3
explicit-exit-notify 1
Last edited by tunnel_lemur on Tue Mar 09, 2021 5:54 pm, edited 5 times in total.

User avatar
TinCanTech
OpenVPN Protagonist
Posts: 8968
Joined: Fri Jun 03, 2016 1:17 pm

Re: OpenVPN Multi Hops - Anybody?

Post by TinCanTech » Tue Mar 09, 2021 12:29 am

This is completely beyond the scope of Openvpn.

I will cut to the chase ..
  • You do not need it.
  • If you really did need it then you would know how to do it.

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Multi Hops - Anybody?

Post by tunnel_lemur » Tue Mar 09, 2021 7:27 am

Hey there!

I'm not referring to anything crazy, just a simple 2-way hop between two OpenVPN servers. Surely, there must be some official documentation, a forum thread on here or at least a failed attempt somewhere? (Haven't found anything yet)

Anyway, this script appears to work, but I would prefer something official from you guys, tbh.
I'm currently trying to understand how it works, while researching certain flags in the docs, but if you can find the time, I'd really appreciate your input on this.

(Unofficial 3rd party)Script enabling multi-hops with OpenVPN:

Code: Select all

#!/bin/bash
#
# This script can be used as an up and down script for OpenVPN.
# It adds and removes the routes required for a VPN connection cascaded over
# multiple vpn servers
#
# Usage:
#   Start OpenVPN with the following options:
#
#   openvpn
#       --config <config file>
#       --script-security 2
#       --route remote_host
#       --persist-tun
#       --up updown.sh
#       --down updown.sh
#       --route-noexec
#       --setenv hopid <hop number>
#       --setenv prevgw <gateway>
#
#       config file
#           The *.conf config file, ie. "London.conf".
#       hop number
#           The number of the hop in the cascading chain, starting with 1.
#           For the first hop, you can omit the hop number (default: 1).
#           It is limited to 5, to limit the amount of total routes.
#       gateway
#          The gateway (internal server IP) of the previous hop as provided
#          in the log of the previous hop.
#          For the first hop ('hop number' = 1), you can omit the gateway.
#          In that case, the gateway of your local network will be used.
#
#   TL;DR: Just start openvpn with all parameters listed above except the last
#   two (--setenv). You will find the command line for the next hop in the log,
#	you will just need to change <config.conf> for the configuration file of 
#	the server of your choice.
#
# Example:
#   Hop #1:
#     sudo openvpn --config London.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#
#   Hop #2 (copy-paste this command line from the log of the hop #1):
#     sudo openvpn --config Rotterdam.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#         --setenv hopid 2 --setenv prevgw 10.1.13.1
#
#   Hop #3 (copy-paste from hop #2):
#     sudo openvpn --config Reykjavik.conf --script-security 2 --route remote_host
#         --persist-tun --up updown.sh --down updown.sh --route-noexec
#         --setenv hopid 3 --setenv prevgw 10.18.12.1
#
# NOTE:
#	We recommend using a dedicated terminal window for each hop. That way the log
#	outputs for each hop can be easily kept apart. It also guarantees that the 
#	routes can be added and removed again in the right order.
#
# Advanced Options:
#   --setenv disable_resolvconf 1
#      By default, this script executes '/etc/openvpn/update-resolv-conf'
#      to update your DNS settings. You can disable this behaviour by
#      setting the environment variable 'disable_resolvconf'.
#   --setenv redirect_output /path/to/file
#      Redirect stdout and stderr to file.
#
# 
#
# 
#
# Changelog:
#   1.0
#     * initial version
#   1.1
#     * nice log output and hint for the next connection
#     * you can omit the --setenv parameter for the first hop
#   1.2
#     * backwards compatible to openvpn version 2.2
#       (uses --up and --down instead of --route-up and --route-pre-down)
#     * two environment variables for hopid and prevgw instead of one
#     * use iproute2 (ip route add) instead of net-tools suite (route add)
#   1.3
#     * added /etc/openvpn/update-resolv-conf (can be disabled by
#       --setenv disable_resolvconf 1)
#   1.4
#     * added ability to redirect stdout and stderr to file
#       (--setenv redirect_output /path/to/file)
#   1.5
#     * added support for IPv6

VERSION="1.5"

# remember script name
script_name=$0

# maximum number of hops
# be careful: number of additional routes per hop: 2^hopid + 1
MAX_HOPID=5


# print $1 with prefix to STDOUT
function updown_print {
    echo "updown.sh: $1"
}


# print $1 with prefix to STDERR
function updown_print_error {
    echo "updown.sh: ERROR: $1">&2
}


# check whether $1 is an IPv4 address (exit on error)
# $1: the IP address to check
# $2: the environment variable name for error message
function check_ipv4_regex {
    regex_255="([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])" # 0-255
    regex_ipv4="^($regex_255.){3}$regex_255$"
    if ! [[ $1 =~ $regex_ipv4 ]]
    then
        updown_print_error "$2 ('$1') is not an IPv4 address"
        updown_print "ABORT"
        exit 1
    fi
}

# redirect output if $redirect_output is set
if ! [ "$redirect_output" == "" ]
then
    exec > "$redirect_output"
    exec 2>&1
fi

updown_print "STARTED"
updown_print "hop number:              $hopid (default: 1)"
updown_print "gateway of previous hop: $prevgw (default: local gateway)"
updown_print "local gateway:           $route_net_gateway"
updown_print "VPN: int. IP address:    $ifconfig_local"
updown_print "VPN: netmask:            $ifconfig_netmask"
updown_print "VPN: gateway:            $route_vpn_gateway"
updown_print "VPN: public IP address:  $route_network_1"

# if hopid is not set, assume it to be 1
if [[ ${hopid} == "" ]]
then
    updown_print "Notice: You didn't set 'hopid'. Assuming this to be the first hop (hopid=1)."
    hopid=1
fi

# check whether environment variable hopid is a number
regex_number="^[0-9]+$"
if ! [[ ${hopid} =~ $regex_number ]]
then
    updown_print_error "hopid ('$hopid') is not a number!"
    updown_print_error "See updown.sh for the usage of this script."
    updown_print "ABORT"
    exit 1
fi

# check whether hopid <= MAX_HOPID
if [[ ${hopid} -gt ${MAX_HOPID} ]]
then
    updown_print_error "You shouldn't use more than $MAX_HOPID hops. Otherwise it will result in a massive amount of routes."
    updown_print "ABORT"
    exit 1
fi

# check whether all environment variables needed are IPv4 addresses
check_ipv4_regex ${route_vpn_gateway} "route_vpn_gateway"
check_ipv4_regex ${route_network_1} "route_network_1"
vpn_server_ip=${route_network_1}

# make sure we have a valid gateway
# (prevgw is the route_vpn_gateway from the previous hop)
if [[ ${prevgw} == "" ]]
then
    if [[ ${hopid} -eq 1 ]]
    then
        # for the first hop, use the local gateway
        updown_print "Notice: You didn't set the previous gateway. The gateway of your local network ('$route_net_gateway') will be used."
        prevgw=${route_net_gateway}
    else
        updown_print_error "You didn't set the previous gateway."
        updown_print_error "See updown.sh for the usage of this script."
    fi
fi
check_ipv4_regex ${prevgw} "prevgw"

# determine whether to add or del our routes
if [[ "$script_type" == "up" ]]
then
    add_del="add"
elif [[ "$script_type" == "down" ]]
then
    add_del="delete"
else
    updown_print_error "script_type is not 'up' or 'down'!"
    updown_print_error "See updown.sh for the usage of this script."
    updown_print "ABORT"
    exit 1
fi

# add route to (next) vpn server via previous gateway
IP=$(which ip)
route_cmd="$IP route $add_del $vpn_server_ip via $prevgw"
updown_print "executing: '$route_cmd'"
eval ${route_cmd}

# calculate and execute IPv4 routes
for (( i=0; i < $((2 ** $hopid)); i++ ))
do
    net="$(( $i << (8 - $hopid) )).0.0.0/$hopid"
    route_cmd="$IP route $add_del $net via $route_vpn_gateway"
    updown_print "executing: '$route_cmd'"
    eval ${route_cmd}
done

# IPv6
for (( i=0; i < $((2 ** $hopid)); i++ ))
do
    net=$(printf "%X::/$((3 + $hopid))" $(( 0x2000 + ( $i << (13 - $hopid) ) )))
    route_cmd="$IP -6 route $add_del $net dev $dev"
    updown_print "executing: '$route_cmd'"
    eval ${route_cmd}
done

# print hint for the next connection (on up)
if [[ "$script_type" == "up" ]]
then
    if [[ ${hopid} -le ${MAX_HOPID} ]]
    then
        next_hop_number=$((hopid+1))
        next_gateway=${route_vpn_gateway}
        updown_print "HINT: For the next hop, start openvpn with the following options:"
        updown_print "HINT: openvpn --config <config.conf> --script-security 2 --route remote_host --persist-tun --up $script_name --down $script_name --route-noexec --setenv hopid $next_hop_number --setenv prevgw $next_gateway"
    else
        updown_print "Notice: Maximum numbers of hops reached. Don't start another connection."
    fi
fi

# update DNS settings
if ! [ "$disable_resolvconf" ]
then
    resolvconf_cmd="/etc/openvpn/update-resolv-conf"
    updown_print "execuding: '$resolvconf_cmd'"
    eval ${resolvconf_cmd}
fi

updown_print "FINISHED"

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Tue Mar 09, 2021 5:55 pm

Would appreciate some other user's experience with this. Come on guys, it's not that rare.

User avatar
TinCanTech
OpenVPN Protagonist
Posts: 8968
Joined: Fri Jun 03, 2016 1:17 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by TinCanTech » Tue Mar 09, 2021 6:28 pm

What is your use case ?

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Tue Mar 09, 2021 7:15 pm

My partner lives in HongKong and supports local activists/students. Since I have some experience with Linux, I promised to help out by building a live system with VPN(s) and Tor preinstalled. I've already set up two OpenVPN servers via VPS and everything works fine. Now I just want the client to use both servers in a 2-way hop configuration to improve redundancy. If everything goes well, I'd like to offer a polished version that will be available for free on campus, using crypto-crowdfunding for subscription fees.

The script I posted earlier appears to work, but I am not sure if it's the most efficient and reliable way to achieve this. Especially, since there seems to be no official documentation for multi-hopping. I'm just a bit anxious, because I really don't want to make any mistakes here or use untrusted sources.

Would be cool if you could share some knowledge on this if time permits.

User avatar
TinCanTech
OpenVPN Protagonist
Posts: 8968
Joined: Fri Jun 03, 2016 1:17 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by TinCanTech » Tue Mar 09, 2021 8:19 pm

Your plan is ambitious and requires that you do your research properly.
Otherwise, you will not understand what you are doing.

Selling something which you don't understand ... is why people never trust the salesman.

If you so choose then you can contact me privately but fees will apply.
tincanteksup <at> gmail

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Tue Mar 09, 2021 9:57 pm

Yeah, there are many providers who offer cascading as a built-in feature, but I distrust proprietary solutions and would rather do it all myself via open source.

You are right, even though I won't be selling the build, rather than allowing people to crowdfund the subscription fees for their own servers, I want to know exactly what every single line of code does, before I ship it out to anybody, especially in this case.

I'd like to do as much research as I possibly can before bothering you further, so that we can communicate on a somewhat even level about this.

Gonna shoot you a message for sure, in the meantime could you share your thoughts about the aforementioned script? I'd just like to know if they are on the right track, which would help my research.

User avatar
TinCanTech
OpenVPN Protagonist
Posts: 8968
Joined: Fri Jun 03, 2016 1:17 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by TinCanTech » Thu Mar 11, 2021 4:21 am

Here is a deal that I offer:
  • You help me and I'll try to help you.
How does that sound ?

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Thu Mar 11, 2021 9:12 pm

No thanks, I've already figured it out.
After some extensive research I was able to verify the legitimacy of that code myself.

To anyone else who may find themselves in a similar situation, simply make sure that you use --route-noexec or a pull-filter from within your client config to ignore the server pushed routes, don't forget --setenv for the previous hop's gateway and definitely be mindful of including paths for /usr/sbin/ & /usr/bin on Ubuntu >20.4. You can pretty much use any of the currently available cascading scripts on github, but in my opinion loeken's contrib has the most online/forum presence and appears to be one of the cleanest and easiest work with, considering that the majority of other maintainers have simply forked his script. If you have ipv6 disabled, you'll need to remove/comment lines 127 - 129, otherwise ipv6 routes will be erroneously assigned and it exits. As it is, this script produces hops in series, i.e chained. If you specifically want them to be nested, you'll have to choose one of the many other options, however this is apparently not advisable from a security standpoint.
.

Code: Select all

#!/bin/bash
#
# This script called updown.sh can be used to create a cascading connection to multiple vpn servers
# You basically start a vpn connection to multiple servers and this script will create routes so it will create a 
# proxy chain for you
#
#
# Example:
# 1:
# sudo openvpn --config eu.fr1.cdn.internetz.me.ovpn --script-security 2 --route remote_host --persist-tun --up updown.sh --down updown.sh --route-noexec
#
# 2 ( script nr 1 will output this command for you
# sudo openvpn --config eu.fr4.cdn.internetz.me.ovpn --script-security 2 --route remote_host --persist-tun --up updown.sh --down updown.sh --route-noexec --setenv hopid 2 --setenv prevgw 10.9.1.1
#
# 3
# sudo openvpn --config am.us4.cdn.internetz.me.ovpn --script-security 2 --route remote_host --persist-tun --up updown.sh --down updown.sh --route-noexec --setenv hopid 3 --setenv prevgw 10.9


# save the name of this script to a variable ( intended name updown.sh )
script_name=$0

# maximum number of hops
# be careful: number of additional routes per hop: 2^($hopid) + 1
MAX_HOPID=5

# disabling resolveconf cause most users are on systems such as kali
disable_resolvconf=1

# all messages from this script start with ##
# print $1 with prefix to STDOUT
function updown_output {
    echo "## updown.sh: $1"
}

# all messages from this script start with ##
# print $1 with prefix to STDERR
function updown_output_error {
    echo "## updown.sh: ERROR: $1">&2
}


# check whether $1 is an IPv4 address (exit on error)
# $1: the IP address to check
# $2: the environment variable name for error message
function check_ipv4_regex {
    regex_255="([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])" # 0-255
    regex_ipv4="^($regex_255.){3}$regex_255$"
    if ! [[ $1 =~ $regex_ipv4 ]]
    then
        updown_output_error "$2 ('$1') is not an IPv4 address"
        updown_output "ABORT"
        exit 1
    fi
}


updown_output "STARTED"
updown_output "hop id:              	$hopid (default: 1)"
updown_output "gateway of last hop: 	$prevgw (default: local gateway)"
updown_output "local gateway:           $route_net_gateway"
updown_output "VPN: local IP address:   $ifconfig_local"
updown_output "VPN: local netmask:      $ifconfig_netmask"
updown_output "VPN: local gateway:      $route_vpn_gateway"
updown_output "VPN: vpn IP address:     $route_network_1"

# if hopid is not set, assume it to be 1
if [[ ${hopid} == "" ]]
then
    updown_output "Notice: You didn't set 'hopid'. Assuming this to be the first hop (hopid=1)."
    hopid=1
fi

# check whether environment variable hopid is a number
regex_number="^[0-9]+$"
if ! [[ ${hopid} =~ $regex_number ]]
then
    updown_output_error "hopid ('$hopid') is not a number!"
    updown_output_error "See updown.sh for the usage of this script."
    updown_output "ABORT"
    exit 1
fi

# check whether hopid <= MAX_HOPID
if [[ ${hopid} -gt ${MAX_HOPID} ]]
then
    updown_output_error "Do not use more than $MAX_HOPID hops."
    updown_output "ABORT"
    exit 1
fi

# check whether all environment variables needed are IPv4 addresses
check_ipv4_regex ${route_vpn_gateway} "route_vpn_gateway"
check_ipv4_regex ${route_network_1} "route_network_1"
vpn_server_ip=${route_network_1}

# make sure we have a valid gateway
# (prevgw is the route_vpn_gateway from the previous hop)
if [[ ${prevgw} == "" ]]
then
    if [[ ${hopid} -eq 1 ]]
    then
        # for the first hop, use the local gateway
        updown_output "Notice: You didn't set the previous gateway. The gateway of your local network ('$route_net_gateway') will be used."
        prevgw=${route_net_gateway}
    else
        updown_output_error "You didn't set the previous gateway."
        updown_output_error "See updown.sh for the usage of this script."
    fi
fi
check_ipv4_regex ${prevgw} "prevgw"

# determine whether to add or del our routes
if [[ "$script_type" == "up" ]]
then
    add_del="add"
elif [[ "$script_type" == "down" ]]
then
    add_del="delete"
else
    updown_output_error "script_type is not 'up' or 'down'!"
    updown_output_error "See updown.sh for the usage of this script."
    updown_output "ABORT"
    exit 1
fi

# add route to (next) vpn server via previous gateway
IP=$(which ip)
route_cmd="$IP route $add_del $vpn_server_ip via $prevgw"
updown_output "executing: '$route_cmd'"
eval ${route_cmd}

# calculate and execute routes
for (( i=0; i < $((2 ** $hopid)); i++ ))
do
    net="$(( $i << $((8 - $hopid)) )).0.0.0"
    route_cmd="$IP route $add_del $net/$hopid via $route_vpn_gateway"
    updown_output "executing: '$route_cmd'"
    eval ${route_cmd}
done

# print hint for the next connection (on up)
if [[ "$script_type" == "up" ]]
then
    if [[ ${hopid} -le ${MAX_HOPID} ]]
    then
        next_hop_number=$((hopid+1))
        next_gateway=${route_vpn_gateway}
        updown_output "HINT: For the next hop, start openvpn with the following options:"
        updown_output "HINT: openvpn --config <config.ovpn> --script-security 2 --route remote_host --persist-tun --up $script_name --down $script_name --route-noexec --setenv hopid $next_hop_number --setenv prevgw $next_gateway"
    else
        updown_output "Notice: Maximum numbers of hops reached. Don't start another connection."
    fi
fi

# update DNS settings
if ! [ "$disable_resolvconf" ]
then
    resolvconf_cmd="/etc/openvpn/update-resolv-conf"
    updown_output "execuding: '$resolvconf_cmd'"
    eval ${resolvconf_cmd}
fi

updown_output "FINISHED"
https://github.com/loeken/CascadingOpenvpnConnect

Respectfully, I understand the position you're in, given that you unconditionally and gratuitously offer 24/7 support to any lazy dummy who could probably answer their own question faster and more effectively with a 1 min google search, however in this particular case I believe a subtle hint, that the script is not only perfectly fine, but also the universally accepted method to achieve cascading with OpenVPN, could have been made without this rather aggressive ploy for compensation.

It's an open source issue, raised on an open source platform, received by an open source specialist.
The second you saw this, you already knew that the script was more than adequate. I think in the spirit of open source, it would have been more appropriate to leave a few words, at least indicative of the solution, as you usually do, instead of depicting this as an issue far too advanced for the forums, only to be solved privately and in exchange for a fee.

I am doing this whole project out of the goodness of my heart and because I truly believe information should be free. It is exactly this ideal, that our entire community is predicated upon. This advanced and delicate knowledge of surfing the web unencumbered may not mean much to us, but to people in less progressive countries it is a crucial means to and literal manifestation of freedom.

I would have happily made a donation to your github page or whatever project you are currently working on, for the simple fact that you are obviously extremely knowledgeable and helpful to so many countless people on here.

Just a little disappointed by the way this was handled.

User avatar
TinCanTech
OpenVPN Protagonist
Posts: 8968
Joined: Fri Jun 03, 2016 1:17 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by TinCanTech » Thu Mar 11, 2021 9:34 pm

tunnel_lemur wrote:
Thu Mar 11, 2021 9:12 pm
No thanks, I've already figured it out.
After some extensive research I was able to verify the legitimacy of that code myself.
OK, that is your job, in this case, as I stated with reasoning.
tunnel_lemur wrote:
Thu Mar 11, 2021 9:12 pm
you already knew full and well that the script was more than adequate
No I did not. I don't have time to debug other peoples code, unless I am motivated for what-ever reason..

If you find you have trouble with DPI firewalls then this project may have a partial solution:
https://github.com/TinCanTech/easy-tls (See "unlimited Client TLS-Crypt-V2 keys")
tunnel_lemur wrote:
Thu Mar 11, 2021 9:12 pm
Just a little disappointed by the way this was handled
As you point out, I live in the Free world ..

And I feel that financial compensation for work as complex as your goals is perfectly justified.

(Although, I sneer derisively at anybody who thinks they live in The Free World.
Those people have their head in the sand IMPO)

tunnel_lemur
OpenVpn Newbie
Posts: 12
Joined: Fri Mar 05, 2021 4:35 pm

Re: OpenVPN Cascading - Anyone ever done it?

Post by tunnel_lemur » Thu Mar 18, 2021 9:25 am

It's fine, let's move on.

It turned out great.
6-7 hops without any issues, it's just that the routes really start piling up around > 10.
One thing that's bothering me, though.
Why can't you have two or more OpenVPN connections over the same port (on separate servers)?

Everything works perfectly as long as every client config has its own unique port for the remote server.
As soon as you have two clients with the same port, the whole thing breaks immediately.

Doesn't make sense to me. Who cares if both tunnels are configured to connect via the same port on two completely different servers?

go85
OpenVpn Newbie
Posts: 4
Joined: Sun May 02, 2021 10:31 am

Re: OpenVPN Cascading - Anyone ever done it?

Post by go85 » Tue May 04, 2021 12:16 pm

Hi @ all!

I can't get hop2 to run.

Error message: (hop2.log)
Error: Nexthop has invalid gateway.

With me the gateway changes with every new dial-in to the VPN provider.

I can get hop 1 to work with the 'updown.sh" script. Can connect to the internet.

From the hop1.log I have taken the gateway of my vpn provider1
and added it to hop 2 (--setenv hopid 2 --setenv prevgw 'gateway-ip here from hop1.log (vpn provider1)

Do I need to change anything else in the updown.sh script?

If not how do I get rid of the error message "Error: Nexthop has invalid gateway."

Please help, thanks!

go85
OpenVpn Newbie
Posts: 4
Joined: Sun May 02, 2021 10:31 am

Re: OpenVPN Cascading - Anyone ever done it?

Post by go85 » Tue May 04, 2021 12:47 pm

Great vpn chain script :-)

I got it working now.

hop 1: (created as systemd.service)
hop 2: I have to start manually.

Is there any way to get this to work with a 2nd systemd.service to hop 2 so I don't have to start hop 2 by hand?

Post Reply