When your ISP is actively blocking websites you need to access, one can use a VPN connection to circumvent the blockage. With this openvpn-netns script, it is possible to use the local network and a direct internet connection through the ISP as usual, and additionally use a VPN connection to tunnel to blocked websites for affected apps and services.
This is an example configuration running on a Raspberry Pi and is using Surfshark as the VPN provider, using the OpenVPN configuration files that are available from them.
Note: This version of the script does require root privileges to work.
As can be been seen above, the local port 32400 can still be made available on the local network (and ISP's IP-address through port forwarding)!
For services requiring the VPN connection, it is also possible to make their port (port 9091 in the schematic above) available on the local network and ISP's IP-address, through a special socat command and, optionally, a separate IP-address (see further below).
Once the openvpn-netns script is installed, and the OpenVPN service is started, it will:
- open a VPN connection tunneled through the virtual tun0 network adapter, which has a local IP-address in a different IP-range as the LAN, where all Internet bound traffic is routed through now. This is happening in a default OpenVPN installation already.
- set-up a Linux network namespace called vpn
- move the tun0 network adapter into the vpn network namespace
- configure DSN servers for the vpn network namespace
- optionally, create a virtual network adapter macvlan0 (MACVLAN in Bridge mode) between the physical LAN eth0 network adapter and the apps and services that need to access the Internet through the VPN tunnel. It is will have a separate IP-address which is accessible on the local network (Note:
ping
will not work on this IP-address, it will only have the exposed ports open, and there is no ICMP Echo Requests service running either).
When the OpenVPN service is stopped, the script will:
- close the active VPN connection and remove the tun0 network adapter
- remove the vpn network namespace
- remove the macvlan0 network adapter
-
First, install the OpenVPN service on Linux.
Typically using the
sudo apt-get install openvpn
command. The complete instructions for the Raspberry Pi are here -
Elevate your permissions to root to be able to do the next steps.
sudo -i
-
Install the configuration file for OpenVPN that will allow it to connect the VPN provider; for Surfshark these can be downloaded here on the Locations tab.
Download, e.g. with
wget
, and save the selected configuration under the/etc/openvpn/
folder. Make sure that the name of the configuration file ends with the.conf
, e.g./etc/openvpn/ch-zur.prod.surfshark.com_udp.conf
, that will allow OpenVPN to find it automatically, however we will still point to the configuration file directly in the next steps. -
Create a credentials file for OpenVPN, according to your VPN provider's instructions; for Surfshark this can be set-up on the Credentials tab on the same page.
These commands will create it under
/etc/openvpn/credentials.txt
echo "CHANGE TO YOUR USERNAME" > /etc/openvpn/credentials.txt echo "CHANGE TO YOUR PASSWORD" >> /etc/openvpn/credentials.txt
-
Next, point to the OpenVPN configuration file in OpenVPN service file. Typically in a systemd-based Linux, the service file for the OpenVPN service is located is
/etc/systemd/system/openvpn.service
.We need to update the
ExecStart
line in the[Service]
section to make sure the OpenVPN service will use the selected configuration file. E.g. for/etc/openvpn/ch-zur.prod.surfshark.com_udp.conf
this would become:ExecStart=/usr/sbin/openvpn --config /etc/openvpn/ch-zur.prod.surfshark.com_udp.conf
-
Test if your VPN connection is working by starting the OpenVPN service: e.g.
sudo systemctl start openvpn
. The whole host Internet access should now go through the VPN; see if thetraceroute www.google.com
command lists the VPN provider's domain names. Stop the service before proceedingsudo systemctl stop openvpn
-
Reconfigure OpenVPN service file further, so that it has to permissions to set-up network namespace (i.e. the same file as referenced in step 5 above) further with these lines in the
[Service]
section:CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_LINUX_IMMUTABLE CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BROADCAST CAP_NET_ADMIN CAP_NET_RAW CAP_IPC_LOCK CAP_IPC_OWNER CAP_SYS_MODULE CAP_SYS_ROWIO CAP_SYS_CHROOT CAP_SYS_PTRACE CAP_SYS_PACCT CAP_SYS_ADMIN CapabilityBoundingSet=CAP_SYS_BOOT CAP_SYS_NICE CAP_SYS_RESOURCE CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_MKNOD CAP_LEASE CAP_AUDIT_WRITE CAP_AUDIT_CONTROL CAP_SETFCAP CAP_MAC_OVERRIDE CapabilityBoundingSet=CAP_MAC_ADMIN CAP_SYSLOG CAP_WAKE_ALARM CAP_BLOCK_SUSPEND CAP_AUDIT_READ LimitNPROC=10 DeviceAllow=/dev/null rw DeviceAllow=/dev/net/tun rw
Note: the complete openvpn.service file is in this repository.
-
Copy the
netns.sh
to/etc/openvpn/
directory and runchmod +x /etc/openvpn/netns.sh
to allow it be executed. -
To enable the optional macvlan0 network adapter, which exposes ports from services in the vpn network namespace on the LAN, though its separate IP-address, the
netns.sh
file needs to be modified. Uncomment the following lines, by removing the first # and space after it:
# echo "add macvlan0 interface and link it to eth0 interface as bridge"
# ip link add macvlan0 link eth0 type macvlan mode bridge
# echo "put macvlan0 interface into netns vpn"
# ip link set macvlan0 netns vpn
# echo "enable macvlan0 interface in netns vpn"
# ip netns exec vpn ip link set macvlan0 up
# echo "configure macvlan0 interface with 192.168.0.50 in netns vpn"
# ip netns exec vpn ip addr add 192.168.0.50/24 dev macvlan0
Change the IP-address on which this new network adapter macvlan0 will listen on the last two lines; it must be in the local network's IP-range - e.g. 192.168.1.236:
These lines will look like:
echo "add macvlan0 interface and link it to eth0 interface as bridge"
ip link add macvlan0 link eth0 type macvlan mode bridge
echo "put macvlan0 interface into netns vpn"
ip link set macvlan0 netns vpn
echo "enable macvlan0 interface in netns vpn"
ip netns exec vpn ip link set macvlan0 up
echo "configure macvlan0 interface with 192.168.1.236 in netns vpn"
ip netns exec vpn ip addr add 192.168.1.236/24 dev macvlan0
- Modify the OpenVPN configuration file (same file as in step 3): the following lines have to be added, before the
<ca>
line or at the very end:
script-security 2
auth-user-pass /etc/openvpn/credentials.txt
writepid /run/openvpn/openvpn.pid
route-noexec
route-nopull
route-up /etc/openvpn/netns.sh
ifconfig-noexec
up /etc/openvpn/netns.sh
down /etc/openvpn/netns.sh
- Start the OpenVPN service:
sudo systemctl start openvpn
Note: the current version requires you to run as root
. Suggestions on how run as a regular user are welcome!
You need to sudo
a call to ip netns exec vpn
, which is then completed with command you want to run:
sudo ip netns exec vpn traceroute www.google.com
For systemd-services, the service file needs to be modified: first add Requires=openvpn.service
in the [Unit]
-section, and then add ExecStartPre=/bin/sleep 30
to [Service]
for a wait time, e.g. 30 seconds, that will wait for the OpenVPN service setup the network namespace, and finally modify the ExecStart=
line to ExecStart=/usr/bin/sudo /sbin/ip netns exec vpn /usr/bin/sudo -u $USER <start command>
Example:
[Unit]
...
After=network.target
Requires=openvpn.service
...
[Service]
...
User=pi
Group=pi
ExecStartPre=/bin/sleep 30
ExecStart=/usr/bin/sudo /sbin/ip netns exec vpn /usr/bin/sudo -u $USER /opt/Service/service_launcher.sh
...
When an exposing a TCP-port that is only available within the network namespace, you might want to expose it on the local network. It is automatically available through the IP-address of the macvlan0 adapter when it is used.
Otherwise, the following command will expose a TCP-port 9091 on the IP-address of the eth0
-network adapter:
sudo socat tcp-listen:9091,fork,reuseaddr exec:'ip netns exec vpn socat STDIO tcp-connect\:127.0.0.1\:9091',nofork &
# ip link show
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether dc:a6:32:57:39:cb brd ff:ff:ff:ff:ff:ff
# ip netns exec vpn ip link show
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
9: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 100
link/none
32: macvlan0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether da:03:45:4c:4a:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0
TUN/TAP device tun0 opened
TUN/TAP TX queue length set to 100
/etc/openvpn/scripts/netns.sh tun0 1500 1584 10.9.9.218 255.255.255.0 init
vpn netns script called - up
Param 1 = tun0
Param 2 = 1500
Param 3 = 1584
Param 4 = 10.9.9.218
creating netns vpn
putting openvpn in netns vpn
configuring route for openvpn
configuring netns vpn dsn settings
enable loopback interface in netns vpn
add macvlan0 interface and link it to eth0 interface as bridge
put macvlan0 interface into netns vpn
enable macvlan0 interface in netns vpn
ip netns exec vpn ip link set macvlan0 up
configure macvlan0 interface with 192.168.1.236 in netns vpn
Initialization Sequence Completed