Qubes PIA VPN CLI Approach - JavaScriptDude/QubesStuff GitHub Wiki

How To make a PIA VPN Gateway in Qubes - CLI Approach

Set up a ProxyVM as a VPN gateway using iptables and CLI scripts

This method is more involved than the one above, but has anti-leak features that also make the connection fail closed should it be interrupted. It has been tested with Fedora 23 and Debian 8 templates.

  1. Create a new VM, name it, click the ProxyVM radio button, and choose a color and template. image vpn pia new vm

    Note: Do not enable NetworkManager in the ProxyVM, as it can interfere with the scripts' DNS features. If you enabled NetworkManager or used other methods in a previous attempt, do not re-use the old ProxyVM... Create a new one according to this step.

    If your choice of template VM doesn't already have the VPN client software, you'll need to install the software in the template before proceeding. Disable any auto-starting service that comes with the software package: for example sudo systemctl disable openvpn.service.

    You may also wish to install nano or another simple text editor for entering the scripts below.

  2. Set up and test the VPN client.

    Make sure the VPN VM and its template VM are not running.

    Run a terminal (CLI) in the VPN VM -- this will start the VM. Then make a new 'vpn' folder with sudo mkdir /rw/config/vpn and copy your VPN config files here (the example config filename used here is openvpn-client.ovpn). Files accompanying the main config such as *.crt and *.pem should also go here, and should not be referenced in the main config by absolute paths such as '/etc/...'.

    Notes about VPN config options: The VPN scripts here are intended to work with commonly used tun interfaces, whereas tap mode is untested. Also, the config should route all traffic through your VPN's interface after a connection is created; For openvpn the directive for this is redirect-gateway def1. Lastly, the VPN client may not be able to prompt you for credentials when connecting to the server: Creating a file in the 'vpn' folder with your credentials and using a directive such as openvpn's auth-user-pass <filename> is recommended.

    Test your client configuration: Run the client from a CLI prompt in the 'vpn' folder, preferably as root. For example:

    sudo openvpn --cd /rw/config/vpn --config openvpn-client.ovpn
    

    Watch for status messages that indicate whether the connection is successful and test from another VPN VM terminal window with ping and traceroute. DNS may be tested at this point by replacing addresses in /etc/resolv.conf with ones appropriate for your VPN (although this file will not be used when setup is complete). Diagnose any connection problems using resources such as client documentation and help from your VPN service provider.

    Proceed to the next step when you're sure the basic VPN connection is working.

  3. Create the DNS-handling script. Use sudo nano /rw/config/vpn/qubes-vpn-handler.sh to edit and add:

    #!/bin/bash
    set -e
    export PATH="$PATH:/usr/sbin:/sbin"
    
    case "$1" in
    
    up)
    # To override DHCP DNS, assign DNS addresses to 'vpn_dns' env variable before calling this script;
    # Format is 'X.X.X.X  Y.Y.Y.Y [...]'
    if [[ -z "$vpn_dns" ]] ; then
    	# Parses DHCP foreign_option_* vars to automatically set DNS address translation:
    	for optionname in ${!foreign_option_*} ; do
    		option="${!optionname}"
    		unset fops; fops=($option)
    		if [ ${fops[1]} == "DNS" ] ; then vpn_dns="$vpn_dns ${fops[2]}" ; fi
    	done
    fi
    
    iptables -t nat -F PR-QBS
    if [[ -n "$vpn_dns" ]] ; then
    	# Set DNS address translation in firewall:
    	for addr in $vpn_dns; do
    		iptables -t nat -A PR-QBS -i vif+ -p udp --dport 53 -j DNAT --to $addr
    		iptables -t nat -A PR-QBS -i vif+ -p tcp --dport 53 -j DNAT --to $addr
    	done
    	su - -c 'notify-send "$(hostname): LINK IS UP." --icon=network-idle' user
    else
    	su - -c 'notify-send "$(hostname): LINK UP, NO DNS!" --icon=dialog-error' user
    fi
    
    ;;
    down)
    su - -c 'notify-send "$(hostname): LINK IS DOWN !" --icon=dialog-error' user
    ;;
    esac
    

    Now save the script and make it executable:
    sudo chmod +x /rw/config/vpn/qubes-vpn-handler.sh

  4. Configure client to use the DNS handling script. Using openvpn as an example, edit the config with sudo nano /rw/config/vpn/openvpn-client.ovpn and add these lines:

    script-security 2
    up 'qubes-vpn-handler.sh up'
    down 'qubes-vpn-handler.sh down'
    

    Restart the client and test the connection again ...this time from an AppVM!

  5. Set up iptables anti-leak rules.

    Edit the firewall script with sudo nano /rw/config/qubes-firewall-user-script then clear out the existing lines and add:

    #!/bin/bash
    #    Block forwarding of connections through upstream network device
    #    (in case the vpn tunnel breaks):
    iptables -I FORWARD -o eth0 -j DROP
    iptables -I FORWARD -i eth0 -j DROP
    
    #    Block all outgoing traffic
    iptables -P OUTPUT DROP
    iptables -F OUTPUT
    iptables -I OUTPUT -o lo -j ACCEPT
    
    #    Add the `qvpn` group to system, if it doesn't already exist
    if ! grep -q "^qvpn:" /etc/group ; then
        groupadd -rf qvpn
        sync
    fi
    sleep 2s
    
    #    Allow traffic from the `qvpn` group to the uplink interface (eth0);
    #    Our VPN client will run with group `qvpn`.
    iptables -I OUTPUT -p all -o eth0 -m owner --gid-owner qvpn -j ACCEPT
    

    Now save the script and make it executable:
    sudo chmod +x /rw/config/qubes-firewall-user-script

  6. Set up the VPN's autostart.

    Use sudo nano /rw/config/rc.local to clear out the existing lines and add:

    #!/bin/bash
    VPN_CLIENT='openvpn'
    VPN_OPTIONS='--cd /rw/config/vpn/ --config openvpn-client.ovpn --daemon'
    
    su - -c 'notify-send "$(hostname): Starting $VPN_CLIENT..." --icon=network-idle' user
    groupadd -rf qvpn ; sleep 2s
    sg qvpn -c "$VPN_CLIENT $VPN_OPTIONS"
    

    Change the VPN_CLIENT and VPN_OPTIONS variables to match your VPN software.

    Now save the script and make it executable:
    sudo chmod +x /rw/config/rc.local

  7. Restart the new VM! The link should then be established automatically with a popup notification to that effect.

Usage

Configure your AppVMs to use the VPN VM as a NetVM...

Settings-NetVM.png

If you want to be able to use the Qubes firewall, create a new FirewallVM (as a ProxyVM) and set it to use the VPN VM as its NetVM. Then, configure AppVMs to use your new FirewallVM as their NetVM.

If you want to update your TemplateVMs through the VPN, enable the qubes-updates-proxy service in your new FirewallVM. You can do this in the Services tab in Qubes VM Manager or on the command-line:

$ qvm-service -e <name> qubes-updates-proxy

Then, configure your templates to use your new FirewallVM as their NetVM.

Troubleshooting

  • Always test your basic VPN connection before adding scripts.
  • Test DNS: Ping a familiar domain name from an appVM. It should print the IP address for the domain.
  • For scripting: Ping external IP addresses from inside the VPN VM using sudo sg qvpn -c 'ping ...', then from an appVM using just ping .... Once the firewall rules are in place, you will have to use sudo sg to run any IP network commands in the VPN VM.
  • Use iptables -L -v and iptables -L -v -t nat to check firewall rules. The latter shows the critical PR-QBS chain that enables DNS forwarding.
⚠️ **GitHub.com Fallback** ⚠️