Question:
How can I restart OpenVPN using a modified openvpn.conf while still preserving GUI-configured PBR rules?
Context:
I am trying to use a script from here that dynamically updates openvpn.conf with a more optimal VPN remote. However, the current method of restarting OpenVPN seems to break any PBR rules applied in the GUI.
Specifically, this line does reconnect OpenVPN to the new remote successfully, however the GUI-configured PBR rules are not applied.
I had tried something like below instead as this watchdog script suggests, however this approach does not use my modified openvpn.conf with the new "remote" config.
Code:
restart_f openvpn
Any suggestions or pointers would be greatly welcomed. Thanks!
Full Script:
Code:
#!/bin/sh
# specify PATH to run from cron
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/jffs/sbin:/jffs/bin:/jffs/usr/sbin:/jffs/usr/bin:/mmc/sbin:/mmc/bin:/mmc/usr/sbin:/mmc/usr/bin:/opt/sbin:/opt/bin:/opt/usr/sbin:/opt/
# kill openvpn
PID=$(pidof openvpn)
kill -s SIGTERM "$PID"
while $(kill -0 "$PID" 2>/dev/null); do
sleep 1
done
# fetch ProtonVPN server info
LOGICALS=$(curl -sk -H "Cache-Control: no-cache" -H "Accept: application/json" https://api.protonmail.ch/vpn/logicals)
# query for optimal server
IPSTRING=$(echo "$LOGICALS" | jq '.LogicalServers | map(select(.Status == 1 and .Tier == 2 and (.Name | (startswith("US-TX#") or startswith("US-GA#")) and (endswith("TOR") | not)))) | [sort_by(.Score, .Load)[]][0] | .Servers[0].EntryIP')
# update openvpn config
if [ -n "$IPSTRING" ]
then
sed -i '/^remote/d' /tmp/openvpncl/openvpn.conf
IPSTRING="${IPSTRING%\"}"
IP="${IPSTRING#\"}"
echo "Connecting to $IP..."
REMOTE="remote ${IP} 443"
echo $REMOTE >> /tmp/openvpncl/openvpn.conf
fi
On the face of it, it's not obvious why PBR would fail to work under such circumstances. But there's been a lot of changes in how the OpenVPN client works over the past couple years, particularly recently, so I decided to take a closer look for myself.
From my own testing, it appears if you have the killswitch OFF, PBR will fail on the restart. At least when I dump the ip rules database, I don't see the rules anymore.
Code:
ip rule show
However, if I turn ON the killswitch, PBR works on the restart. And that's because as part of the killswitch logic, it preserves the ip rules database. IOW, after the openvpn process is killed, you can still dump the ip rules database again and see your rules.
IIRC, before the killswitch was implemented, PBR used to be managed within the route-up/route-down scripts. But that logic was moved into the firmware to make this and some other data structures persistent should the openvpn process fail. And perhaps that change in logic explains why simply restarting the OpenVPN client doesn't preserve PBR, at least w/ the killswitch OFF.
Btw, there's no need to include the --route-up or --route-pre-down options on the command line. Another one of the many changes to the OpenVPN client is the router now includes these directives in the config file (so they can be overridden in the Additional Config field should anyone find it necessary). IOW, the following will suffice.
Joined: 18 Mar 2014 Posts: 12450 Location: Netherlands
Posted: Wed Jul 28, 2021 7:02 Post subject:
It is like @eibgrad said the PBR has been moved to the code logic so either do your own PBR (I can send examples if you want) but easier is this (have not tried it)
Code:
stopservice openvpn
#replace your remote ip address/port in the nvram parameter:
nvram set openvpncl_remoteip="<my_new_server_address>"
nvram set openvpncl_remoteport="<my_new_port>"
nvram commit
startservice openvpn
Posted: Wed Jul 28, 2021 13:15 Post subject: It works!
Thanks, @eibgrad and @egc . I tried @egc 's approach, and it seems to work well, and simpler to boot!
Updated code:
Code:
#!/bin/sh
# specify PATH to run from cron
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/jffs/sbin:/jffs/bin:/jffs/usr/sbin:/jffs/usr/bin:/mmc/sbin:/mmc/bin:/mmc/usr/sbin:/mmc/usr/bin:/opt/sbin:/opt/bin:/opt/usr/sbin:/opt/
# stop openvpn
stopservice openvpn
# fetch ProtonVPN server info
LOGICALS=$(curl -sk -H "Cache-Control: no-cache" -H "Accept: application/json" https://api.protonmail.ch/vpn/logicals)
# query for optimal server
IPSTRING=$(echo "$LOGICALS" | jq '.LogicalServers | map(select(.Status == 1 and .Tier == 2 and (.Name | (startswith("US-TX#") or startswith("US-GA#")) and (endswith("TOR") | not)))) | [sort_by(.Score, .Load)[]][0] | .Servers[0].EntryIP')
# update openvpn config
if [ -n "$IPSTRING" ]
then
IPSTRING="${IPSTRING%\"}"
IP="${IPSTRING#\"}"
echo "Connecting to $IP..."
nvram set openvpncl_remoteip="${IP}"
nvram commit
fi