tasket / Qubes-vpn-support

VPN configuration in Qubes OS
GNU General Public License v3.0
126 stars 28 forks source link

NordVPN Customization #37

Open bouncymonkey opened 5 years ago

bouncymonkey commented 5 years ago

NordVPN provides neat packages for Linux, based on OpenVPN. One key feature of that package over vanilla OpenVPN is that NordVPN connects to a recommended server based on load and location.

It is not obvious how to use the NordVPN program in stead of OpenVPN in Qubes-vpn-support. OpenVPN provides several hooks (e.g. --up and --down) that are lacking in the NordVPN binary.

However, the NordVPN website provides a utility to determine which server is best. It is trivial to pull that information from the webpage and use the matching configuration in OpenVPN. See the utility here: https://nordvpn.com/servers/tools/

Here's what to do:

  1. Create a NordVPN account.
  2. Download the OpenVPN configuration files for all NordVPN servers: https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
  3. Extract the configuration files in: /rw/config/vpn (You should have directory /rw/config/vpn/ovpn_udp containing many OVPN configuration files.)
  4. You may have to install extra packages, such as jq for JSON parsing.
  5. Modify script /usr/lib/qubes/qubes-vpn-setup as follows:
--pre-start)
    if [ ! -f $qvpnpath/$upfile ] && [ ! -f $qvpnpath/no-$upfile ]; then
        if [ ! -f /tmp/qvpn-askpass ]; then
            systemd-run --unit=qvpn-askpass -E DISPLAY=:0 sh -c \
            'sleep 2s; /usr/bin/xterm \
            -T "VPN Login" -e /usr/lib/qubes/qubes-vpn-setup --xterm'
        fi
    elif [ ! -f $tmppath ] && [ ! -f $qvpnpath/no-$upfile ]; then
        cp -aL $qvpnpath/$upfile $tmppath.tmp
        mv $tmppath.tmp $tmppath
    fi

    recommended=`wget -q -O - 'https://nordvpn.com/wp-admin/admin-ajax.php?action=servers_recommendations&filters={"servers_groups":[11],"servers_technologies":[3]}' | jq -r '.[0]["hostname"]'`.udp.ovpn
    vpn_node=`echo $recommended | cut -d . -f 1`
    if [ -f $recommended ]; then
        ln -sf $qvpnpath/ovpn_udp/$recommended $qvpnpath/vpn-client.conf
    fi

    if [ -n "$filter_opts" ]; then
        # workaround for option parser bugs and overrides:
        # process config to remove options.
        grep -Ev '^[[:space:]]*('"$filter_opts"')[[:space:]]*' \
          $qvpnpath/vpn-client.conf  >/tmp/vpn-client.conf
    else
        cp -aL $qvpnpath/vpn-client.conf /tmp
    fi
    sync
    su - -c "notify-send \"`hostname`: Ready to start link on $vpn_node.\"" user
;;

This should pick the recommended NordVPN server every time the VPN service starts. Note that in this example, we ask the website for a US-based server that supports UDP. The wget URL can be changed to match one's needs.

This is probably not the best way to go about it, but it solved my problem and I figured it would solve other's. If anyone is willing to make it a solid feature, I will be happy to help.

tasket commented 5 years ago

Hi, thanks for the suggestion.

Unfortunately, NordVPN appears to use hundreds of DNS entries to point to individual servers...it is almost like referring to individual ips. I'd suggest the most straightforward way around this is to not use a vendor-specific kludge, but to list many of the servers (hundreds, if you wish) in the conf and use the openvpn remote-random option. This exposes the vpn vm to less risk since it doesn't have to interact with a web server during startup.

I'd also suggest that NordVPN customers contact their support to request a regional domain name like "us-east", "us-nyc", "us-west" etc. that does load balancing via the traditional DNS method. Notice that a domain like us-east.privateinternetaccess.com has an DNS response that changes ips second-by-second for this reason.

bouncymonkey commented 5 years ago

Note: It would probably be better to fetch the recommended VPN server every time the service is restarted. Unfortunately, the firewall is dropping any connection to the outside while the VPN is down to prevent leaks, so we would have to poke a hole in its configuration.