Giter VIP home page Giter VIP logo

vopono's Introduction

vopono logo

vopono is a tool to run applications through VPN tunnels via temporary network namespaces. This allows you to run only a handful of applications through different VPNs simultaneously, whilst keeping your main connection as normal.

vopono includes built-in killswitches for both Wireguard and OpenVPN.

Currently Mullvad, AzireVPN, MozillaVPN, ProtonVPN, iVPN, NordVPN, AirVPN, HMA (HideMyAss) and PrivateInternetAccess are supported directly, with custom configuration files also supported with the --custom argument. Cloudflare Warp is also supported.

For custom connections the OpenConnect and OpenFortiVPN protocols are also supported (e.g. for enterprise VPNs). See the vopono User Guide for more details.

Screenshot

Screenshot showing an example with firefox, google-chrome-stable and lynx all running through different VPN connections:

Screenshot

Supported Providers

Provider OpenVPN support Wireguard support
Mullvad
AzireVPN
iVPN
PrivateInternetAccess ✅*
ProtonVPN ✅** ✅***
MozillaVPN
NordVPN
HMA (HideMyAss)
AirVPN
Cloudflare Warp****
Self host (--custom)

* Port forwarding supported with the --port-forwarding option and --port-forwarding-callback to run a command when the port is refreshed.

** See the User Guide for authentication instructions for generating the OpenVPN config files via vopono sync. You must copy the authentication header of the form AUTH-xxx=yyy where yyy is the value of the x-pm-uid header in the same request when logged in, in your web browser.

*** For ProtonVPN you can generate and download specific Wireguard config files, and use them as a custom provider config. See the User Guide for details. Port Forwarding is supported with the --port-forwarding argument for both OpenVPN and Wireguard. Note for using a custom config with Wireguard, the port forwarding implementation to be used should be specified with --custom-port-forwarding (i.e. with --provider custom --custom xxx.conf --protocol wireguard --custom-port-forwarding protonvpn ). natpmpc must be installed. Note for OpenVPN you must generate the OpenVPN config files appending +pmp to your OpenVPN username, and you must choose servers which support this feature (e.g. at the time of writing, the Romania servers do). The assigned port is then printed to the terminal where vopono was launched - this should then be set in any applications that require it. The port can also be passed to a custom script that will be executed within the network namespace via the --port-forwarding-callback argument.

**** Cloudflare Warp uses its own protocol. Set both the provider and protocol to warp. Note you must first register with sudo warp-cli register and then run it once with sudo warp-svc and sudo warp-cli connect outside of vopono. Please verify this works first before trying it with vopono. Note there may also be issues with Warp overriding the DNS settings.

Usage

Set up VPN provider configuration files:

$ vopono sync

Note when creating and uploading new Wireguard keypairs there may be a slight delay until they are usable (about 30-60 seconds on Mullvad for example).

Run Firefox through an AzireVPN Wireguard connection to a server in Norway:

$ vopono exec --provider azirevpn --server norway firefox

You should run vopono as your own user (not using sudo) as it will handle privilege escalation where necessary. For more details around running as a systemd service, etc. see the User Guide.

vopono can handle up to 255 separate network namespaces (i.e. different VPN server connections - if your VPN provider allows it). Commands launched with the same server prefix and VPN provider will share the same network namespace.

Default configuration options can be saved in the ~/.config/vopono/config.toml file, for example:

firewall = "NfTables"
provider = "Mullvad"
protocol = "Wireguard"
server = "usa-us22"

Note that the values are case-sensitive.

See the vopono User Guide for much more detailed usage instructions (including handling daemons and servers).

Installation

AUR (Arch Linux)

Install the vopono-git package with your favourite AUR helper.

$ paru -S vopono-git
$ vopono sync

Alternatively use the vopono-bin package if you don't want to compile from source.

Raspberry Pi (Raspbian)

Download and install the vopono_x.y.z_armhf.deb package from the releases page:

$ sudo dpkg -i vopono_0.2.1_armhf.deb

You will need to install OpenVPN (available in the Raspbian repos):

$ sudo apt install openvpn

You can then use vopono as above (note that the Chromium binary is chromium-browser):

$ vopono sync --protocol openvpn mullvad
$ vopono exec --provider mullvad --server sweden chromium-browser

Screenshot of vopono with OpenVPN running on Raspbian:

Raspbian Screenshot

Note Wireguard is not in the Raspbian repositories, so installing it is not trivial. You can follow this guide to attempt it, but note that not only do you need to install Wireguard and wireguard-tools to have wg available, but also the linux-headers to ensure it works correctly (i.e. you don't just get Protocol not supported errors when trying to establish a connection).

Check the User Guide for details on port forwarding and using vopono with daemons and servers, in case you want to use your Raspberry Pi to run privoxy or transmission-daemon, etc.

Debian + Ubuntu

Install the deb package provided on the releases page.

Fedora + OpenSUSE

Install the rpm package provided on the release page (choose the correct version).

Other Linux

Either use the compiled binaries on the release page, or install from source with Cargo as documented below.

From this repository (with Cargo)

Run the install script provided: install.sh - this will cargo install the repository and copy over the configuration files to ~/.config/vopono/

Note the minimum supported Rust version is 1.43. You can check your version with:

$ rustc --version

Known issues

  • When launching a new application in an existing vopono namespace, any modifications to the firewall rules (i.e. forwarding and opening ports) will not be applied (they are only used when creating the namespace). The same applies for port forwarding.
  • OpenVPN credentials are always stored in plaintext in configuration - may add option to not store credentials, but it seems OpenVPN needs them provided in plaintext.
  • There is no easy way to delete MozillaVPN devices (Wireguard keypairs) - unlike Mullvad this cannot be done on the webpage. I recommend using MozWire to manage this.
  • gnome-terminal will not run in the network namespace due to the client-server model - see issue #48
  • Port forwarding from inside the network namespace to the host (e.g. for running transmission-daemon) does not work correctly when vopono is run as root - see issue #84

License

vopono is licensed under the GPL Version 3.0 (or above), see the LICENSE file or https://www.gnu.org/licenses/gpl-3.0.en.html

Etymology

vopono is the pronunciation of the letters VPN in Esperanto.

Se vi ankaŭ parolas Esperanton, bonvolu serĉi min en la kanalo de Discord de Rust Programming Language Community.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, will be licensed under the GPLv3 (or above), without any additional terms or conditions.

Many thanks to NilsIrl's MozWire for the investigation of the MozillaVPN API.

vopono's People

Contributors

ak-1 avatar asloan7 avatar benland100 avatar broderpeters avatar chapmanjacobd avatar chrilves avatar dlemel8 avatar douile avatar eorlbruder avatar hashworks avatar jamesmcm avatar jeff-hughes avatar jramseygreen avatar meldafert avatar mobad avatar necrevistonnezr avatar nilsirl avatar noah-witt avatar vulpyne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vopono's Issues

Add firewall rules to always allow connection from netns to host

See #59

iptables -A FORWARD -i eth0 -o v-eth1 -j ACCEPT
iptables -A FORWARD -o eth0 -i v-eth1 -j ACCEPT

Need to add both iptables and nftables rules to do the above (and remove them when vopono terminates) - like we do with the masquerade rule on the host.

Interface must be the detected (or provided) interface e.g. ethernet or wireless.

veth tunnel name must be generated name.

Can't access exposed port from LAN

When running vopono -v exec -k -f 9091 --provider azirevpn --server norway "transmission-daemon -a *.*.*.*" I can access the exposed port from the same machine but I can't access it from another machine in same LAN. Would it be possible to do so?

I am asked for my mullvad private key but I don't have any?

So, I use by default, the Mullvad app but I want to only have specific apps use it.

I found this project and seems like it would be perfect for me. I installed it, started the sync, gave my account number and picked the public key.

Then I am asked for my private key but what is it and where do I find it? There aren't any on my Mullvad account page and googling around also did not provide answers. And this projects setting up page also did not mentioned private key's.

Anyone here know where I can find the private key?

Add keep-alive option

Add option to keep namespace alive (and a way of killing a specific namespace).

Useful for daemons, etc.

NordVPN sync not working

2021-02-16T19:04:10.987Z INFO  vopono::util > Calling sudo for elevated privileges, current user will be used as default user
 2021-02-16T19:04:10.987Z DEBUG vopono::util > Args: ["target/debug/vopono", "-v", "sync"]
Which VPN providers do you wish to synchronise? Press Space to select and Enter to continue: NordVPN
 2021-02-16T19:04:12.727Z INFO  vopono::sync > Starting OpenVPN configuration...
Please choose the set of OpenVPN configuration files you wish to install: UDP: These files connect over UDP.
 2021-02-16T19:04:14.076Z DEBUG reqwest::connect > starting new connection: https://downloads.nordcdn.com/
 2021-02-16T19:04:14.086Z DEBUG rustls::client::hs > No cached session for DNSNameRef("downloads.nordcdn.com")
 2021-02-16T19:04:14.086Z DEBUG rustls::client::hs > Not resuming any session
 2021-02-16T19:04:14.094Z DEBUG rustls::client::hs > Using ciphersuite TLS13_CHACHA20_POLY1305_SHA256
 2021-02-16T19:04:14.095Z DEBUG rustls::client::tls13 > Not resuming
 2021-02-16T19:04:14.095Z DEBUG rustls::client::tls13 > TLS1.3 encrypted extensions: [ServerNameAck, Protocols([PayloadU8([104, 50])])]
 2021-02-16T19:04:14.096Z DEBUG rustls::client::hs    > ALPN protocol is Some(b"h2")
 2021-02-16T19:04:14.100Z DEBUG rustls::client::tls13 > Ticket saved
 2021-02-16T19:04:14.101Z DEBUG rustls::client::tls13 > Ticket saved
 2021-02-16T19:04:14.144Z DEBUG reqwest::async_impl::client > response '200 OK' for https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
 2021-02-16T19:04:16.359Z DEBUG vopono::providers::nordvpn::openvpn > Reading file: ovpn_udp/al18.nordvpn.com.udp.ovpn
 2021-02-16T19:04:16.359Z DEBUG vopono::providers::nordvpn::openvpn > Reading filename: **albania-ovpn_udp/al18.ovpn**
Error: No such file or directory (os error 2)

This is on 7a9402a and no .config/vopono
I added that last print.
It seems like it should be stripping off the ovpn_udp part at some point.

@jeff-hughes

Launched process has no internet connection

Hi,

I am trying to use vopono on Arch Linux (Installed from the AUR) with Mullvad using Wireguard. However I am not getting any internet connection. Neither a browser (firefox or chrome) or a bash terminal has connection (even ping 8.8.8.8 does not respond).

I have tried a bunch of different options including disabling the killswitch and specifying the dns, interface etc.. but still no connection.

Below is a the output of vopono when I try and connect using a custom config (The config does work with wg-quick)

vopono -v exec --custom /etc/wireguard/mullvad-se4.conf bash
 2020-10-26T15:57:27.841Z INFO  vopono::util > Calling sudo for elevated privileges, current user will be used as default user
 2020-10-26T15:57:27.841Z DEBUG vopono::util > Args: ["vopono", "-v", "exec", "--custom", "/etc/wireguard/mullvad-se4.conf", "bash"]
 2020-10-26T15:57:28.105Z DEBUG vopono::util > Existing namespaces: []
 2020-10-26T15:57:28.105Z DEBUG vopono::network_interface > ip addr
 2020-10-26T15:57:28.106Z DEBUG vopono::exec              > Interface: wlan0
 2020-10-26T15:57:28.107Z DEBUG vopono::util              > Existing namespaces: []
 2020-10-26T15:57:28.107Z DEBUG vopono::util              > ip netns add vopono_custom_mull
 2020-10-26T15:57:28.109Z INFO  vopono::netns             > Created new network namespace: vopono_custom_mull
 2020-10-26T15:57:28.110Z DEBUG vopono::util              > Existing interfaces: 
 2020-10-26T15:57:28.111Z DEBUG vopono::util              > Assigned IPs: []
 2020-10-26T15:57:28.111Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip addr add 127.0.0.1/8 dev lo
 2020-10-26T15:57:28.113Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip link set lo up
 2020-10-26T15:57:28.115Z DEBUG vopono::veth_pair         > NetworkManager detected, adding custom_mull_d to unmanaged devices
 2020-10-26T15:57:28.116Z DEBUG vopono::util              > nmcli connection reload
 2020-10-26T15:57:28.126Z DEBUG vopono::util              > ip link add custom_mull_d type veth peer name custom_mull_s
 2020-10-26T15:57:28.127Z DEBUG vopono::util              > ip link set custom_mull_d up
 2020-10-26T15:57:28.128Z DEBUG vopono::util              > ip link set custom_mull_s netns vopono_custom_mull up
 2020-10-26T15:57:28.181Z DEBUG vopono::util              > ip addr add 10.200.1.1/24 dev custom_mull_d
 2020-10-26T15:57:28.182Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip addr add 10.200.1.2/24 dev custom_mull_s
 2020-10-26T15:57:28.184Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip route add default via 10.200.1.1 dev custom_mull_s
 2020-10-26T15:57:28.186Z INFO  vopono::netns             > IP address of namespace as seen from host: 10.200.1.2
 2020-10-26T15:57:28.186Z INFO  vopono::netns             > IP address of host as seen from namespace: 10.200.1.1
 2020-10-26T15:57:28.186Z DEBUG vopono::util              > nft add table inet vopono_nat
 2020-10-26T15:57:28.188Z DEBUG vopono::util              > nft add chain inet vopono_nat postrouting { type nat hook postrouting priority 100 ; }
 2020-10-26T15:57:28.190Z DEBUG vopono::util              > nft add rule inet vopono_nat postrouting oifname wlan0 ip saddr 10.200.1.0/24 counter masquerade
 2020-10-26T15:57:28.192Z DEBUG vopono::util              > sysctl -q net.ipv4.ip_forward=1
 2020-10-26T15:57:28.193Z DEBUG vopono::wireguard         > Deserializing: 193.138.218.74 to Vec<IpAddr>
 2020-10-26T15:57:28.193Z DEBUG vopono::wireguard         > TOML config: WireguardConfig { interface: WireguardInterface { private_key: "REMOVED", address: [10.99.73.31/32, fc00:bbbb:bbbb:bb01::491f/128], dns: [V4(193.138.218.74)] }, peer: WireguardPeer { public_key: "m4jnogFbACz7LByjo++8z5+1WV0BuR1T7E1OWA+n8h0=", allowed_ips: [0.0.0.0/0, ::/0], endpoint: V4(193.138.218.130:51820) } }
 2020-10-26T15:57:28.193Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip link add custom_mull type wireguard
 2020-10-26T15:57:28.194Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull wg setconf custom_mull /tmp/vopono_nft.conf
 2020-10-26T15:57:28.231Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -4 address add 10.99.73.31/32 dev custom_mull
 2020-10-26T15:57:28.233Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -6 address add fc00:bbbb:bbbb:bb01::491f/128 dev custom_mull
 2020-10-26T15:57:28.235Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip link set mtu 1420 up dev custom_mull
 2020-10-26T15:57:28.273Z DEBUG vopono::dns_config        > Setting namespace vopono_custom_mull DNS server to 193.138.218.74
 2020-10-26T15:57:28.273Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull wg set custom_mull fwmark 51820
 2020-10-26T15:57:28.275Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -4 route add 0.0.0.0/0 dev custom_mull table 51820
 2020-10-26T15:57:28.277Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -4 rule add not fwmark 51820 table 51820
 2020-10-26T15:57:28.279Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -4 rule add table main suppress_prefixlength 0
 2020-10-26T15:57:28.281Z DEBUG vopono::util              > sysctl -q net.ipv4.conf.all.src_valid_mark=1
 2020-10-26T15:57:28.281Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -6 route add ::/0 dev custom_mull table 51820
 2020-10-26T15:57:28.283Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -6 rule add not fwmark 51820 table 51820
 2020-10-26T15:57:28.285Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull ip -6 rule add table main suppress_prefixlength 0
 2020-10-26T15:57:28.286Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull nft -f /tmp/vopono_nft.sh
 2020-10-26T15:57:28.289Z DEBUG vopono::wireguard         > Setting Wireguard killswitch....
 2020-10-26T15:57:28.289Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull nft add table inet vopono_custom_mull
 2020-10-26T15:57:28.290Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull nft add chain inet vopono_custom_mull output { type filter hook output priority -500 ; policy accept; }
 2020-10-26T15:57:28.292Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull nft add rule inet vopono_custom_mull output oifname != custom_mull mark != 51820 fib daddr type != local counter reject
 2020-10-26T15:57:28.294Z DEBUG vopono::netns             > Writing lockfile: /home/madhogs/.config/vopono/locks/vopono_custom_mull
 2020-10-26T15:57:28.294Z DEBUG vopono::netns             > Lockfile written: /home/madhogs/.config/vopono/locks/vopono_custom_mull/15293
 2020-10-26T15:57:28.338Z DEBUG vopono::netns             > ip netns exec vopono_custom_mull sudo -u madhogs bash
 2020-10-26T15:57:28.338Z INFO  vopono::exec              > Application bash launched in network namespace vopono_custom_mull with pid 15351

Inside the namespace, ip addr outputs the following

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: custom_mull: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 10.99.73.31/32 scope global custom_mull
       valid_lft forever preferred_lft forever
    inet6 fc00:bbbb:bbbb:bb01::491f/128 scope global 
       valid_lft forever preferred_lft forever
9: custom_mull_s@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ee:47:47:b6:de:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.200.1.2/24 scope global custom_mull_s
       valid_lft forever preferred_lft forever
    inet6 fe80::ec47:47ff:feb6:deee/64 scope link 
       valid_lft forever preferred_lft forever

Not sure what is causing this as I don't see any errors outputted by vopono. Do you have any ideas of what I can try to fix this?

Worrying iptables errors

(At first i thought that the "use bridge" configuration is being ignored. I realized that while the mullvad ipleak tool doesn't show it, it's connecting to a different server than the exit server. Still, more info in the documentation would be helpful, maybe more choice about the entry server location...)

However, in looking into this topic, I noticed that when pulling up a new namespace, some strange iptables errors happen - maybe it's expecting a different version of iptables? (I'm on Ubuntu 20.04, so it shouldn't be too outdated... iptables version is iptables v1.8.4 (legacy))

$ vopono exec --provider mullvad --server sweden "google-chrome"
 2021-03-07T16:02:30.112Z INFO  vopono::util > Calling sudo for elevated privileges, current user will be used as default user
 2021-03-07T16:02:30.211Z INFO  vopono::util > Chosen config: /home/dani/.config/vopono/mv/wireguard/sweden-se9.conf
 2021-03-07T16:02:30.214Z INFO  vopono::netns > Created new network namespace: vopono_mv_sweden
 2021-03-07T16:02:30.262Z INFO  vopono::netns > IP address of namespace as seen from host: 10.200.1.2
 2021-03-07T16:02:30.262Z INFO  vopono::netns > IP address of host as seen from namespace: 10.200.1.1
Bad argument `addrtype'
Try `iptables -h' or 'iptables --help' for more information.
Bad argument `addrtype'
Try `ip6tables -h' or 'ip6tables --help' for more information.
 2021-03-07T16:02:30.575Z INFO  vopono::exec  > Application google-chrome launched in network namespace vopono_mv_sweden with pid 93266

(google-chrome:93267): dbind-WARNING **: 17:02:30.823: Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-UPvZ3RgVN4: Connection refused
[93304:93304:0307/170230.874610:ERROR:sandbox_linux.cc(374)] InitializeSandbox() called with multiple threads in process gpu-process.
[93556:20:0307/170327.411124:ERROR:stun_port.cc(96)] Binding request timed out from [0:0:0:x:x:x:x:x]:37352 (any)
[93556:20:0307/170825.558446:ERROR:stun_port.cc(96)] Binding request timed out from [0:0:0:x:x:x:x:x]:57085 (any)
[93556:20:0307/171446.541568:ERROR:stun_port.cc(96)] Binding request timed out from [0:0:0:x:x:x:x:x]:48301 (any)

Custom NordVPN won't connect

I'm trying to use the --custom flag to use NordVPN via OpenVPN. However, it seems to get stuck on "Attempting to establish TCP connection..." and I'm trying to troubleshoot.

Here's the command I'm running:
vopono -v exec --custom /etc/openvpn/client/ovpn_tcp/ca1074.nordvpn.com.tcp.ovpn --protocol openvpn "bash"

The config files are downloaded straight from the NordVPN website. Here's the output I receive:

2021-01-26T00:17:45.635Z WARN  vopono > Could not parse PULSE_SERVER from pactl info output: Err(Could not parse pactl output!:
)
 2021-01-26T00:17:45.635Z DEBUG vopono::util > Existing namespaces: []
 2021-01-26T00:17:45.635Z DEBUG vopono::network_interface > ip addr
 2021-01-26T00:17:45.636Z DEBUG vopono::exec              > Interface: eno1
 2021-01-26T00:17:45.637Z DEBUG vopono::util              > Existing namespaces: []
 2021-01-26T00:17:45.637Z DEBUG vopono::util              > ip netns add vopono_custom_ca10
 2021-01-26T00:17:45.638Z INFO  vopono::netns             > Created new network namespace: vopono_custom_ca10
 2021-01-26T00:17:45.639Z DEBUG vopono::util              > Existing interfaces: 
 2021-01-26T00:17:45.640Z DEBUG vopono::util              > Assigned IPs: []
 2021-01-26T00:17:45.640Z DEBUG vopono::netns             > ip netns exec vopono_custom_ca10 ip addr add 127.0.0.1/8 dev lo
 2021-01-26T00:17:45.641Z DEBUG vopono::netns             > ip netns exec vopono_custom_ca10 ip link set lo up
 2021-01-26T00:17:45.643Z DEBUG vopono::veth_pair         > NetworkManager detected, adding custom_ca10_d to unmanaged devices
 2021-01-26T00:17:45.643Z DEBUG vopono::util              > nmcli connection reload
 2021-01-26T00:17:45.649Z DEBUG vopono::util              > ip link add custom_ca10_d type veth peer name custom_ca10_s
 2021-01-26T00:17:45.650Z DEBUG vopono::util              > ip link set custom_ca10_d up
 2021-01-26T00:17:45.651Z DEBUG vopono::util              > ip link set custom_ca10_s netns vopono_custom_ca10 up
 2021-01-26T00:17:45.693Z DEBUG vopono::util              > ip addr add 10.200.1.1/24 dev custom_ca10_d
 2021-01-26T00:17:45.694Z DEBUG vopono::netns             > ip netns exec vopono_custom_ca10 ip addr add 10.200.1.2/24 dev custom_ca10_s
 2021-01-26T00:17:45.695Z DEBUG vopono::netns             > ip netns exec vopono_custom_ca10 ip route add default via 10.200.1.1 dev custom_ca10_s
 2021-01-26T00:17:45.696Z INFO  vopono::netns             > IP address of namespace as seen from host: 10.200.1.2
 2021-01-26T00:17:45.696Z INFO  vopono::netns             > IP address of host as seen from namespace: 10.200.1.1
 2021-01-26T00:17:45.696Z DEBUG vopono::util              > iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eno1 -j MASQUERADE
 2021-01-26T00:17:45.722Z DEBUG vopono::util              > sysctl -q net.ipv4.ip_forward=1
 2021-01-26T00:17:45.723Z DEBUG vopono::dns_config        > Setting namespace vopono_custom_ca10 DNS server to 8.8.8.8
 2021-01-26T00:17:45.723Z INFO  vopono::openvpn           > Launching OpenVPN...
 2021-01-26T00:17:45.724Z DEBUG vopono::openvpn           > Found remotes: [Remote { host: IPv4(89.47.234.3), port: 443, protocol: TCP }]
 2021-01-26T00:17:45.724Z DEBUG vopono::netns             > ip netns exec vopono_custom_ca10 openvpn --config /etc/openvpn/client/ovpn_tcp/ca1074.nordvpn.com.tcp.ovpn --machine-readable-output --log /etc/netns/vopono_custom_ca10/openvpn.log
 2021-01-26T00:17:45.733Z DEBUG vopono::openvpn           > "1611620265.733128 40 DEPRECATED OPTION: --cipher set to \'AES-256-CBC\' but missing in --data-ciphers (AES-256-GCM:AES-128-GCM). Future OpenVPN version will ignore --cipher for cipher negotiations. Add \'AES-256-CBC\' to --data-ciphers or change --cipher \'AES-256-CBC\' to --data-ciphers-fallback \'AES-256-CBC\' to silence this warning.\n"
 2021-01-26T00:17:45.733Z DEBUG vopono::openvpn           > "1611620265.733315 1 OpenVPN 2.5.0 [git:makepkg/a73072d8f780e888+] x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Nov  6 2020\n"
 2021-01-26T00:17:45.733Z DEBUG vopono::openvpn           > "1611620265.733334 1 library versions: OpenSSL 1.1.1i  8 Dec 2020, LZO 2.10\n"
🔐 Enter Auth Username: ***************
🔐 Enter Auth Password: ************            
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851084 40 WARNING: --ping should normally be used with --ping-restart or --ping-exit\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851100 1 NOTE: --fast-io is disabled since we are not using UDP\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851429 14000002 Outgoing Control Channel Authentication: Using 512 bit message hash \'SHA512\' for HMAC authentication\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851435 14000002 Incoming Control Channel Authentication: Using 512 bit message hash \'SHA512\' for HMAC authentication\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851491 1 TCP/UDP: Preserving recently used remote address: [AF_INET]89.47.234.3:443\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851508 2b000003 Socket Buffers: R=[131072->131072] S=[16384->16384]\n"
 2021-01-26T00:17:51.851Z DEBUG vopono::openvpn           > "1611620271.851512 1 Attempting to establish TCP connection with [AF_INET]89.47.234.3:443 [nonblock]\n"
 2021-01-26T00:19:51.964Z DEBUG vopono::openvpn           > "1611620391.964795 1000021 TCP: connect to [AF_INET]89.47.234.3:443 failed: Connection timed out\n"
 2021-01-26T00:19:51.964Z DEBUG vopono::openvpn           > "1611620391.964870 1 SIGUSR1[connection failed(soft),init_instance] received, process restarting\n"
 2021-01-26T00:19:51.964Z DEBUG vopono::openvpn           > "1611620391.964884 21000003 Restart pause, 5 second(s)\n"

From there it repeats as it times out and retries.

And here's an output dump of some other potentially relevant info:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether b4:2e:99:f2:67:09 brd ff:ff:ff:ff:ff:ff
    altname enp8s0
    inet 192.168.0.39/24 brd 192.168.0.255 scope global dynamic noprefixroute eno1
       valid_lft 579391sec preferred_lft 579391sec
    inet6 2607:f2c0:ead4:8:e752:8f29:f11d:29f/64 scope global dynamic noprefixroute 
       valid_lft 578677sec preferred_lft 146677sec
    inet6 fe80::29e3:58d2:9f8a:acb0/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
14: custom_ca10_d@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 46:91:e7:e4:95:30 brd ff:ff:ff:ff:ff:ff link-netns vopono_custom_ca10
    inet 10.200.1.1/24 scope global custom_ca10_d
       valid_lft forever preferred_lft forever
    inet6 fe80::4491:e7ff:fee4:9530/64 scope link 
       valid_lft forever preferred_lft forever
$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether b4:2e:99:f2:67:09 brd ff:ff:ff:ff:ff:ff
    altname enp8s0
14: custom_ca10_d@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 46:91:e7:e4:95:30 brd ff:ff:ff:ff:ff:ff link-netns vopono_custom_ca10
$ ping -c 3 10.200.1.2
PING 10.200.1.2 (10.200.1.2) 56(84) bytes of data.
64 bytes from 10.200.1.2: icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from 10.200.1.2: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 10.200.1.2: icmp_seq=3 ttl=64 time=0.040 ms

--- 10.200.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2033ms
rtt min/avg/max/mdev = 0.040/0.044/0.052/0.005 ms
# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  10.200.1.0/24        anywhere
# ip netns exec vopono_custom_ca10 ip addr
Bind /etc/netns/vopono_custom_ca10/openvpn.log -> /etc/openvpn.log failed: No such file or directory
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
15: custom_ca10_s@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 22:cd:0a:e3:15:01 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.200.1.2/24 scope global custom_ca10_s
       valid_lft forever preferred_lft forever
    inet6 fe80::20cd:aff:fee3:1501/64 scope link 
       valid_lft forever preferred_lft forever
# ip netns exec vopono_custom_ca10 ip link
Bind /etc/netns/vopono_custom_ca10/openvpn.log -> /etc/openvpn.log failed: No such file or directory
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
15: custom_ca10_s@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 22:cd:0a:e3:15:01 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# ip netns exec vopono_custom_ca10 iptables -L
Bind /etc/netns/vopono_custom_ca10/openvpn.log -> /etc/openvpn.log failed: No such file or directory
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
# ip netns exec vopono_custom_ca10 ping -c 3 10.200.1.1
Bind /etc/netns/vopono_custom_ca10/openvpn.log -> /etc/openvpn.log failed: No such file or directory
PING 10.200.1.1 (10.200.1.1) 56(84) bytes of data.
64 bytes from 10.200.1.1: icmp_seq=1 ttl=64 time=0.039 ms
64 bytes from 10.200.1.1: icmp_seq=2 ttl=64 time=0.044 ms
64 bytes from 10.200.1.1: icmp_seq=3 ttl=64 time=0.040 ms

--- 10.200.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2015ms
rtt min/avg/max/mdev = 0.039/0.041/0.044/0.002 ms
# ip netns exec vopono_custom_ca10 ping -c 3 8.8.8.8
Bind /etc/netns/vopono_custom_ca10/openvpn.log -> /etc/openvpn.log failed: No such file or directory
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=25.3 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=194 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=116 time=21.5 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 21.547/80.282/194.017/80.436 ms

I don't see anything in particular there that jumps out at me -- the namespace seems to be set up, I can ping inside and outside just fine, and pinging the VPN server also seems to work. But vopono seems to just not be able to connect for some reason. The only particular error I see at all is this issue with trying to bind the openvpn.log, but I'm not sure if that's necessary or related. Any thoughts?

Edit: I should add that connecting directly via sudo openvpn works as expected, so again, it doesn't seem to be an issue with the VPN connection itself.

Unable to install on Ubuntu 20.04

I saw your post on Reddit and decided to try it out. I downloaded the latest from Git and ran install.sh and received the following error:

Installing vopono v0.1.2 (/home/darren/bin/vopono)
Updating crates.io index
Compiling compound_duration v1.2.0
Compiling libc v0.2.71
Compiling proc-macro2 v1.0.18
Compiling syn v1.0.33
Compiling serde_derive v1.0.114
Compiling getrandom v0.1.14
Compiling bitflags v1.2.1
Compiling serde v1.0.114
Compiling proc-macro-error-attr v1.0.3
Compiling byteorder v1.3.4
Compiling num-traits v0.2.12
Compiling proc-macro-error v1.0.3
Compiling num-integer v0.1.43
Compiling heck v0.3.1
Compiling ryu v1.0.5
Compiling memchr v2.3.3
Compiling anyhow v1.0.31
Compiling nix v0.17.0
Compiling walkdir v2.3.1
Compiling log v0.4.8
error[E0599]: no associated item named MAX found for type usize in the current scope
--> /home/darren/.cargo/registry/src/github.com-1ecc6299db9ec823/compound_duration-1.2.0/src/lib.rs:40:27
|
40 | (seconds & usize::MAX.try_into().unwrap())
| ^^^ associated item not found in usize
|
help: you are looking for the module in std, not the primitive type
|
40 | (seconds & std::usize::MAX.try_into().unwrap())
| ^^^^^^^^^^^^^^^

error[E0599]: no associated item named MAX found for type usize in the current scope
--> /home/darren/.cargo/registry/src/github.com-1ecc6299db9ec823/compound_duration-1.2.0/src/lib.rs:96:27
|
96 | (seconds & usize::MAX.try_into().unwrap())
| ^^^ associated item not found in usize
|
help: you are looking for the module in std, not the primitive type
|
96 | (seconds & std::usize::MAX.try_into().unwrap())
| ^^^^^^^^^^^^^^^

error[E0599]: no associated item named MAX found for type usize in the current scope
--> /home/darren/.cargo/registry/src/github.com-1ecc6299db9ec823/compound_duration-1.2.0/src/lib.rs:161:25
|
161 | (nanos & usize::MAX.try_into().unwrap()).try_into().unwrap()
| ^^^ associated item not found in usize
|
help: you are looking for the module in std, not the primitive type
|
161 | (nanos & std::usize::MAX.try_into().unwrap()).try_into().unwrap()
| ^^^^^^^^^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try rustc --explain E0599.
error: could not compile compound_duration.
warning: build failed, waiting for other jobs to finish...
error: failed to compile vopono v0.1.2 (/home/darren/bin/vopono), intermediate artifacts can be found at /home/darren/bin/vopono/target

Caused by:
build failed

I haven't used Rust before but am I reading this right that it is grabbing dependencies and building them so this is probably an issue upstream from you with the dependency compound_duration?

Please let me know if there are more logs I can provide or if Ubuntu is just not supported at this time.

Thanks!

Getting "Could not parse pactl output!" when using exec command

Verbose output looks like this:

./vopono -v exec --provider ProtonVPN --server us bash
 2020-11-30T18:08:03.306Z DEBUG vopono::pulseaudio > Setting PULSE_SERVER to /run/user/1000/pulse/native
 2020-11-30T18:08:03.306Z INFO  vopono::util       > Calling sudo for elevated privileges, current user will be used as default user
 2020-11-30T18:08:03.306Z DEBUG vopono::util       > Args: ["./vopono", "-v", "exec","--provider", "ProtonVPN", "--server", "us", "bash"]
Error: Could not parse pactl output!

Running pactl info as root fails for me, which seems to cause the error. I think this has to with running pulseaudio system-wide vs. per-user. As a workaround I added if std::env::var("PULSE_SERVER").is_err() { ... before get_pulse_server() gets called and now everything seems to work.

Clean up pseudo-downcasting from VpnProvider supertrait to subtraits with better solution

The idea was that we could then pass trait objects to the functions that use the VPN provider objects with dynamic dispatch (i.e. as Box<dyn VpnProvider>, and then check if the actual struct implements the necessary WireguardProvider or OpenVpnProvider traits depending on the command line arguments, and return an error if not.

The problem is that there is no way to try to downcast from a supertrait trait object to subtrait. That is we want something like:

fn myfn(obj: Box<dyn VpnProvider>) -> anyhow::Result<()> {
    let as_wireguard: Box<dyn WireguardProvider> = try_downcast<WireguardProvider>(obj)?;
    // ...
}

But no such function exists. As far as I can tell, the downcast-rs crate is for downcasting to concrete types, not subtrait trait objects. This forum post offers some possibilities using std::mem::transmute but it is unsafe, and not guaranteed to be stable (note the need for #[repr(C)] for example).

For now I worked around this by using the fact that we have the enum from the command line arguments, so we can use that to try to generate the trait object we want. But this is a painful workaround as we have a fair amount of boilerplate code and some extra allocation of trait objects.

Relevant code:

// Do this since we can't downcast from Provider to other trait objects
impl VpnProvider {
pub fn get_dyn_provider(&self) -> Box<dyn Provider> {
match self {
Self::PrivateInternetAccess => Box::new(pia::PrivateInternetAccess {}),
Self::Mullvad => Box::new(mullvad::Mullvad {}),
Self::TigerVpn => Box::new(tigervpn::TigerVPN {}),
Self::ProtonVpn => Box::new(protonvpn::ProtonVPN {}),
Self::MozillaVpn => Box::new(mozilla::MozillaVPN {}),
Self::Custom => unimplemented!("Custom provider uses separate logic"),
}
}
pub fn get_dyn_openvpn_provider(&self) -> anyhow::Result<Box<dyn OpenVpnProvider>> {
match self {
Self::PrivateInternetAccess => Ok(Box::new(pia::PrivateInternetAccess {})),
Self::Mullvad => Ok(Box::new(mullvad::Mullvad {})),
Self::TigerVpn => Ok(Box::new(tigervpn::TigerVPN {})),
Self::ProtonVpn => Ok(Box::new(protonvpn::ProtonVPN {})),
Self::MozillaVpn => Err(anyhow!("MozillaVPN only supports Wireguard!")),
Self::Custom => Err(anyhow!("Custom provider uses separate logic")),
}
}
pub fn get_dyn_wireguard_provider(&self) -> anyhow::Result<Box<dyn WireguardProvider>> {
match self {
Self::Mullvad => Ok(Box::new(mullvad::Mullvad {})),
Self::MozillaVpn => Ok(Box::new(mozilla::MozillaVPN {})),
Self::Custom => Err(anyhow!("Custom provider uses separate logic")),
_ => Err(anyhow!("Wireguard not implemented")),
}
}
pub fn get_dyn_shadowsocks_provider(&self) -> anyhow::Result<Box<dyn ShadowsocksProvider>> {
match self {
Self::Mullvad => Ok(Box::new(mullvad::Mullvad {})),
Self::Custom => Err(anyhow!("Start Shadowsocks manually for custom provider")),
_ => Err(anyhow!("Shadowsocks not supported")),
}
}
}

Custom OpenVPN Provider: --custom option must be path, not filename

I was trying out vopono, and basically tried to run this command:

$ vopono -v exec --custom custom_openvpn.ovpn --protocol openvpn "firefox"

However, this mysteriously faild with Error: Failed to launch OpenVPN - is openvpn installed? Caused by: No such file or directory (os error 2) - which was not very useful, since openvpn was installed, and all the referenced paths in the ip netns exec ... openvpn command existed.

After some digging, I found that working_dir in

let working_dir = PathBuf::from(config_file.as_path().parent().unwrap());
was an empty string, which caused the No such file or directory in std::process::Command. When using a relative path instead of just the filename (like the examples), it suddenly works.

This is somewhat uninituitive - I would have expected to be able to use a filename instead of a relative path, as most other CLI applications do. It would be helpful to either mention that the option must be a (relative or absolute) path, or to fallback to the current directory as working_dir if it is an empty string.

Change VpnProvider enum to be separate structs with traits

Refactor the VpnProvider enum to use a separate struct per provider, with traits for OpenVPN and Wireguard implementations - i.e. generating the config files for this provider.

So in the end we have a providers directory in src with the implementations for each provider.

Use dynamic dispatch for functions which currently take VpnProvider.

This will make it much easier to add more VPN providers (i.e. ProtonVPN) in the future.

Add automatic connection check

Add some sort of connection to check to run before launching the application to handle cases of bad Wireguard auth, etc.

Could be added as part of ApplicationWrapper or before it.

Accessing behind a reverse proxy.

I'm trying to use vopono for my transmission webservice, which runs its own webserver. I can access the address directly by its LAN IP, but if I try to access the address via an nginx reverse proxy I see the following error in the nginx log:

2021/02/24 23:11:17 [error] 503#503: *165 connect() failed (111: Unknown error) while connecting to upstream, client: <redacted>, server: <redacted>, request: "GET /transmission/web/javascript/transmission.js HTTP/1.1", upstream: "http://[::1]:9091/transmission/web/javascript/transmission.js", host: "<redacted>", referrer: "https://<redacted>/transmission/web/"

Based on that log I see that nginx is trying to connect to an IPv6 address. Indeed if I try to curl the address locally I get:

user@machine:~$ curl 127.0.0.1:9091/transmission/web
<h1>409: Conflict</h1><p>Your request had an invalid session-id header.</p><p>To fix this, follow these steps:<ol><li> When reading a response, get its X-Transmission-Session-Id header and remember it<li> Add the updated header to your outgoing requests<li> When you get this 409 error message, resend your request with the updated header</ol></p><p>This requirement has been added to help prevent <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF</a> attacks.</p><p><code>X-Transmission-Session-Id: <redacted></code></p>%                                                            

user@machine:~$ curl [::1]:9091/transmission/web    
curl: (7) Failed to connect to ::1 port 9091: Connection refused

Note that I don't care about the 409 response in the first command, it is just to show it communicates with the transmission service when using an IPv4 address. When trying to connect over IPv6 the connection is refused.

I figured changing this would work, and in the terminal it indeed works:

user@machine:~$ curl 127.0.0.1:9091/transmission/web
<h1>409: Conflict</h1><p>Your request had an invalid session-id header.</p><p>To fix this, follow these steps:<ol><li> When reading a response, get its X-Transmission-Session-Id header and remember it<li> Add the updated header to your outgoing requests<li> When you get this 409 error message, resend your request with the updated header</ol></p><p>This requirement has been added to help prevent <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF</a> attacks.</p><p><code>X-Transmission-Session-Id: <redacted></code></p>%                                                            

user@machine:~$ curl [::1]:9091/transmission/web    
<h1>409: Conflict</h1><p>Your request had an invalid session-id header.</p><p>To fix this, follow these steps:<ol><li> When reading a response, get its X-Transmission-Session-Id header and remember it<li> Add the updated header to your outgoing requests<li> When you get this 409 error message, resend your request with the updated header</ol></p><p>This requirement has been added to help prevent <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF</a> attacks.</p><p><code>X-Transmission-Session-Id: <redacted></code></p>%   

But unfortunately the situation behind nginx reverse proxy remains broken. I looked into disabling IPv6 for nginx, but I prefer not to go that route. Note that I also briefly tried an Apache configuration, which should be very similar, however that did work. My guess is that Apache does not try to route data over an IPv6 address.

Could the issue be some other socket binding in vopono to an IPv4 address only?

ps. the command I'm running:

vopono -v exec --no-killswitch --forward 9091 --user transmission --custom /etc/wireguard/mullvad.conf "/usr/bin/transmission-daemon -f --log-error"

Change OpenVPN code to use separate config files rather than server list

Current server list approach with fixed config per VPN provider has limitations - i.e. for PrivateInternetAccess where there are many configs available to the user, and they may use different ciphers in addition to the different ports and protocols.

This would make the sync code more similar to the Wireguard approach, and easier to maintain for different providers (as many provide .ovpn config files directly).

Gnome apps escape the jail

This is potentially a big issue. It seems to happen by "accident" pointing to the sandboxing provided by vopono being quite porose. Here's the terminal output:

$ vopono exec --provider mullvad --server sweden "qbittorrent"
 2021-03-07T16:18:39.239Z INFO  vopono::util > Calling sudo for elevated privileges, current user will be used as default user
[sudo] password for dani: 
 2021-03-07T16:18:47.062Z INFO  vopono::util > Chosen config: /home/dani/.config/vopono/mv/wireguard/sweden-se19.conf
 2021-03-07T16:18:47.065Z INFO  vopono::exec > Using existing namespace: vopono_mv_sweden, will not modify firewall rules
 2021-03-07T16:18:47.065Z INFO  vopono::netns > Using existing network namespace: vopono_mv_sweden
 2021-03-07T16:18:47.143Z INFO  vopono::exec  > Application qbittorrent launched in network namespace vopono_mv_sweden with pid 96151

(qbittorrent:96152): dbind-WARNING **: 17:18:47.176: Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-UPvZ3RgVN4: Connection refused
dani@gorillo:~$ 

As you see, the process exits. And after it's exited, the qBittorrent GUI comes online. And...

$ ps ax | grep qbittorrent
  87697 ?        Sl     0:28 qbittorrent

That PID is not 96151! Through some kind of trick, the sandboxed qBittorrent process fails, maybe due to the dbus refusal, and... a completely different, non-sandboxed process is pulled up.

The reason I know this to be true is that I tested it with the Mullvad ip leak tool, and it does not go through the vpn at all.

Handle cases where dependencies are missing

If wireguard-tools or openvpn are missing then warn the user and handle error from std::Process.

Make them mandatory in AUR packages, and add dependencies to README for other distros (Debian, Fedora, etc.)

Add iptables or nftables option

We should use only iptables or only nftables, favouring the later if nft is installed.

This means converting all existing iptables rules to nftables and vice versa, and making the option globally available somehow.

Bad argument `addrtype'

I have this error when running anythong on vopono. Do you know what is causing it?

~ % vopono exec --provider mullvad --server switzerland 'curl http://ip-api.com/json/'
 2020-12-04T12:46:48.856Z INFO  vopono::util > Calling sudo for elevated privileges, current user will be used as default user
 2020-12-04T12:46:50.033Z INFO  vopono::util > Chosen config: /home/egidio/.config/vopono/mv/wireguard/switzerland-ch8.conf
 2020-12-04T12:46:50.074Z INFO  vopono::netns > Created new network namespace: vopono_mv_switzerland
 2020-12-04T12:46:50.173Z INFO  vopono::netns > IP address of namespace as seen from host: 10.200.1.2
 2020-12-04T12:46:50.173Z INFO  vopono::netns > IP address of host as seen from namespace: 10.200.1.1
Bad argument `addrtype'
Try `iptables -h' or 'iptables --help' for more information.
Bad argument `addrtype'
Try `ip6tables -h' or 'ip6tables --help' for more information.
 2020-12-04T12:46:50.399Z INFO  vopono::exec  > Application curl http://ip-api.com/json/ launched in network namespace vopono_mv_switzerland with pid 40869
{"status":"success","country":"Switzerland","countryCode":"CH","region":"ZH","regionName":"Zurich","city":"Zurich","zip":"8152","lat":47.43,"lon":8.5718,"timezone":"Europe/Zurich","isp":"31173 Services AB","org":"31173 Services Switzerland","as":"AS39351 31173 Services AB","query":"193.32.127.230"}

Fix Mullvad sync requesting credentials twice

Will affect all providers with both Wireguard and OpenVPN, need way of sharing credentials between calls (i.e. cache in another struct and pass in rather than calling inside the config generation).

Also affects AzireVPN.

Fix additional clones when validating Wireguard private key in Dialoguer validate_with()

vopono uses the dialoguer crate for user input for the vopono sync command. The input is validated with the validate_with() method so that the user gets feedback immediately and can correct any errors.

However, the validate_with() method requires that the closure used has a static lifetime. This is problematic for checking whether the user-entered private key matches the chosen public key, since we need to include the previously-chosen public key in the closure - but this doesn't have a static lifetime.

For now this is worked around with extra clones (see devices_clone) but hopefully a better solution is possible. Perhaps the static lifetime restriction in dialoguer could also be relaxed (since we know the closure will terminate before we receive the input and continue).

Relevant code:

let devices_clone = devices.clone();
let private_key = Input::<String>::new()
.with_prompt(format!(
"Private key for {}",
&devices[selection].pubkey
))
.validate_with(move |private_key: &str| -> Result<(), &str> {
let private_key = private_key.trim();
if private_key.len() != 44 {
return Err("Expected private key length of 44 characters"
);

let info_clone = user_info.clone();
let private_key = Input::<String>::new()
.with_prompt(format!(
"Private key for {}",
user_info.wg_peers[selection].key.public
))
.validate_with(move |private_key: &str| -> Result<(), &str> {
let private_key = private_key.trim();
if private_key.len() != 44 {
return Err("Expected private key length of 44 characters"
);

Fix PulseAudio tunnelling for server running on host, outside network namespace

See https://wiki.archlinux.org/index.php/PulseAudio#On_the_server

Need to forward TCP port 4713 from host in to network namespace, and then set the host to be the Pulse Audio server address (i.e. 10.200.1.1).

Ideally we'd proxy localhost:4713 inside the namespace to the host address to avoid dealing with setting environment variables every time, but then we need a way of running the TCP proxy inside the namespace itself (currently we run it as part of vopono on the host, outside the namespace).

nmcli general reload doesn't exist

`sudo vopono -v exec --custom ./us-free-03.protonvpn.com.udp.ovpn --protocol openvpn "brave-browser"
[sudo] password for user:
2020-08-08T22:05:03.524Z DEBUG vopono::util > Existing namespaces: []
2020-08-08T22:05:03.561Z DEBUG vopono::network_interface > ip addr
2020-08-08T22:05:03.677Z DEBUG vopono > Interface: eth0
2020-08-08T22:05:03.693Z DEBUG vopono::util > Existing namespaces: []
2020-08-08T22:05:03.693Z DEBUG vopono::util > ip netns add vopono_cus_usfr
2020-08-08T22:05:03.736Z DEBUG vopono::util > Existing interfaces:
2020-08-08T22:05:04.026Z DEBUG vopono::util > Assigned IPs: []
2020-08-08T22:05:04.054Z DEBUG vopono::netns > ip netns exec vopono_cus_usfr ip addr add 127.0.0.1/8 dev lo
2020-08-08T22:05:04.145Z DEBUG vopono::netns > ip netns exec vopono_cus_usfr ip link set lo up
2020-08-08T22:05:04.249Z DEBUG vopono::veth_pair > NetworkManager detected, adding cus_usfr_d to unmanaged devices
2020-08-08T22:05:04.274Z DEBUG vopono::util > nmcli general reload
Error: argument 'reload' not understood. Try passing --help instead.
2020-08-08T22:05:04.729Z DEBUG vopono::util > ip netns delete vopono_cus_usfr
Error: Failed to reload NetworkManager configuration

Caused by:
Command failed: nmcli general reload`

Looking throw this documentation the correct command would be:
nmcli connection reload

I'm using debian and the .deb package provided on the realease page

Cannot resolve PIA address

Hi! I just tried vopono with my PIA subscription, but it appears that the connection isn't getting properly established, or something. The chrome window just complains that there's no internet available. Any ideas?

$ vopono -v exec --provider privateinternetaccess --server nz "/usr/bin/google-chrome-stable"
 2020-06-27T00:37:29.718Z DEBUG users::base > Running getuid
 2020-06-27T00:37:29.718Z DEBUG users::base > Running getpwuid_r for user #1000
 2020-06-27T00:37:29.718Z DEBUG users::base > Loading user with uid 1000
 2020-06-27T00:37:29.718Z DEBUG vopono::util > Cleaning dead lock files...
 2020-06-27T00:37:29.728Z INFO  vopono       > Calling sudo for elevated privileges, current user will be used as default user
 2020-06-27T00:37:29.728Z DEBUG vopono       > Args: ["vopono", "-v", "exec", "--provider", "privateinternetaccess", "--server", "nz", "/usr/bin/google-chrome-stable"]
 2020-06-27T00:37:29.734Z DEBUG vopono::util > Cleaning dead lock files...
 2020-06-27T00:37:29.745Z DEBUG vopono::network_interface > ip addr
 2020-06-27T00:37:29.745Z DEBUG vopono                    > Interface: eth0
 2020-06-27T00:37:29.746Z DEBUG vopono::util              > Existing namespaces: []
 2020-06-27T00:37:29.746Z DEBUG vopono::util              > ip netns add vopono_pia_nz
 2020-06-27T00:37:29.749Z DEBUG vopono::util              > Existing interfaces:
 2020-06-27T00:37:29.749Z DEBUG vopono::util              > Assigned IPs: []
 2020-06-27T00:37:29.749Z DEBUG vopono::vpn               > Read auth file: /home/chj/.config/vopono/pia/openvpn/auth.txt
 2020-06-27T00:37:29.749Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz ip addr add 127.0.0.1/8 dev lo
 2020-06-27T00:37:29.751Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz ip link set lo up
 2020-06-27T00:37:29.753Z DEBUG vopono::util              > ip link add _pia_nz_d type veth peer name _pia_nz_s
 2020-06-27T00:37:29.754Z DEBUG vopono::util              > ip link set _pia_nz_d up
 2020-06-27T00:37:29.755Z DEBUG vopono::util              > ip link set _pia_nz_s netns vopono_pia_nz up
 2020-06-27T00:37:29.805Z DEBUG vopono::util              > ip addr add 10.200.1.1/24 dev _pia_nz_d
 2020-06-27T00:37:29.806Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz ip addr add 10.200.1.2/24 dev _pia_nz_s
 2020-06-27T00:37:29.808Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz ip route add default via 10.200.1.1 dev _pia_nz_s
 2020-06-27T00:37:29.809Z DEBUG vopono::util              > iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
 2020-06-27T00:37:29.810Z DEBUG vopono::util              > sysctl -q net.ipv4.ip_forward=1
 2020-06-27T00:37:29.811Z DEBUG vopono::vpn               > Chosen server: nz.privateinternetaccess.com:1198
 2020-06-27T00:37:29.811Z DEBUG vopono::openvpn           > OpenVPN config: "/home/chj/.config/vopono/pia/openvpn/client.conf"
 2020-06-27T00:37:29.811Z INFO  vopono::openvpn           > Launching OpenVPN...
 2020-06-27T00:37:29.811Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz openvpn --config /home/chj/.config/vopono/pia/openvpn/client.conf --remote nz.privateinternetaccess.com 1198 --auth-user-pass /home/chj/.config/vopono/pia/openvpn/auth.txt --ca /home/chj/.config/vopono/pia/openvpn/ca.rsa.2048.crt --crl-verify /home/chj/.config/vopono/pia/openvpn/crl.rsa.2048.pem
Sat Jun 27 08:37:29 2020 OpenVPN 2.4.9 [git:makepkg/9b0dafca6c50b8bb+] x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Apr 20 2020
Sat Jun 27 08:37:29 2020 library versions: OpenSSL 1.1.1g  21 Apr 2020, LZO 2.10
Sat Jun 27 08:37:29 2020 CRL: loaded 1 CRLs from file /home/chj/.config/vopono/pia/openvpn/crl.rsa.2048.pem
Sat Jun 27 08:37:35 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
 2020-06-27T00:37:39.812Z DEBUG vopono                    > Checking that OpenVPN is running in namespace: vopono_pia_nz
 2020-06-27T00:37:39.821Z DEBUG vopono::netns             > Writing lockfile: /home/chj/.config/vopono/locks/vopono_pia_nz
 2020-06-27T00:37:39.821Z DEBUG vopono::netns             > Lockfile written: /home/chj/.config/vopono/locks/vopono_pia_nz/13727
 2020-06-27T00:37:39.822Z DEBUG vopono::netns             > ip netns exec vopono_pia_nz sudo -u chj /usr/bin/google-chrome-stable
[13761:13797:0627/083740.479124:ERROR:bus.cc(393)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[13761:13797:0627/083740.479163:ERROR:bus.cc(393)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[13798:13798:0627/083740.515594:ERROR:sandbox_linux.cc(374)] InitializeSandbox() called with multiple threads in process gpu-process.
Sat Jun 27 08:37:41 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:37:41 2020 Could not determine IPv4/IPv6 protocol
Sat Jun 27 08:37:41 2020 SIGUSR1[soft,init_instance] received, process restarting
[13761:13797:0627/083745.198126:ERROR:bus.cc(393)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[13761:13797:0627/083745.198152:ERROR:bus.cc(393)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
Sat Jun 27 08:37:51 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:37:57 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:37:57 2020 Could not determine IPv4/IPv6 protocol
Sat Jun 27 08:37:57 2020 SIGUSR1[soft,init_instance] received, process restarting
Fontconfig error: Cannot load default config file: No such file: (null)
Sat Jun 27 08:38:06 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:12 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:12 2020 Could not determine IPv4/IPv6 protocol
Sat Jun 27 08:38:12 2020 SIGUSR1[soft,init_instance] received, process restarting
Sat Jun 27 08:38:23 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:29 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:29 2020 Could not determine IPv4/IPv6 protocol
Sat Jun 27 08:38:29 2020 SIGUSR1[soft,init_instance] received, process restarting
Sat Jun 27 08:38:40 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:46 2020 RESOLVE: Cannot resolve host address: nz.privateinternetaccess.com:1198 (Temporary failure in name resolution)
Sat Jun 27 08:38:46 2020 Could not determine IPv4/IPv6 protocol
Sat Jun 27 08:38:46 2020 SIGUSR1[soft,init_instance] received, process restarting
 2020-06-27T00:38:50.102Z DEBUG vopono::openvpn           > Killed OpenVPN (pid: 13756)
 2020-06-27T00:38:50.102Z DEBUG vopono::util              > ip link delete _pia_nz_d
[0627/083850.104156:ERROR:nacl_helper_linux.cc(308)] NaCl helper process running without a sandbox!
Most likely you need to configure your SUID sandbox correctly
 2020-06-27T00:38:50.164Z DEBUG vopono::util              > iptables -t nat -D POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
 2020-06-27T00:38:50.165Z DEBUG vopono::util              > ip netns delete vopono_pia_nz

Clean up Box::leak() calls for skipping destructors

vopono uses the Drop trait to automatically clean up resources when the relevant structs go out of scope. However, sometimes we don't want to trigger these destructors but still have the structs go out of scope - for example, if we have multiple vopono processes running applications in the same network namespace, then we don't want to destroy the network namespace until the final application has terminated. So if lockfiles still exist, we need to prevent the clean-up destructors from firing for the vopono instances terminated first.

For now this is done by putting the relevant structs in a Box, and then calling Box::leak() (docs here). This works but feels a bit clunky when dealing with multiple structs/fields (e.g. here preventing the destructors when another vopono instance is using the same namespace).

One possible alternative might be to use std::mem::ManuallyDrop, however then the drop() method is unsafe, so this might end up being even less ergonomic.

Note that for the vopono list and cleaning dead namespaces cases we could at least deserialize to a read-only version of the LockFile struct:

vopono/src/util/mod.rs

Lines 240 to 242 in 014cd42

// TODO - deserialize to struct without Drop instead
let lock_namespaces = Box::new(lock_namespaces);
Box::leak(lock_namespaces);

vopono/src/list.rs

Lines 45 to 47 in b754420

// Avoid triggering Drop for these namespaces
let namespaces = Box::new(namespaces);
Box::leak(namespaces);

But for the usual Drop in netns.rs we would need a real solution:

vopono/src/netns.rs

Lines 305 to 324 in 4ebf4b6

debug!("Skipping destructors since other vopono instance using this namespace!");
let openvpn = self.openvpn.take();
let openvpn = Box::new(openvpn);
Box::leak(openvpn);
let veth_pair = self.veth_pair.take();
let veth_pair = Box::new(veth_pair);
Box::leak(veth_pair);
let dns_config = self.dns_config.take();
let dns_config = Box::new(dns_config);
Box::leak(dns_config);
let wireguard = self.wireguard.take();
let wireguard = Box::new(wireguard);
Box::leak(wireguard);
let iptables = self.iptables.take();
let iptables = Box::new(iptables);
Box::leak(iptables);

Use unshare, setns, netlink etc. to create and configure netns

Unshare: https://stackoverflow.com/questions/10730838/how-to-create-multiple-network-namespace-from-a-single-process-instance
https://man7.org/linux/man-pages/man1/unshare.1.html
https://crates.io/crates/unshare (does this support modifying current process, i.e. without spawn)
https://stackoverflow.com/questions/10730838/how-to-create-multiple-network-namespace-from-a-single-process-instance
https://stackoverflow.com/questions/30142799/how-to-add-a-name-to-namespace

setns: https://man7.org/linux/man-pages/man2/setns.2.html

Also in nix:
https://docs.rs/nix/0.19.0/nix/sched/fn.setns.html
https://docs.rs/nix/0.19.0/nix/sched/fn.unshare.html

Rust example: https://github.com/johnae/netns-exec/blob/master/src/main.rs

Bind mount (to keep netns alive): https://man7.org/linux/man-pages/man2/mount.2.html
https://crates.io/crates/sys-mount

Plan:

Might be easier with separate thread anyway, to manage executions inside and outside the namespace.


exec (can probably just use std::Process): https://linux.die.net/man/3/execvpe

netlink (for ip link): http://ifeanyi.co/posts/linux-namespaces-part-4/
https://crates.io/crates/rtnetlink (routing)
https://crates.io/crates/netlink-sys (netlink sockets)

libnftnl (for nftables): https://crates.io/crates/nftnl

libiptc (for iptables): https://crates.io/crates/libiptc-sys

wgctrl (Wireguard i.e. wg conf): https://crates.io/crates/wgctrl-rs

sysctl: https://crates.io/crates/sysctl

nmcli (Network Manager): https://crates.io/crates/networkmanager
https://docs.rs/networkmanager/0.3.2/networkmanager/devices/trait.Any.html#tymethod.set_managed

sudo: https://crates.io/crates/sudo - use instead of just checking uid - but how to verify capabilities?

Some C examples: https://github.com/Intika-Linux-Namespace

Add other popular VPN providers

With Wireguard:

  • TorGuard
  • Windscribe
  • PrivateInternetAccess (add Wireguard config generation)

OpenVPN only:

  • Tunnelbear
  • NordVPN
  • ExpressVPN
  • Surfshark
  • Cyberghost (also owned by PIA owner Kape Technologies)

Other:

  • RiseupVPN

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.