wknapik / vpnfailsafe

IP leak prevention for OpenVPN

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

vpnfailsafe.sh fails with "unbound variable"

cjordan opened this issue · comments

Hi, I'm trying to use this script with my Private Internet Access configs, provided by the AUR package private-internet-access-git - one example is as follows:

client
dev tun
proto udp
remote aus.privateinternetaccess.com 1198
resolv-retry infinite
nobind
persist-key
persist-tun
cipher aes-128-cbc
auth sha1
tls-client
remote-cert-tls server
auth-user-pass /etc/private-internet-access/login.conf
comp-lzo
verb 1
reneg-sec 0
crl-verify /etc/openvpn/crl.rsa.2048.pem
ca /etc/openvpn/ca.rsa.2048.crt
disable-occ
auth-nocache
script-security 2
up /etc/openvpn/update-resolv-conf.sh
down /etc/openvpn/update-resolv-conf.sh
up /etc/openvpn/vpnfailsafe.sh # my addition
down /etc/openvpn/vpnfailsafe.sh # my addition

When I call openvpn --config /etc/openvpn/AU_Sydney.conf, I get the following:

Wed Nov  2 07:25:29 2016 Multiple --up scripts defined.  The previously configured script is overridden.
Wed Nov  2 07:25:29 2016 Multiple --down scripts defined.  The previously configured script is overridden.
Wed Nov  2 07:25:29 2016 OpenVPN 2.3.12 x86_64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug 24 2016
Wed Nov  2 07:25:29 2016 library versions: OpenSSL 1.0.2j  26 Sep 2016, LZO 2.09
Wed Nov  2 07:25:29 2016 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Wed Nov  2 07:25:29 2016 UDPv4 link local: [undef]
Wed Nov  2 07:25:29 2016 UDPv4 link remote: [AF_INET]168.1.6.54:1198
Wed Nov  2 07:25:29 2016 [0842bd2998fed6b55e1cf952a940c935] Peer Connection Initiated with [AF_INET]168.1.6.54:1198
Wed Nov  2 07:25:31 2016 TUN/TAP device tun0 opened
Wed Nov  2 07:25:31 2016 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Wed Nov  2 07:25:31 2016 /usr/bin/ip link set dev tun0 up mtu 1500
Wed Nov  2 07:25:31 2016 /usr/bin/ip addr add dev tun0 local 10.44.10.6 peer 10.44.10.5
Wed Nov  2 07:25:31 2016 /etc/openvpn/vpnfailsafe.sh tun0 1500 1558 10.44.10.6 10.44.10.5 init
/etc/openvpn/vpnfailsafe.sh: line 69: !opt: unbound variable
Wed Nov  2 07:25:31 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Wed Nov  2 07:25:31 2016 Exiting due to fatal error

Multiple --up and --down scripts, yes, but I don't care, vpnfailsafe should take priority. Note than when I do not include vpnfailsafe, this call to openvpn works fine.

However, curiously, if I launch the VPN connection via NetworkManager, vpnfailsafe appears to work, because I can see /etc/hosts populated.

Unfortunately I'm at a loss for how to diagnose this problem. Perhaps vpnfailsafe.sh could be made more robust? Any help appreciated.

Hi. Just pushed a fix. Please update the package/script and retry.

vpnfailsafe.sh contains the functionality of update-resolv-conf.sh, so the calls to the latter can (and should) be removed from the config.

Thanks for your prompt action. Unfortunately, it looks like there's still room for improvement:

$ sudo openvpn --config /etc/openvpn/AU_Sydney.conf
Wed Nov  2 21:57:04 2016 OpenVPN 2.3.12 x86_64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug 24 2016
Wed Nov  2 21:57:04 2016 library versions: OpenSSL 1.0.2j  26 Sep 2016, LZO 2.09
Wed Nov  2 21:57:04 2016 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Wed Nov  2 21:57:04 2016 UDPv4 link local: [undef]
Wed Nov  2 21:57:04 2016 UDPv4 link remote: [AF_INET]168.1.23.112:1198
Wed Nov  2 21:57:05 2016 [28875836b69b5ccc1c80c1a22b73b052] Peer Connection Initiated with [AF_INET]168.1.23.112:1198
Wed Nov  2 21:57:07 2016 TUN/TAP device tun0 opened
Wed Nov  2 21:57:07 2016 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Wed Nov  2 21:57:07 2016 /usr/bin/ip link set dev tun0 up mtu 1500
Wed Nov  2 21:57:07 2016 /usr/bin/ip addr add dev tun0 local 10.3.10.6 peer 10.3.10.5
Wed Nov  2 21:57:07 2016 /etc/openvpn/vpnfailsafe.sh tun0 1500 1558 10.3.10.6 10.3.10.5 init
/etc/openvpn/vpnfailsafe.sh: line 117: ifconfig_netmask: unbound variable
Wed Nov  2 21:57:08 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Wed Nov  2 21:57:08 2016 Exiting due to fatal error

Also, this is without up and down update-resolv-conf.sh. Please let me know if there's anything else I can do to test.

Ok, so I figured this would take forever to resolve by posting logs back and forth, so I just bought a PIA account to debug this myself.

It turns out PIA uses p2p topology, rather than subnet, like my provider and there's a few differences that have to taken into account.

I just pushed a fix, so you should be able to use vpnfailsafe with PIA, but there's one caveat, described here:

UPDATE: some VPN providers assign hundreds of IPs to their VPN server domains and return different subsets of that pool in subsequent queries. This gets in the way of keeping routes and firewall exceptions for all these servers. At worst, this can prevent reconnection after the tunnel goes down and require going back to the state from before running vpnfailsafe to reconnect (see: below).

Unfortunately PIA is one of those providers.

So when you connect, for each IP the server domain(s) resolve(s) to, vpnfailsafe adds a hosts entry, a route and a firewall exception, so you can always (re)connect to the VPN. But that list is incomplete and when you combine that with periodic hosts updates, you might be left in a state where you're trying to connect to a correct server IP, but there's no route and no firewall exception to allow it.

The periodic hosts updates were a good idea for my case, but in case of PIA, they seem to be doing more harm than good. I'll have to think about a good way to balance these conflicting cases. I could turn the periodic hosts updates into periodic hosts/routes/iptables updates, but that's quite a bit of work. Right now, I'm leaning towards updating /etc/hosts only once on --up, but we'll see.

Thanks for the report.

Thanks for your dedication!

No problem @cjordan :)

I just pushed a change that removes the periodic hosts updates. It should improve things for you quite a bit, so please update the script again.

OK, so vpnfailsafe will now run the first time with the following output:

# openvpn --config /etc/openvpn/AU_Sydney.conf
Thu Nov  3 10:42:25 2016 OpenVPN 2.3.12 x86_64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug 24 2016
Thu Nov  3 10:42:25 2016 library versions: OpenSSL 1.0.2j  26 Sep 2016, LZO 2.09
Thu Nov  3 10:42:25 2016 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Thu Nov  3 10:42:25 2016 UDPv4 link local: [undef]
Thu Nov  3 10:42:25 2016 UDPv4 link remote: [AF_INET]168.1.6.21:1198
Thu Nov  3 10:42:25 2016 [58cb24fd5be83d09a89aad7241adddf6] Peer Connection Initiated with [AF_INET]168.1.6.21:1198
Thu Nov  3 10:42:27 2016 TUN/TAP device tun0 opened
Thu Nov  3 10:42:27 2016 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Thu Nov  3 10:42:27 2016 /usr/bin/ip link set dev tun0 up mtu 1500
Thu Nov  3 10:42:27 2016 /usr/bin/ip addr add dev tun0 local 10.76.10.6 peer 10.76.10.5
Thu Nov  3 10:42:27 2016 /etc/openvpn/vpnfailsafe.sh tun0 1500 1558 10.76.10.6 10.76.10.5 init
RTNETLINK answers: File exists
Thu Nov  3 10:42:29 2016 ERROR: Linux route add command failed: external program exited with error status: 2
RTNETLINK answers: File exists
Thu Nov  3 10:42:29 2016 ERROR: Linux route add command failed: external program exited with error status: 2
RTNETLINK answers: File exists
Thu Nov  3 10:42:29 2016 ERROR: Linux route add command failed: external program exited with error status: 2
Thu Nov  3 10:42:29 2016 Initialization Sequence Completed
^CThu Nov  3 10:42:49 2016 event_wait : Interrupted system call (code=4)
Thu Nov  3 10:42:49 2016 /usr/bin/ip addr del dev tun0 local 10.76.10.6 peer 10.76.10.5
Thu Nov  3 10:42:49 2016 /etc/openvpn/vpnfailsafe.sh tun0 1500 1558 10.76.10.6 10.76.10.5 init

I will let this run overnight to see if there's any issue with maintaining the connection while the server's IP changes (ideally, openvpn successfully reconnects after any dropout). I was aware of the dynamic IP before, and did some awful hacking to get around it, which is how I stumbled on vpnfailsafe in the first place - so it'd be great to have a one-stop solution to the issue.

Something you might be interested in while I'm testing, however, are some cleanups with iptables - if I kill the first openvpn connection and try to run again:

# openvpn --config /etc/openvpn/AU_Sydney.conf
Thu Nov  3 10:41:51 2016 OpenVPN 2.3.12 x86_64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug 24 2016
Thu Nov  3 10:41:51 2016 library versions: OpenSSL 1.0.2j  26 Sep 2016, LZO 2.09
Thu Nov  3 10:41:51 2016 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Thu Nov  3 10:41:51 2016 UDPv4 link local: [undef]
Thu Nov  3 10:41:51 2016 UDPv4 link remote: [AF_INET]168.1.53.213:1198
Thu Nov  3 10:41:51 2016 [e05cfa6b59da8d4c4cb9e81297052ab2] Peer Connection Initiated with [AF_INET]168.1.53.213:1198
Thu Nov  3 10:41:53 2016 TUN/TAP device tun0 opened
Thu Nov  3 10:41:53 2016 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Thu Nov  3 10:41:53 2016 /usr/bin/ip link set dev tun0 up mtu 1500
Thu Nov  3 10:41:53 2016 /usr/bin/ip addr add dev tun0 local 10.71.10.6 peer 10.71.10.5
Thu Nov  3 10:41:53 2016 /etc/openvpn/vpnfailsafe.sh tun0 1500 1558 10.71.10.6 10.71.10.5 init
iptables: Chain already exists.
/etc/openvpn/vpnfailsafe.sh:87: `iptables -N "VPNFAILSAFE_$*"` returned 1
RTNETLINK answers: Cannot assign requested address
Thu Nov  3 10:41:55 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 2
Thu Nov  3 10:41:55 2016 Exiting due to fatal error

This is simply fixed with:

iptables -F
iptables -X VPNFAILSAFE_FORWARD
iptables -X VPNFAILSAFE_INPUT
iptables -X VPNFAILSAFE_OUTPUT

... so I assume these need to go somewhere in the "down" part of your script.

Thanks again for your help.

The first log looks fine. As mentioned here, the "RTNETLINK answers: File exists" errors can be ignored safely. If you don't like them, you can use the "route-noexec" option to make OpenVPN leave all the routing to vpnfailsafe.

ideally, openvpn successfully reconnects after any dropout

My thinking here is there's no point in tracking all the IPs behind the domain. There's too many and you can't validate any set at any given point quickly and reliably. Instead, let's remember the 13 addresses we get initially and just run with it, ideally, indefinitely. If one becomes invalid, we have 12 others to use. I'll have to test if OpenVPN will actually cycle through them on reconnection attempts. If the set saved in /etc/hosts becomes too stale, we reset iptables and reconnect, getting a new set on --up. I assume this would be necessary only after a reasonably long time.

Unless PIA starts returning all IPs for their domains in one response, this seems to me to be the only way to go.

iptables: Chain already exists.

That is really weird. The first thing the function that updates the firewall does is remove the old chains. I've been using vpnfailsafe for a month now and I've tested all forms of reconnections many times and never got that error. Is this reproducable ? If so, could you add set -x in the second line of the script and post the log of this happening ?

In any case though, we don't want to remove those chains on --down, otherwise vpnfailsafe will not live up to its name ;]

BTW, what distro/kernel/iptables version are you using ?

I didn't have any connection problems with openvpn last night, so I'll try something more long term to see what happens. Everything looks good on my end!

Unless PIA starts returning all IPs for their domains in one response, this seems to me to be the only way to go.

I agree with your logic. Hopefully it's only a long term problem, if ever, but maybe the best solution is another VPN provider without these "dynamic" IPs.

If so, could you add set -x in the second line of the script and post the log of this happening ?

I can't seem to reproduce what was happening. I'll post back if I notice something.

In any case though, we don't want to remove those chains on --down, otherwise vpnfailsafe will not live up to its name ;]

Yes that makes sense, I'm showing my lack of iptables ability here...

BTW, what distro/kernel/iptables version are you using ?

Up to date Arch, 4.8.4-1-ARCH, iptables v1.6.0.

Everything looks good on my end!

Great! :)

maybe the best solution is another VPN provider without these "dynamic" IPs.

Hopefully this will not be enough of a problem to switch. If you do decide to do it, check out torrentfreak's recommendations.

Anyway, I'm glad things are working well so far. Let me know if anything changes ;] And thanks again for reporting it. If you just gave up after the first error, I would not have known there was a problem. With no real alternatives (that I'm aware of), I'm hoping vpnfailsafe can become the go-to solution for those who don't need/want a GUI to set up a firewall ;]