Setup a VPN to protect your privacy
Last update
2023-08-24
2023
08-24
«proxy.sh vpn»

Configuration

The following instructions use the wonderful proxy.sh VPN provider: they are cheap, transparent, strongly privacy oriented, and they offer many servers, proxies and scriptable web APIs!

If you want to buy an account then click the following banner:

or just start installing and configuring the software:

1
apt-get install openvpn netselect

Create the proxysh.auth text file containing your VPN credentials:

1
2
3
mkdir -p /path/to/myvpn
cd /path/to/myvpn
echo -e "myusername\nmypassword" > proxysh.auth

Setup an openvpn config updater script, say proxysh-update.sh and substitute username, password with your VPN credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/bin/bash

OVPND=/path/to/openvpn/config

cat > $OVPND/proxysh.conf.tmp <<VPNCFG
# https://openvpn.net/index.php/open-source/documentation/manuals/65-openvpn-20x-manpage.html
client
dev   tun
proto tcp
port  443
auth            SHA512
auth-user-pass  $OVPND/proxysh.auth
cipher          AES-256-CBC
remote-cert-tls server
resolv-retry    infinite
verb            3
reneg-sec       0
route-method    exe
route-delay     2
mute            2
mute-replay-warnings
nobind
comp-lzo

# https://community.openvpn.net/openvpn/wiki/IgnoreRedirectGateway
#pull-filter     ignore redirect-gateway

# args: tun_dev tun_mtu link_mtu local_ip remote_ip [init|restart]
up-restart
script-security 2
up              $OVPND/proxysh-cmd-up.sh
#down           $OVPND/proxysh-cmd-down.sh

# ping every N seconds and restart after M without reply
keepalive       30 90

<ca>
-----BEGIN CERTIFICATE-----
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
TSMSym4Np+AmT7OX
-----END CERTIFICATE-----
</ca>

#remote-random

VPNCFG

echo -n "`date +'%F %T'`: refresh config servers... "

# get servers list with load < 80%
post_auth=`cat $OVPND/proxysh.auth | tr "\n" "#" | sed -r 's/([^#]+)#([^#]+).*/u=\1\&p=\2/'`
curl -s -X POST https://proxy.sh/api.php -d "$post_auth" | \
  sed 's/^ *//' | tr -d "\n" | sed 's/<.server>/\0\n/g' | \
  grep -Ev "Hub" | grep -Ev "load>(100|[5-9][0-9])" | \
  sed -r 's/.*ress>(.+)<.add.*load>(.+)<.server_load.*/\1/' \
  > $OVPND/proxysh.srv
# check servers length
if [ `cat $OVPND/proxysh.srv | wc -l` -eq 0 ]; then
  echo " DL ERROR!"
  exit
fi

# order servers by ping time (UDP)
sudo netselect -s 64 `cat $OVPND/proxysh.srv | tr "\n" " "` 2> /dev/null | \
  sed 's/.* /remote /' >> $OVPND/proxysh.conf.tmp

# check servers length
if [ `grep -E "remote [0-9]+" $OVPND/proxysh.conf.tmp | wc -l` -eq 0 ]; then
  echo " SORT ERROR!"
  exit
fi

# update the real config file
chmod 600 $OVPND/proxysh.conf.tmp
mv -f $OVPND/proxysh.conf.tmp $OVPND/proxysh.conf
rm -f $OVPND/proxysh.srv
echo "OK"

Create an optional proxysh-cmd-up.sh script that run after each openvpn start/restart:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# script args:  tun_dev  tun_mtu  link_mtu  local_ip  remote_ip  [init|restart]

# updating deluge listen_interface IP address
if [ -d $HOME/.config/deluge ]; then
  cd $HOME/.config/deluge

  sed -i -r 's/("listen_interface": ")(.*)(")/\1'$4'\3/' ./core.conf

  if pgrep deluged > /dev/null ; then
    sudo -u cloud deluge-console "pause  *"
    sudo -u cloud deluge-console "config -s listen_interface $4" ; sleep 3
    sudo -u cloud deluge-console "resume *"
  fi

  cd -
fi

exit 0

Create a script to start and keep running the openvpn daemon say run.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/sudo /bin/bash

[ `whoami` != "root" ] && { echo "you must be root!" && exit 1; }

cd /path/to/myvpn || { echo "pwd not found!" && exit 2; }

# read current config for later usage
tbl=10
dev=`ip link  | paste - - | grep -iE "state up.+ether" | head -n 1 | sed -r 's/[0-9]+: ([^:]+).+/\1/'` # device name
met=`ip route | grep -E "^default via.+dev $dev.+metric" | sed -r 's/^.+metric ([0-9]+)[^0-9]*/\1/'  ` # metric
sub=`ip route | grep -E ".+\/.+dev $dev .+metric $met" | sed -r 's/^([0-9.]+\/[0-9]+).+/\1/'         ` # subnet
src=`ip route | grep -E ".+\/.+dev $dev .+metric $met" | sed -r 's/.+src ([0-9.]+) .+/\1/'           ` # source
gwy=`ip route | grep -E ".+via.+dev $dev.+metric $met" | sed -r 's/.+via ([0-9.]+) .+/\1/'           ` # gateway
# echo -e "dev: $dev\nmet: $met\nsub: $sub\nsrc: $src\ngwy: $gwy"

if [ -z "$tbl" -o -z "$dev" -o -z "$met" -o -z "$sub" -o -z "$src" -o -z "$gwy" ]; then
  echo "configuration error"
  exit
fi

flg_restart="/tmp/openvpn.restart"
flg_iproute="/tmp/openvpn.routes"

del_ext_routes_rules () {
  echo "  deleting old routes..."                                                       
  ip route del default via $gwy table $tbl dev $dev # local gateway ip
  ip route del to          $sub table $tbl dev $dev # local subnet
  ip rule  del from        $src table $tbl          # local ip
} # del_ext_routes_rules

add_ext_routes_rules () {
  echo "  adding routes..."
  ip rule  add from        $src table $tbl          # local ip
  ip route add to          $sub table $tbl dev $dev # local subnet
  ip route add default via $gwy table $tbl dev $dev # local gateway ip
} # add_ext_routes_rules

# allow ssh via on non vpn address while vpn is open
# https://forums.openvpn.net/viewtopic.php?f=15&t=7163&start=20
# https://serverfault.com/questions/659955/allowing-ssh-on-a-server-with-an-active-openvpn-client
allow_outside_connections_to_eth () {
  echo "`date +'%F %T'`: updating routes..."

  # fix random lock out by deleting previous rules/routes
  del_ext_routes_rules ; sleep 1
  del_ext_routes_rules 2> /dev/null # paranoia

  # re-add rules/routes
  add_ext_routes_rules
} # allow_outside_connections_to_eth

# another idea to check connection is up: try pinging vpn server ip (ping -c1 -w5 -q \`ip route|grep via.*eth0|sed ...\`)
# here instead we check SSH connectivity:
ensure_ssh_from_outside_via_eth () {
  tun_ip=`ip route | grep "tun.*src" | cut -f 9 -d ' '`
  cur_ip=`dig -b $src @208.67.222.222 +short myip.opendns.com`

  #[ -n "$tun_ip" -a -n "$cur_ip" ] && \
  #  { nc -w 5 -z -s $tun_ip $cur_ip 22 || allow_outside_connections_to_eth; }

  if ! [ -n "$tun_ip" -a -n "$cur_ip" ] ; then
    echo "`date +'%F %T'`: tun/eth IPs unavailable, stopping VPN..."
    quit_openvpn
    return
  fi

  if nc -w 5 -z -s $tun_ip $cur_ip 22 ; then
    rm -f $flg_iproute
  else
    if ! [ -f "$flg_routes" ]; then
      allow_outside_connections_to_eth
      touch $flg_iproute
    else
      echo "`date +'%F %T'`: no access from outside, stopping VPN..."
      quit_openvpn
      rm -f $flg_iproute
    fi
  fi
} # ensure_ssh_from_outside_via_eth

quit_openvpn () {
  # clean status
  rm -f $flg_iproute
  del_ext_routes_rules

  pkill openvpn ; sleep 2
  pgrep openvpn > /dev/null && pkill -KILL openvpn
} # quit_openvpn

canary_failed () {
  echo "`date +'%F %T'`: CANARY FAILED ($1)! stopping VPN & exiting..."
  quit_openvpn
  exit
} # canary_failed

# exit if requested
if [ "$1" = "stop" ]; then
  quit_openvpn
  exit
fi

# infinite loop run/check/restart openvpn
while : ; do
  ts=`date +'%F %T'`

  # test proxy.sh warrant canary at 3AM
  psh_canary="/tmp/proxysh-${ts:0:10}.canary"
  if [ ! -f $psh_canary -a "03" = "${ts:11:2}" ]; then
    echo "$ts: testing warrant canary..."
    rm -f /tmp/proxysh-*.canary # purge previous file
    wget -q -O - https://proxy.sh/canary | gzip -9 -c > $psh_canary
    # test messages list
    cnd='The below "warrant canary" has been generated on: ' #`date +%F`
    cn1="To this date, there has been no warrants, searches or seizures that have not been reported in our Transparency Report, and that have actually taken place. The sky is blue :)"
    cn2="No warrants, searches or seizures of any kind, other than those reported via our Transparency Report, have ever been performed on proxy.sh assets, including in the following locations:"
    # test messages presence
    zgrep "$cnd" $psh_canary # print generation date
    zgrep "$cn1" $psh_canary > /dev/null || canary_failed "cn1"
    zgrep "$cn2" $psh_canary > /dev/null || canary_failed "cn2"
  fi

  if ! pgrep openvpn > /dev/null ; then
    ./proxysh-update.sh

    echo "$ts: restarting openvpn..."
    echo "${ts:0:10}" > $flg_restart # update restart flag
    allow_outside_connections_to_eth > /dev/null 2>&1
    openvpn --config proxysh.conf --daemon
    sleep 20
  fi

  # check SSH is reachable from outside via eth0 every 10 minutes
  [ "${ts:15:1}" = "0" ] && ensure_ssh_from_outside_via_eth

  # restart daily at 1AM
  if [ "$(<$flg_restart)" != "${ts:0:10}" -a "01" = "${ts:11:2}" ]; then
    echo
    echo "$ts: daily stopping..."
    quit_openvpn
  fi

  #echo -en "$ts\r"

  sleep 15
done

the ip commands are used to keep allowing incoming traffic from the previous default gateway 192.168.1.1 to the initial ip 192.168.1.110 in order to continue accessing your host services from the outside.

Note: Rarely you may lost connectivity via your physical interface (eg: dig -b 192.168.1.110 ...) for apparently no reason... just restart the networking service and vpn:

1
sudo pkill openvpn && sudo systemctl restart networking && ./run.sh

this should not happen anymore thanks to the ensure_ssh_from_outside_via_eth function.

Extras

  • To know your public IPs while openvpn is running type:
1
2
dig                  @208.67.222.222 myip.opendns.com # get VPN server IP
dig -b 192.168.1.110 @208.67.222.222 myip.opendns.com # get ISP public IP
1
2
3
socks.proxy.sh:1080  # useable inside the vpn (no auth required)
ext-eu.proxy.sh:1080 # useable outside the vpn (same VPN credential)
ext-us.proxy.sh:1080 # useable outside the vpn (same VPN credential)
1
2
3
# Note: VPN servers must have different IPs
ip route add IP_OF_1st_VPN_SERVER dev WAN_INTERFACE     # eth0/wlan0
ip route add IP_OF_2nd_VPN_SERVER dev 1st_VPN_INTERFACE # tun0
  • In order to retain connectivity to your default interface and just create a new virtual interface tun0 for a separate use, you can override pushed routes from the server by adding these lines to the configuration file:
1
2
3
4
route   0.0.0.0 192.0.0.0 net_gateway
route  64.0.0.0 192.0.0.0 net_gateway
route 128.0.0.0 192.0.0.0 net_gateway
route 192.0.0.0 192.0.0.0 net_gateway

Run the vpn inside a network namespace

Run openvpn telling it to not configure the tunX interface:

1
2
3
4
5
openvpn --config proxysh.conf \
  --ifconfig-noexec --route-noexec \
  --up       proxysh-cmd-netns.sh \
  --route-up proxysh-cmd-netns.sh \
  --down     proxysh-cmd-netns.sh

namespace creation and network configuration are then managed by the proxysh-cmd-netns.sh script1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/sh

case $script_type in
  up)
    ( ip netns list | grep -qs proxysh ) || ip netns add proxysh
    ip netns exec proxysh ip link set dev lo up
    ip link set dev "$1" up netns proxysh mtu "$2"
    ip netns exec proxysh ip addr add dev "$1" \
      "$4/${ifconfig_netmask:-30}" \
      ${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
    if [ -n "$ifconfig_ipv6_local" ]; then
      ip netns exec proxysh ip addr add dev "$1" \
        "$ifconfig_ipv6_local"/112
    fi
    ;;
  route-up)
    ip netns exec proxysh ip route add default via "$route_vpn_gateway"
    if [ -n "$ifconfig_ipv6_remote" ]; then
      ip netns exec proxysh ip route add default via \
        "$ifconfig_ipv6_remote"
    fi
    ;;
  down)
    # do not delete the namespace: keep it in case of server disconnection
    #ip netns delete proxysh
    ;;
esac

Once the VPN is up and running you can launch programs in the proxysh network namespace as root like this:

1
ip netns exec proxysh   su username -l -c "command arguments"

or you can conveniently use this script:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/sudo /bin/bash

function die() {
  echo "USAGE: netns-exec namespace cmd [arguments...]"
  exit
}

([ -z "$1" ] && die) || NS_NAME="$1" ; shift # check namespace
([ -z "$1" ] && die) || NS_CMD="$1"  ; shift # check command

RUN_CMD="$NS_CMD $@"
ip netns exec $NS_NAME su $SUDO_USER -l -c "${RUN_CMD}"

Source: OpenVPN manpage, Proxy.sh raspi howto, allow SSH via eth0, up/down scripts, UFW limit traffic on eth0 & paste.bin script, Deluge vpn/proxy guide, Proxy.sh SOCKS, Deluge VPN 1 and 2, Getting started with Proxy.sh VPN, Ignore pushed routes on Superuser and OpenVPN docs

Tips: test reachable port on server with NC, get SSH server key fingerprint, VPN services comparison