Trying to run OpenVPN in Network Namespace












2















I want certain apps to access the internet via OpenVPN. I found a solution in the final answer/comment at the bottom of this question/thread here:
Feed all traffic through OpenVPN for a specific network namespace only



I am quoting that post, the problem I am having is stated at the bottom:



You can start the OpenVPN link inside a namespace and then run every command you want to use that OpenVPN link inside the namespace. Details on how to do it (not my work) here:



http://www.naju.se/articles/openvpn-netns.html



I tried it and it does work; the idea is to provide a custom script to carry out the up and route-up phases of the OpenVPN connection inside a specific namespace instead of the global one. I quote from the above link just in case it goes offline in the future:



First create an --up script for OpenVPN. This script will create the VPN tunnel interface inside a network namespace called vpn, instead of the default namespace.

$ cat > netns-up << EOF
#!/bin/sh
case $script_type in
up)
ip netns add vpn
ip netns exec vpn ip link set dev lo up
mkdir -p /etc/netns/vpn
echo "nameserver 8.8.8.8" > /etc/netns/vpn/resolv.conf
ip link set dev "$1" up netns vpn mtu "$2"
ip netns exec vpn ip addr add dev "$1"
"$4/${ifconfig_netmask:-30}"
${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
test -n "$ifconfig_ipv6_local" &&
ip netns exec vpn ip addr add dev "$1"
"$ifconfig_ipv6_local"/112
;;
route-up)
ip netns exec vpn ip route add default via "$route_vpn_gateway"
test -n "$ifconfig_ipv6_remote" &&
ip netns exec vpn ip route add default via
"$ifconfig_ipv6_remote"
;;
down)
ip netns delete vpn
;;
esac


Then start OpenVPN and tell it to use our --up script instead of executing ifconfig and route.



openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up


Now you can start programs to be tunneled like this:



ip netns exec vpn command


The only catch is that you need to be root to invoke ip netns exec ... and maybe you do not want your application to run as root. The solution is simple:



sudo ip netns exec vpn sudo -u $(whoami) command


MY PROBLEM:



When I try to run the openvpn command which calls the netns-up script, I get two errors:



:/etc/openvpn$ sudo openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up --config za1.nordvpn.com.tcp443.ovpn
(..)

Tue Mar 22 00:10:56 2016 [vpn-za.nordvpn.com] Peer Connection Initiated with [AF_INET]154.127.61.142:443
Tue Mar 22 00:10:59 2016 SENT CONTROL [vpn-za.nordvpn.com]: 'PUSH_REQUEST' (status=1)
Tue Mar 22 00:10:59 2016 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 78.46.223.24,dhcp-option DNS 162.242.211.137,route 10.7.7.1,topology net30,ping 5,ping-restart 30,ifconfig 10.7.7.102 10.7.7.101'
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: timers and/or timeouts modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ifconfig/up options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: route options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Tue Mar 22 00:10:59 2016 ROUTE_GATEWAY 192.168.1.254/255.255.255.0 IFACE=eth0 HWADDR=b8:27:eb:39:7e:46
Tue Mar 22 00:10:59 2016 TUN/TAP device tun0 opened
Tue Mar 22 00:10:59 2016 TUN/TAP TX queue length set to 100
Tue Mar 22 00:10:59 2016 netns-up tun0 1500 1592 10.7.7.102 10.7.7.101 init
Tue Mar 22 00:10:59 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Tue Mar 22 00:10:59 2016 Exiting due to fatal error


I tried re-creating the netns-up script with and without sudo but it didn't help. What am I doing wrong?










share|improve this question

























  • So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

    – zilexa
    Mar 21 '16 at 23:06











  • It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

    – zilexa
    Mar 29 '16 at 13:28











  • This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

    – Felix
    Sep 14 '16 at 9:28











  • Yes absolutely!

    – zilexa
    Dec 17 '16 at 22:58
















2















I want certain apps to access the internet via OpenVPN. I found a solution in the final answer/comment at the bottom of this question/thread here:
Feed all traffic through OpenVPN for a specific network namespace only



I am quoting that post, the problem I am having is stated at the bottom:



You can start the OpenVPN link inside a namespace and then run every command you want to use that OpenVPN link inside the namespace. Details on how to do it (not my work) here:



http://www.naju.se/articles/openvpn-netns.html



I tried it and it does work; the idea is to provide a custom script to carry out the up and route-up phases of the OpenVPN connection inside a specific namespace instead of the global one. I quote from the above link just in case it goes offline in the future:



First create an --up script for OpenVPN. This script will create the VPN tunnel interface inside a network namespace called vpn, instead of the default namespace.

$ cat > netns-up << EOF
#!/bin/sh
case $script_type in
up)
ip netns add vpn
ip netns exec vpn ip link set dev lo up
mkdir -p /etc/netns/vpn
echo "nameserver 8.8.8.8" > /etc/netns/vpn/resolv.conf
ip link set dev "$1" up netns vpn mtu "$2"
ip netns exec vpn ip addr add dev "$1"
"$4/${ifconfig_netmask:-30}"
${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
test -n "$ifconfig_ipv6_local" &&
ip netns exec vpn ip addr add dev "$1"
"$ifconfig_ipv6_local"/112
;;
route-up)
ip netns exec vpn ip route add default via "$route_vpn_gateway"
test -n "$ifconfig_ipv6_remote" &&
ip netns exec vpn ip route add default via
"$ifconfig_ipv6_remote"
;;
down)
ip netns delete vpn
;;
esac


Then start OpenVPN and tell it to use our --up script instead of executing ifconfig and route.



openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up


Now you can start programs to be tunneled like this:



ip netns exec vpn command


The only catch is that you need to be root to invoke ip netns exec ... and maybe you do not want your application to run as root. The solution is simple:



sudo ip netns exec vpn sudo -u $(whoami) command


MY PROBLEM:



When I try to run the openvpn command which calls the netns-up script, I get two errors:



:/etc/openvpn$ sudo openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up --config za1.nordvpn.com.tcp443.ovpn
(..)

Tue Mar 22 00:10:56 2016 [vpn-za.nordvpn.com] Peer Connection Initiated with [AF_INET]154.127.61.142:443
Tue Mar 22 00:10:59 2016 SENT CONTROL [vpn-za.nordvpn.com]: 'PUSH_REQUEST' (status=1)
Tue Mar 22 00:10:59 2016 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 78.46.223.24,dhcp-option DNS 162.242.211.137,route 10.7.7.1,topology net30,ping 5,ping-restart 30,ifconfig 10.7.7.102 10.7.7.101'
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: timers and/or timeouts modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ifconfig/up options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: route options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Tue Mar 22 00:10:59 2016 ROUTE_GATEWAY 192.168.1.254/255.255.255.0 IFACE=eth0 HWADDR=b8:27:eb:39:7e:46
Tue Mar 22 00:10:59 2016 TUN/TAP device tun0 opened
Tue Mar 22 00:10:59 2016 TUN/TAP TX queue length set to 100
Tue Mar 22 00:10:59 2016 netns-up tun0 1500 1592 10.7.7.102 10.7.7.101 init
Tue Mar 22 00:10:59 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Tue Mar 22 00:10:59 2016 Exiting due to fatal error


I tried re-creating the netns-up script with and without sudo but it didn't help. What am I doing wrong?










share|improve this question

























  • So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

    – zilexa
    Mar 21 '16 at 23:06











  • It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

    – zilexa
    Mar 29 '16 at 13:28











  • This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

    – Felix
    Sep 14 '16 at 9:28











  • Yes absolutely!

    – zilexa
    Dec 17 '16 at 22:58














2












2








2


2






I want certain apps to access the internet via OpenVPN. I found a solution in the final answer/comment at the bottom of this question/thread here:
Feed all traffic through OpenVPN for a specific network namespace only



I am quoting that post, the problem I am having is stated at the bottom:



You can start the OpenVPN link inside a namespace and then run every command you want to use that OpenVPN link inside the namespace. Details on how to do it (not my work) here:



http://www.naju.se/articles/openvpn-netns.html



I tried it and it does work; the idea is to provide a custom script to carry out the up and route-up phases of the OpenVPN connection inside a specific namespace instead of the global one. I quote from the above link just in case it goes offline in the future:



First create an --up script for OpenVPN. This script will create the VPN tunnel interface inside a network namespace called vpn, instead of the default namespace.

$ cat > netns-up << EOF
#!/bin/sh
case $script_type in
up)
ip netns add vpn
ip netns exec vpn ip link set dev lo up
mkdir -p /etc/netns/vpn
echo "nameserver 8.8.8.8" > /etc/netns/vpn/resolv.conf
ip link set dev "$1" up netns vpn mtu "$2"
ip netns exec vpn ip addr add dev "$1"
"$4/${ifconfig_netmask:-30}"
${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
test -n "$ifconfig_ipv6_local" &&
ip netns exec vpn ip addr add dev "$1"
"$ifconfig_ipv6_local"/112
;;
route-up)
ip netns exec vpn ip route add default via "$route_vpn_gateway"
test -n "$ifconfig_ipv6_remote" &&
ip netns exec vpn ip route add default via
"$ifconfig_ipv6_remote"
;;
down)
ip netns delete vpn
;;
esac


Then start OpenVPN and tell it to use our --up script instead of executing ifconfig and route.



openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up


Now you can start programs to be tunneled like this:



ip netns exec vpn command


The only catch is that you need to be root to invoke ip netns exec ... and maybe you do not want your application to run as root. The solution is simple:



sudo ip netns exec vpn sudo -u $(whoami) command


MY PROBLEM:



When I try to run the openvpn command which calls the netns-up script, I get two errors:



:/etc/openvpn$ sudo openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up --config za1.nordvpn.com.tcp443.ovpn
(..)

Tue Mar 22 00:10:56 2016 [vpn-za.nordvpn.com] Peer Connection Initiated with [AF_INET]154.127.61.142:443
Tue Mar 22 00:10:59 2016 SENT CONTROL [vpn-za.nordvpn.com]: 'PUSH_REQUEST' (status=1)
Tue Mar 22 00:10:59 2016 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 78.46.223.24,dhcp-option DNS 162.242.211.137,route 10.7.7.1,topology net30,ping 5,ping-restart 30,ifconfig 10.7.7.102 10.7.7.101'
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: timers and/or timeouts modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ifconfig/up options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: route options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Tue Mar 22 00:10:59 2016 ROUTE_GATEWAY 192.168.1.254/255.255.255.0 IFACE=eth0 HWADDR=b8:27:eb:39:7e:46
Tue Mar 22 00:10:59 2016 TUN/TAP device tun0 opened
Tue Mar 22 00:10:59 2016 TUN/TAP TX queue length set to 100
Tue Mar 22 00:10:59 2016 netns-up tun0 1500 1592 10.7.7.102 10.7.7.101 init
Tue Mar 22 00:10:59 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Tue Mar 22 00:10:59 2016 Exiting due to fatal error


I tried re-creating the netns-up script with and without sudo but it didn't help. What am I doing wrong?










share|improve this question
















I want certain apps to access the internet via OpenVPN. I found a solution in the final answer/comment at the bottom of this question/thread here:
Feed all traffic through OpenVPN for a specific network namespace only



I am quoting that post, the problem I am having is stated at the bottom:



You can start the OpenVPN link inside a namespace and then run every command you want to use that OpenVPN link inside the namespace. Details on how to do it (not my work) here:



http://www.naju.se/articles/openvpn-netns.html



I tried it and it does work; the idea is to provide a custom script to carry out the up and route-up phases of the OpenVPN connection inside a specific namespace instead of the global one. I quote from the above link just in case it goes offline in the future:



First create an --up script for OpenVPN. This script will create the VPN tunnel interface inside a network namespace called vpn, instead of the default namespace.

$ cat > netns-up << EOF
#!/bin/sh
case $script_type in
up)
ip netns add vpn
ip netns exec vpn ip link set dev lo up
mkdir -p /etc/netns/vpn
echo "nameserver 8.8.8.8" > /etc/netns/vpn/resolv.conf
ip link set dev "$1" up netns vpn mtu "$2"
ip netns exec vpn ip addr add dev "$1"
"$4/${ifconfig_netmask:-30}"
${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
test -n "$ifconfig_ipv6_local" &&
ip netns exec vpn ip addr add dev "$1"
"$ifconfig_ipv6_local"/112
;;
route-up)
ip netns exec vpn ip route add default via "$route_vpn_gateway"
test -n "$ifconfig_ipv6_remote" &&
ip netns exec vpn ip route add default via
"$ifconfig_ipv6_remote"
;;
down)
ip netns delete vpn
;;
esac


Then start OpenVPN and tell it to use our --up script instead of executing ifconfig and route.



openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up


Now you can start programs to be tunneled like this:



ip netns exec vpn command


The only catch is that you need to be root to invoke ip netns exec ... and maybe you do not want your application to run as root. The solution is simple:



sudo ip netns exec vpn sudo -u $(whoami) command


MY PROBLEM:



When I try to run the openvpn command which calls the netns-up script, I get two errors:



:/etc/openvpn$ sudo openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up --config za1.nordvpn.com.tcp443.ovpn
(..)

Tue Mar 22 00:10:56 2016 [vpn-za.nordvpn.com] Peer Connection Initiated with [AF_INET]154.127.61.142:443
Tue Mar 22 00:10:59 2016 SENT CONTROL [vpn-za.nordvpn.com]: 'PUSH_REQUEST' (status=1)
Tue Mar 22 00:10:59 2016 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 78.46.223.24,dhcp-option DNS 162.242.211.137,route 10.7.7.1,topology net30,ping 5,ping-restart 30,ifconfig 10.7.7.102 10.7.7.101'
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: timers and/or timeouts modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ifconfig/up options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: route options modified
Tue Mar 22 00:10:59 2016 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Tue Mar 22 00:10:59 2016 ROUTE_GATEWAY 192.168.1.254/255.255.255.0 IFACE=eth0 HWADDR=b8:27:eb:39:7e:46
Tue Mar 22 00:10:59 2016 TUN/TAP device tun0 opened
Tue Mar 22 00:10:59 2016 TUN/TAP TX queue length set to 100
Tue Mar 22 00:10:59 2016 netns-up tun0 1500 1592 10.7.7.102 10.7.7.101 init
Tue Mar 22 00:10:59 2016 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Tue Mar 22 00:10:59 2016 Exiting due to fatal error


I tried re-creating the netns-up script with and without sudo but it didn't help. What am I doing wrong?







linux networking openvpn namespace






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 13 '17 at 12:37









Community

1




1










asked Mar 19 '16 at 12:19









zilexazilexa

113




113













  • So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

    – zilexa
    Mar 21 '16 at 23:06











  • It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

    – zilexa
    Mar 29 '16 at 13:28











  • This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

    – Felix
    Sep 14 '16 at 9:28











  • Yes absolutely!

    – zilexa
    Dec 17 '16 at 22:58



















  • So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

    – zilexa
    Mar 21 '16 at 23:06











  • It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

    – zilexa
    Mar 29 '16 at 13:28











  • This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

    – Felix
    Sep 14 '16 at 9:28











  • Yes absolutely!

    – zilexa
    Dec 17 '16 at 22:58

















So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

– zilexa
Mar 21 '16 at 23:06





So I had an error in the script (at the bottom "EOF"), the script is now fixed but I still get a fatal error after the VPN connection is initiated. I updated my problem above to include the log.

– zilexa
Mar 21 '16 at 23:06













It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

– zilexa
Mar 29 '16 at 13:28





It amazes me this is not a highly popular topic. It relates directly to the use of torrent clients and torrent scrapers, which by now (2016-Q2) for most countries need to be behind VPN. To debug the issue I ran each command of the "UP" case one by one and noticed one command fails because it's syntax is incorrect. I tried a few things but this is way over my head: ip link set dev "$1" up netns vpn mtu "$2" Any help fixing this command would be greatly appreciated.

– zilexa
Mar 29 '16 at 13:28













This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

– Felix
Sep 14 '16 at 9:28





This script popcorntime-vpn.sh with some little modifications worked for me. If you are still interested, I can share it here.

– Felix
Sep 14 '16 at 9:28













Yes absolutely!

– zilexa
Dec 17 '16 at 22:58





Yes absolutely!

– zilexa
Dec 17 '16 at 22:58










3 Answers
3






active

oldest

votes


















3














Starting openvpn inside the network namespace is safer. I use the following script (fork of Schnouki's) to create namespace, configure firewall, DNS, test connectivity, start openvpn, and finally start a torrent client. I put TODOs in the script where you have to adjust it to your needs.



#!/bin/sh
# start openvpn tunnel and torrent client inside Linux network namespace
#
# this is a fork of schnouki's script, see original blog post
# https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
#
# original script can be found here
# https://gist.github.com/Schnouki/fd171bcb2d8c556e8fdf

# ------------ adjust values below ------------
# network namespace
NS_NAME=myVPN
NS_EXEC="ip netns exec $NS_NAME"
# user for starting the torrent client
REGULAR_USER=heinzwurst
# ---------------------------------------------

# exit on unbound variable
set -u

# exit on error
set -e
set -o pipefail

# trace option
#set -x

if [ $USER != "root" ]; then
echo "This must be run as root."
exit 1
fi

start_vpn() {
echo "Add network interface"

# Create the network namespace
ip netns add $NS_NAME

# Start the loopback interface in the namespace
$NS_EXEC ip addr add 127.0.0.1/8 dev lo
$NS_EXEC ip link set lo up

# Create virtual network interfaces that will let OpenVPN (in the
# namespace) access the real network, and configure the interface in the
# namespace (vpn1) to use the interface out of the namespace (vpn0) as its
# default gateway
ip link add vpn0 type veth peer name vpn1
ip link set vpn0 up
ip link set vpn1 netns $NS_NAME up

ip addr add 10.200.200.1/24 dev vpn0
$NS_EXEC ip addr add 10.200.200.2/24 dev vpn1
$NS_EXEC ip link set dev vpn1 mtu 1492
$NS_EXEC ip route add default via 10.200.200.1 dev vpn1

# Configure the nameserver to use inside the namespace
# TODO use VPN-provided DNS servers in order to prevent leaks
mkdir -p /etc/netns/$NS_NAME
cat >/etc/netns/$NS_NAME/resolv.conf <<EOF || exit 1
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF

# IPv4 NAT, you may need to adjust the interface name prefixes 'eth' 'wlan'
iptables -t nat -A POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
iptables -t nat -A POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
iptables -t mangle -A PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

# TODO create firewall rules for your specific application (torrent)
# or just comment the line below
$NS_EXEC iptables-restore < /etc/iptables/iptables-$NS_NAME.rules

# we should have full network access in the namespace
$NS_EXEC ping -c 3 www.google.com

# start OpenVPN in the namespace
echo "Starting VPN"
cd /etc/openvpn
# TODO create openvpn configuration in /etc/openvpn/$NS_NAME.conf
$NS_EXEC openvpn --config $NS_NAME.conf &

# wait for the tunnel interface to come up
while ! $NS_EXEC ip link show dev tun0 >/dev/null 2>&1 ; do sleep .5 ; done
}

stop_vpn() {
echo "Stopping VPN"
ip netns pids $NS_NAME | xargs -rd'n' kill
# TODO wait for terminate

# clear NAT
iptables -t nat -D POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
iptables -t nat -D POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
iptables -t mangle -D PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

echo "Delete network interface"
rm -rf /etc/netns/$NS_NAME

ip netns delete $NS_NAME
ip link delete vpn0
}

# stop VPN on exit (even when error occured)
trap stop_vpn EXIT

start_vpn

# TODO start your favorite torrent client
$NS_EXEC sudo -u $REGULAR_USER transmission-gtk





share|improve this answer


























  • @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

    – zilexa
    Sep 18 '16 at 20:06













  • @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

    – HalosGhost
    Sep 18 '16 at 20:27











  • answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

    – Felix
    Sep 25 '16 at 13:33













  • Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

    – Nehal J Wani
    Jul 30 '17 at 7:14











  • Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

    – enigmaticPhysicist
    Jan 19 at 1:50



















0














I have been using the forked script provided by Felix for about six months and up until the past week it has been working quite well (once I added the sysctl -w net.ipv4.ip_forward=1 line suggested by Nehal J Wani and commented out the pipefail - as it would crash with it otherwise).



Yet I noticed in the past week it has completely stopped working (Debian 9.4). I spent a lot of time trying to debug what went wrong, as the script is quite useful - but no matter what I tried I couldn't get it working again.



As this is such a useful feature, I wanted to provide an alternative fork of Schnouki's work that's works well in case anyone runs into the same issue.



https://github.com/crasm/vpnshift.sh



#!/bin/bash
#
# Copyright (c) 2016, crasm <crasm@vczf.io>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


usage="usage: vpnshift -c <config> [<command> [<arg>...]]
optional:
-u <user> Execute <command> as <user>
-d Toggle namespace debug shell

if not otherwise specified:
- The command defaults to the user's shell (${SHELL}).
- The user must be inferred from sudo.
"

quick_die() {
format="$1"; shift
>&2 printf "${format}n" "$@"
exit 1
}

die() {
format="$1"; shift
>&2 printf "${format}n" "$@"
clean_exit 1
}

hush() {
eval "$@" > /dev/null 2> /dev/null
}

must() {
eval "$@" || die "failed: %s" "$*"
}

is_running() {
local pid="$1"
hush kill -s 0 "${pid}"
}

sig_kill() {
local pid="$1"
hush kill -s KILL "${pid}"
}

sig_term() {
local pid="$1"
hush kill -s TERM "${pid}"
}

clean_exit() {
local exit_code="$1"

if is_running "${openvpn_pid}"; then
# Kill openvpn.
sig_term "${openvpn_pid}"
>&2 printf "stopping openvpn (pid = %d)." "${openvpn_pid}"
for i in {1..100}; do
if is_running "${openvpn_pid}"; then
sleep 0.1
printf "."
else
break
fi
done
printf "n"

if is_running "${openvpn_pid}"; then
>&2 echo "forced to kill openvpn"
sig_kill "${openvpn_pid}"
fi
else
>&2 echo "openvpn exited"
fi

# don't start cleaning up until openvpn is gone
hush ip netns delete "${namespace}"
hush rm --recursive --force "${namespace_dir}"
hush sysctl --quiet net.ipv4.ip_forward="${forward}"
if hush ps -C 'firewalld'; then
echo "[firewalld] clearing firewalld state"
hush systemctl restart firewalld
else
echo "${rules}" | hush iptables-restore
fi

# Sometimes there's a lag for the veths to be deleted by linux, so we
# delete it manually.
hush ip link delete "${veth_default}"
hush ip link delete "${veth_vpn}"

exit "${exit_code}"
}

nsdo() {
ip netns exec "${namespace}" "$@"
}

_debug=0

main() {
local config=
local user="${SUDO_USER}"
while getopts "hdc:u:" opt; do
case "${opt}" in
h) quick_die "${usage}" ;;
d) _debug=1 ;;
c) config="$(realpath "${OPTARG}")" ;;
u) user="${OPTARG}" ;;
*) quick_die "unknown option: %s" "${opt}" ;;
esac
done
shift $(( OPTIND - 1 ))

if [[ -z "${config}" ]]; then
quick_die "openvpn config is required"
fi

if [[ -z "${user}" ]]; then
quick_die "user must be provided explicitly via '-u' or implicitly via SUDO_USER"
fi

local cmd="$1"; shift

if [[ -z "${cmd}" ]]; then
cmd="${SHELL}"
fi

must ip netns add vpnshift
must mkdir --parents "${namespace_dir}"

# Set up loopback interface

must nsdo ip address add '127.0.0.1/8' dev lo
must nsdo ip address add '::1/128' dev lo
must nsdo ip link set lo up

# Set up veth tunnel

must ip link add "${veth_vpn}" type veth peer name "${veth_default}"
must ip link set "${veth_vpn}" netns "${namespace}"

must ip link set "${veth_default}" up
must nsdo ip link set "${veth_vpn}" up

must ip address add "10.10.10.10/31" dev "${veth_default}"
must nsdo ip
address add "10.10.10.11/31" dev "${veth_vpn}"

must nsdo ip
route add default via "10.10.10.10" dev "${veth_vpn}"

# Set up NAT and IP forwarding
must sysctl --quiet net.ipv4.ip_forward=1

# check if we need to enable masquerading via firewalld for veth_default
if hush ps -C 'firewalld'; then
echo "[firewalld] enabling firewalld based masquerading for ${veth_default}"

if [[ $(firewall-cmd --get-zones | grep "${namespace}") != *"${namespace}"* ]]
then
echo "[firewalld] creating permanent new zone ${namespace} with target default"
must firewall-cmd -q --permanent --new-zone="${namespace}"
must firewall-cmd -q --permanent --zone="${namespace}" --set-target="default"
must firewall-cmd -q --reload
fi

# add interface to our zone
echo "[firewalld] adding ${veth_default} and ${veth_vpn} to zone ${namespace}"
must firewall-cmd -q --zone="${namespace}" --change-interface="${veth_default}"

# apply our source range to our zone
echo "[firewalld] adding 10.10.10.10/31 as source for ${namespace}"
must firewall-cmd -q --zone="${namespace}" --add-source=10.10.10.10/31

# enable masquerading from our new source range on the default zone
default_zone=$(firewall-cmd --get-default-zone)
echo "[firewalld] enabling masquerading on default zone: ${default_zone}"
must firewall-cmd -q --zone="${default_zone}" --add-masquerade
must firewall-cmd -q --zone="${default_zone}" --add-rich-rule='rule family="ipv4" source address="10.10.10.10/31" masquerade'

# optionally allow ports, services, etc. on our zone

# enabling desired ports
#echo "enabling all port traffic on zone ${namespace}"
#must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/udp
#must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/tcp

# enable services
#echo "enabling dns on zone ${namespace}"
#must firewall-cmd -q --zone="${namespace}" --add-service=dns

else
must iptables --table "nat" --append "POSTROUTING" --jump "MASQUERADE" --source "10.10.10.10/31"
fi

# Set up DNS inside the new namespace
printf > "${namespace_dir}/resolv.conf"
"nameserver %snnameserver %sn"
"108.62.19.131"
"104.238.194.235"

# drop in a shell to debug namespace connectivity ... the exit trap will catch exit from this and clean up
if [[ "$_debug" == 1 ]]; then
nsdo "${SHELL}"
fi

# Launch openvpn
local tun="tunvpn"
nsdo openvpn
--cd "$(dirname "${config}")"
--config "${config}"
--dev "${tun}"
--errors-to-stderr &

openvpn_pid=$(ps --ppid "$!"
--format "pid"
--no-headers
)

>&2 printf "waiting for openvpn (pid = %d)n" "${openvpn_pid}"

while ! hush nsdo ip link show "${tun}"; do
if ! is_running "${openvpn_pid}"; then
clean_exit 1
fi
sleep 0.2
done

# Removing the default route protects from exposure if openvpn exits
# prematurely.
must nsdo ip
route delete default via "10.10.10.10" dev "${veth_vpn}"

nsdo sudo -u "${user}" "${cmd}" "$@"
}

if [[ $# == 0 ]]; then
quick_die "${usage}"
elif [[ "$(id -u)" != 0 ]]; then
sudo "$0" "$@"
exit "$?"
fi

# Stuff needed by clean_exit() to restore previous state.
namespace="vpnshift"
namespace_dir="/etc/netns/${namespace}"
forward="$(sysctl --values "net.ipv4.ip_forward")"
rules="$(iptables-save -t nat)"
veth_default="veth_default"
veth_vpn="veth_vpn"

openvpn_pid= # This is set later.

# Enable cleanup routine.
trap 'clean_exit 1' INT TERM
trap 'clean_exit $?' EXIT

main "$@"





share|improve this answer































    0














    I have a computer connected to a lan with wired ethernet, which allowed me to take a bit of a different tack with this: bridging! No iptables modifications or manual IP address specifications are required. Here's my whole solution in commands.



    lan='eth0'  # Replace with your lan interface.
    nsname='vpn'

    # Make the netns.
    ip netns add "$nsname"

    # Make the inter-namespace pipe.
    ip link add veth.host type veth peer veth.vpn
    ip link set veth.host up
    ip link set veth.vpn netns "$nsname" up

    # Bridge the host end of the pipe with the wired ethernet.
    ip link add br0 type bridge
    ip link set veth.host master br0
    ip link set "$lan" master br0 up
    ip link set br0 up

    # Start dhcpcd on the bridge for the host to use and on veth.vpn
    # for the vpn netns to use. The router will grant separate IP
    # addresses to both! (They have different MAC addresses.)
    dhcpcd br0
    ip netns exec "$nsname" dhcpcd veth.vpn

    # OK, now start the VPN.
    ip netns exec "$nsname" openvpn --config etc etc etc




    share























      Your Answer








      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "106"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f270883%2ftrying-to-run-openvpn-in-network-namespace%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      3














      Starting openvpn inside the network namespace is safer. I use the following script (fork of Schnouki's) to create namespace, configure firewall, DNS, test connectivity, start openvpn, and finally start a torrent client. I put TODOs in the script where you have to adjust it to your needs.



      #!/bin/sh
      # start openvpn tunnel and torrent client inside Linux network namespace
      #
      # this is a fork of schnouki's script, see original blog post
      # https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
      #
      # original script can be found here
      # https://gist.github.com/Schnouki/fd171bcb2d8c556e8fdf

      # ------------ adjust values below ------------
      # network namespace
      NS_NAME=myVPN
      NS_EXEC="ip netns exec $NS_NAME"
      # user for starting the torrent client
      REGULAR_USER=heinzwurst
      # ---------------------------------------------

      # exit on unbound variable
      set -u

      # exit on error
      set -e
      set -o pipefail

      # trace option
      #set -x

      if [ $USER != "root" ]; then
      echo "This must be run as root."
      exit 1
      fi

      start_vpn() {
      echo "Add network interface"

      # Create the network namespace
      ip netns add $NS_NAME

      # Start the loopback interface in the namespace
      $NS_EXEC ip addr add 127.0.0.1/8 dev lo
      $NS_EXEC ip link set lo up

      # Create virtual network interfaces that will let OpenVPN (in the
      # namespace) access the real network, and configure the interface in the
      # namespace (vpn1) to use the interface out of the namespace (vpn0) as its
      # default gateway
      ip link add vpn0 type veth peer name vpn1
      ip link set vpn0 up
      ip link set vpn1 netns $NS_NAME up

      ip addr add 10.200.200.1/24 dev vpn0
      $NS_EXEC ip addr add 10.200.200.2/24 dev vpn1
      $NS_EXEC ip link set dev vpn1 mtu 1492
      $NS_EXEC ip route add default via 10.200.200.1 dev vpn1

      # Configure the nameserver to use inside the namespace
      # TODO use VPN-provided DNS servers in order to prevent leaks
      mkdir -p /etc/netns/$NS_NAME
      cat >/etc/netns/$NS_NAME/resolv.conf <<EOF || exit 1
      nameserver 8.8.8.8
      nameserver 8.8.4.4
      EOF

      # IPv4 NAT, you may need to adjust the interface name prefixes 'eth' 'wlan'
      iptables -t nat -A POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -A POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -A PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      # TODO create firewall rules for your specific application (torrent)
      # or just comment the line below
      $NS_EXEC iptables-restore < /etc/iptables/iptables-$NS_NAME.rules

      # we should have full network access in the namespace
      $NS_EXEC ping -c 3 www.google.com

      # start OpenVPN in the namespace
      echo "Starting VPN"
      cd /etc/openvpn
      # TODO create openvpn configuration in /etc/openvpn/$NS_NAME.conf
      $NS_EXEC openvpn --config $NS_NAME.conf &

      # wait for the tunnel interface to come up
      while ! $NS_EXEC ip link show dev tun0 >/dev/null 2>&1 ; do sleep .5 ; done
      }

      stop_vpn() {
      echo "Stopping VPN"
      ip netns pids $NS_NAME | xargs -rd'n' kill
      # TODO wait for terminate

      # clear NAT
      iptables -t nat -D POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -D POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -D PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      echo "Delete network interface"
      rm -rf /etc/netns/$NS_NAME

      ip netns delete $NS_NAME
      ip link delete vpn0
      }

      # stop VPN on exit (even when error occured)
      trap stop_vpn EXIT

      start_vpn

      # TODO start your favorite torrent client
      $NS_EXEC sudo -u $REGULAR_USER transmission-gtk





      share|improve this answer


























      • @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

        – zilexa
        Sep 18 '16 at 20:06













      • @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

        – HalosGhost
        Sep 18 '16 at 20:27











      • answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

        – Felix
        Sep 25 '16 at 13:33













      • Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

        – Nehal J Wani
        Jul 30 '17 at 7:14











      • Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

        – enigmaticPhysicist
        Jan 19 at 1:50
















      3














      Starting openvpn inside the network namespace is safer. I use the following script (fork of Schnouki's) to create namespace, configure firewall, DNS, test connectivity, start openvpn, and finally start a torrent client. I put TODOs in the script where you have to adjust it to your needs.



      #!/bin/sh
      # start openvpn tunnel and torrent client inside Linux network namespace
      #
      # this is a fork of schnouki's script, see original blog post
      # https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
      #
      # original script can be found here
      # https://gist.github.com/Schnouki/fd171bcb2d8c556e8fdf

      # ------------ adjust values below ------------
      # network namespace
      NS_NAME=myVPN
      NS_EXEC="ip netns exec $NS_NAME"
      # user for starting the torrent client
      REGULAR_USER=heinzwurst
      # ---------------------------------------------

      # exit on unbound variable
      set -u

      # exit on error
      set -e
      set -o pipefail

      # trace option
      #set -x

      if [ $USER != "root" ]; then
      echo "This must be run as root."
      exit 1
      fi

      start_vpn() {
      echo "Add network interface"

      # Create the network namespace
      ip netns add $NS_NAME

      # Start the loopback interface in the namespace
      $NS_EXEC ip addr add 127.0.0.1/8 dev lo
      $NS_EXEC ip link set lo up

      # Create virtual network interfaces that will let OpenVPN (in the
      # namespace) access the real network, and configure the interface in the
      # namespace (vpn1) to use the interface out of the namespace (vpn0) as its
      # default gateway
      ip link add vpn0 type veth peer name vpn1
      ip link set vpn0 up
      ip link set vpn1 netns $NS_NAME up

      ip addr add 10.200.200.1/24 dev vpn0
      $NS_EXEC ip addr add 10.200.200.2/24 dev vpn1
      $NS_EXEC ip link set dev vpn1 mtu 1492
      $NS_EXEC ip route add default via 10.200.200.1 dev vpn1

      # Configure the nameserver to use inside the namespace
      # TODO use VPN-provided DNS servers in order to prevent leaks
      mkdir -p /etc/netns/$NS_NAME
      cat >/etc/netns/$NS_NAME/resolv.conf <<EOF || exit 1
      nameserver 8.8.8.8
      nameserver 8.8.4.4
      EOF

      # IPv4 NAT, you may need to adjust the interface name prefixes 'eth' 'wlan'
      iptables -t nat -A POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -A POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -A PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      # TODO create firewall rules for your specific application (torrent)
      # or just comment the line below
      $NS_EXEC iptables-restore < /etc/iptables/iptables-$NS_NAME.rules

      # we should have full network access in the namespace
      $NS_EXEC ping -c 3 www.google.com

      # start OpenVPN in the namespace
      echo "Starting VPN"
      cd /etc/openvpn
      # TODO create openvpn configuration in /etc/openvpn/$NS_NAME.conf
      $NS_EXEC openvpn --config $NS_NAME.conf &

      # wait for the tunnel interface to come up
      while ! $NS_EXEC ip link show dev tun0 >/dev/null 2>&1 ; do sleep .5 ; done
      }

      stop_vpn() {
      echo "Stopping VPN"
      ip netns pids $NS_NAME | xargs -rd'n' kill
      # TODO wait for terminate

      # clear NAT
      iptables -t nat -D POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -D POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -D PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      echo "Delete network interface"
      rm -rf /etc/netns/$NS_NAME

      ip netns delete $NS_NAME
      ip link delete vpn0
      }

      # stop VPN on exit (even when error occured)
      trap stop_vpn EXIT

      start_vpn

      # TODO start your favorite torrent client
      $NS_EXEC sudo -u $REGULAR_USER transmission-gtk





      share|improve this answer


























      • @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

        – zilexa
        Sep 18 '16 at 20:06













      • @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

        – HalosGhost
        Sep 18 '16 at 20:27











      • answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

        – Felix
        Sep 25 '16 at 13:33













      • Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

        – Nehal J Wani
        Jul 30 '17 at 7:14











      • Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

        – enigmaticPhysicist
        Jan 19 at 1:50














      3












      3








      3







      Starting openvpn inside the network namespace is safer. I use the following script (fork of Schnouki's) to create namespace, configure firewall, DNS, test connectivity, start openvpn, and finally start a torrent client. I put TODOs in the script where you have to adjust it to your needs.



      #!/bin/sh
      # start openvpn tunnel and torrent client inside Linux network namespace
      #
      # this is a fork of schnouki's script, see original blog post
      # https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
      #
      # original script can be found here
      # https://gist.github.com/Schnouki/fd171bcb2d8c556e8fdf

      # ------------ adjust values below ------------
      # network namespace
      NS_NAME=myVPN
      NS_EXEC="ip netns exec $NS_NAME"
      # user for starting the torrent client
      REGULAR_USER=heinzwurst
      # ---------------------------------------------

      # exit on unbound variable
      set -u

      # exit on error
      set -e
      set -o pipefail

      # trace option
      #set -x

      if [ $USER != "root" ]; then
      echo "This must be run as root."
      exit 1
      fi

      start_vpn() {
      echo "Add network interface"

      # Create the network namespace
      ip netns add $NS_NAME

      # Start the loopback interface in the namespace
      $NS_EXEC ip addr add 127.0.0.1/8 dev lo
      $NS_EXEC ip link set lo up

      # Create virtual network interfaces that will let OpenVPN (in the
      # namespace) access the real network, and configure the interface in the
      # namespace (vpn1) to use the interface out of the namespace (vpn0) as its
      # default gateway
      ip link add vpn0 type veth peer name vpn1
      ip link set vpn0 up
      ip link set vpn1 netns $NS_NAME up

      ip addr add 10.200.200.1/24 dev vpn0
      $NS_EXEC ip addr add 10.200.200.2/24 dev vpn1
      $NS_EXEC ip link set dev vpn1 mtu 1492
      $NS_EXEC ip route add default via 10.200.200.1 dev vpn1

      # Configure the nameserver to use inside the namespace
      # TODO use VPN-provided DNS servers in order to prevent leaks
      mkdir -p /etc/netns/$NS_NAME
      cat >/etc/netns/$NS_NAME/resolv.conf <<EOF || exit 1
      nameserver 8.8.8.8
      nameserver 8.8.4.4
      EOF

      # IPv4 NAT, you may need to adjust the interface name prefixes 'eth' 'wlan'
      iptables -t nat -A POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -A POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -A PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      # TODO create firewall rules for your specific application (torrent)
      # or just comment the line below
      $NS_EXEC iptables-restore < /etc/iptables/iptables-$NS_NAME.rules

      # we should have full network access in the namespace
      $NS_EXEC ping -c 3 www.google.com

      # start OpenVPN in the namespace
      echo "Starting VPN"
      cd /etc/openvpn
      # TODO create openvpn configuration in /etc/openvpn/$NS_NAME.conf
      $NS_EXEC openvpn --config $NS_NAME.conf &

      # wait for the tunnel interface to come up
      while ! $NS_EXEC ip link show dev tun0 >/dev/null 2>&1 ; do sleep .5 ; done
      }

      stop_vpn() {
      echo "Stopping VPN"
      ip netns pids $NS_NAME | xargs -rd'n' kill
      # TODO wait for terminate

      # clear NAT
      iptables -t nat -D POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -D POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -D PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      echo "Delete network interface"
      rm -rf /etc/netns/$NS_NAME

      ip netns delete $NS_NAME
      ip link delete vpn0
      }

      # stop VPN on exit (even when error occured)
      trap stop_vpn EXIT

      start_vpn

      # TODO start your favorite torrent client
      $NS_EXEC sudo -u $REGULAR_USER transmission-gtk





      share|improve this answer















      Starting openvpn inside the network namespace is safer. I use the following script (fork of Schnouki's) to create namespace, configure firewall, DNS, test connectivity, start openvpn, and finally start a torrent client. I put TODOs in the script where you have to adjust it to your needs.



      #!/bin/sh
      # start openvpn tunnel and torrent client inside Linux network namespace
      #
      # this is a fork of schnouki's script, see original blog post
      # https://schnouki.net/posts/2014/12/12/openvpn-for-a-single-application-on-linux/
      #
      # original script can be found here
      # https://gist.github.com/Schnouki/fd171bcb2d8c556e8fdf

      # ------------ adjust values below ------------
      # network namespace
      NS_NAME=myVPN
      NS_EXEC="ip netns exec $NS_NAME"
      # user for starting the torrent client
      REGULAR_USER=heinzwurst
      # ---------------------------------------------

      # exit on unbound variable
      set -u

      # exit on error
      set -e
      set -o pipefail

      # trace option
      #set -x

      if [ $USER != "root" ]; then
      echo "This must be run as root."
      exit 1
      fi

      start_vpn() {
      echo "Add network interface"

      # Create the network namespace
      ip netns add $NS_NAME

      # Start the loopback interface in the namespace
      $NS_EXEC ip addr add 127.0.0.1/8 dev lo
      $NS_EXEC ip link set lo up

      # Create virtual network interfaces that will let OpenVPN (in the
      # namespace) access the real network, and configure the interface in the
      # namespace (vpn1) to use the interface out of the namespace (vpn0) as its
      # default gateway
      ip link add vpn0 type veth peer name vpn1
      ip link set vpn0 up
      ip link set vpn1 netns $NS_NAME up

      ip addr add 10.200.200.1/24 dev vpn0
      $NS_EXEC ip addr add 10.200.200.2/24 dev vpn1
      $NS_EXEC ip link set dev vpn1 mtu 1492
      $NS_EXEC ip route add default via 10.200.200.1 dev vpn1

      # Configure the nameserver to use inside the namespace
      # TODO use VPN-provided DNS servers in order to prevent leaks
      mkdir -p /etc/netns/$NS_NAME
      cat >/etc/netns/$NS_NAME/resolv.conf <<EOF || exit 1
      nameserver 8.8.8.8
      nameserver 8.8.4.4
      EOF

      # IPv4 NAT, you may need to adjust the interface name prefixes 'eth' 'wlan'
      iptables -t nat -A POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -A POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -A PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      # TODO create firewall rules for your specific application (torrent)
      # or just comment the line below
      $NS_EXEC iptables-restore < /etc/iptables/iptables-$NS_NAME.rules

      # we should have full network access in the namespace
      $NS_EXEC ping -c 3 www.google.com

      # start OpenVPN in the namespace
      echo "Starting VPN"
      cd /etc/openvpn
      # TODO create openvpn configuration in /etc/openvpn/$NS_NAME.conf
      $NS_EXEC openvpn --config $NS_NAME.conf &

      # wait for the tunnel interface to come up
      while ! $NS_EXEC ip link show dev tun0 >/dev/null 2>&1 ; do sleep .5 ; done
      }

      stop_vpn() {
      echo "Stopping VPN"
      ip netns pids $NS_NAME | xargs -rd'n' kill
      # TODO wait for terminate

      # clear NAT
      iptables -t nat -D POSTROUTING -o eth+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t nat -D POSTROUTING -o wlan+ -m mark --mark 0x29a -j MASQUERADE
      iptables -t mangle -D PREROUTING -i vpn0 -j MARK --set-xmark 0x29a/0xffffffff

      echo "Delete network interface"
      rm -rf /etc/netns/$NS_NAME

      ip netns delete $NS_NAME
      ip link delete vpn0
      }

      # stop VPN on exit (even when error occured)
      trap stop_vpn EXIT

      start_vpn

      # TODO start your favorite torrent client
      $NS_EXEC sudo -u $REGULAR_USER transmission-gtk






      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Sep 14 '16 at 16:42









      HalosGhost

      3,72592236




      3,72592236










      answered Sep 14 '16 at 16:23









      FelixFelix

      315




      315













      • @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

        – zilexa
        Sep 18 '16 at 20:06













      • @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

        – HalosGhost
        Sep 18 '16 at 20:27











      • answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

        – Felix
        Sep 25 '16 at 13:33













      • Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

        – Nehal J Wani
        Jul 30 '17 at 7:14











      • Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

        – enigmaticPhysicist
        Jan 19 at 1:50



















      • @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

        – zilexa
        Sep 18 '16 at 20:06













      • @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

        – HalosGhost
        Sep 18 '16 at 20:27











      • answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

        – Felix
        Sep 25 '16 at 13:33













      • Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

        – Nehal J Wani
        Jul 30 '17 at 7:14











      • Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

        – enigmaticPhysicist
        Jan 19 at 1:50

















      @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

      – zilexa
      Sep 18 '16 at 20:06







      @HalosGhost OMG this is exactly what I have been looking for! But I am a noob compared to you. Could you please explain a little bit more for example share your firewall rules for your torrent client? I am using Transmission, with transmission-daemon. Also, should I add the pathfilename of my .ovpn file at the end of this line or should $NS_NAME.conf resolve to the actual name of my (NordVPN) conf file? "$NS_EXEC openvpn --config $NS_NAME.conf &"

      – zilexa
      Sep 18 '16 at 20:06















      @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

      – HalosGhost
      Sep 18 '16 at 20:27





      @zilexa, I edited the post only for clarity; you should address clarifying questions to the actual author.

      – HalosGhost
      Sep 18 '16 at 20:27













      answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

      – Felix
      Sep 25 '16 at 13:33







      answers to your questions: (1) use something like this simple stateful firewall configuration and open a single port for your torrent client (2) NS_NAME is without '.ovpn' suffix

      – Felix
      Sep 25 '16 at 13:33















      Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

      – Nehal J Wani
      Jul 30 '17 at 7:14





      Don't forget to sysctl -w net.ipv4.ip_forward=1 on the host machine!

      – Nehal J Wani
      Jul 30 '17 at 7:14













      Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

      – enigmaticPhysicist
      Jan 19 at 1:50





      Looks like you have got two completely different interfaces both named tun0 and you get away with it because they're in different namespaces. That's a bad idea. You should call the veth interfaces vethhost and vethvpn.

      – enigmaticPhysicist
      Jan 19 at 1:50













      0














      I have been using the forked script provided by Felix for about six months and up until the past week it has been working quite well (once I added the sysctl -w net.ipv4.ip_forward=1 line suggested by Nehal J Wani and commented out the pipefail - as it would crash with it otherwise).



      Yet I noticed in the past week it has completely stopped working (Debian 9.4). I spent a lot of time trying to debug what went wrong, as the script is quite useful - but no matter what I tried I couldn't get it working again.



      As this is such a useful feature, I wanted to provide an alternative fork of Schnouki's work that's works well in case anyone runs into the same issue.



      https://github.com/crasm/vpnshift.sh



      #!/bin/bash
      #
      # Copyright (c) 2016, crasm <crasm@vczf.io>
      #
      # Permission to use, copy, modify, and/or distribute this software for any
      # purpose with or without fee is hereby granted, provided that the above
      # copyright notice and this permission notice appear in all copies.
      #
      # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


      usage="usage: vpnshift -c <config> [<command> [<arg>...]]
      optional:
      -u <user> Execute <command> as <user>
      -d Toggle namespace debug shell

      if not otherwise specified:
      - The command defaults to the user's shell (${SHELL}).
      - The user must be inferred from sudo.
      "

      quick_die() {
      format="$1"; shift
      >&2 printf "${format}n" "$@"
      exit 1
      }

      die() {
      format="$1"; shift
      >&2 printf "${format}n" "$@"
      clean_exit 1
      }

      hush() {
      eval "$@" > /dev/null 2> /dev/null
      }

      must() {
      eval "$@" || die "failed: %s" "$*"
      }

      is_running() {
      local pid="$1"
      hush kill -s 0 "${pid}"
      }

      sig_kill() {
      local pid="$1"
      hush kill -s KILL "${pid}"
      }

      sig_term() {
      local pid="$1"
      hush kill -s TERM "${pid}"
      }

      clean_exit() {
      local exit_code="$1"

      if is_running "${openvpn_pid}"; then
      # Kill openvpn.
      sig_term "${openvpn_pid}"
      >&2 printf "stopping openvpn (pid = %d)." "${openvpn_pid}"
      for i in {1..100}; do
      if is_running "${openvpn_pid}"; then
      sleep 0.1
      printf "."
      else
      break
      fi
      done
      printf "n"

      if is_running "${openvpn_pid}"; then
      >&2 echo "forced to kill openvpn"
      sig_kill "${openvpn_pid}"
      fi
      else
      >&2 echo "openvpn exited"
      fi

      # don't start cleaning up until openvpn is gone
      hush ip netns delete "${namespace}"
      hush rm --recursive --force "${namespace_dir}"
      hush sysctl --quiet net.ipv4.ip_forward="${forward}"
      if hush ps -C 'firewalld'; then
      echo "[firewalld] clearing firewalld state"
      hush systemctl restart firewalld
      else
      echo "${rules}" | hush iptables-restore
      fi

      # Sometimes there's a lag for the veths to be deleted by linux, so we
      # delete it manually.
      hush ip link delete "${veth_default}"
      hush ip link delete "${veth_vpn}"

      exit "${exit_code}"
      }

      nsdo() {
      ip netns exec "${namespace}" "$@"
      }

      _debug=0

      main() {
      local config=
      local user="${SUDO_USER}"
      while getopts "hdc:u:" opt; do
      case "${opt}" in
      h) quick_die "${usage}" ;;
      d) _debug=1 ;;
      c) config="$(realpath "${OPTARG}")" ;;
      u) user="${OPTARG}" ;;
      *) quick_die "unknown option: %s" "${opt}" ;;
      esac
      done
      shift $(( OPTIND - 1 ))

      if [[ -z "${config}" ]]; then
      quick_die "openvpn config is required"
      fi

      if [[ -z "${user}" ]]; then
      quick_die "user must be provided explicitly via '-u' or implicitly via SUDO_USER"
      fi

      local cmd="$1"; shift

      if [[ -z "${cmd}" ]]; then
      cmd="${SHELL}"
      fi

      must ip netns add vpnshift
      must mkdir --parents "${namespace_dir}"

      # Set up loopback interface

      must nsdo ip address add '127.0.0.1/8' dev lo
      must nsdo ip address add '::1/128' dev lo
      must nsdo ip link set lo up

      # Set up veth tunnel

      must ip link add "${veth_vpn}" type veth peer name "${veth_default}"
      must ip link set "${veth_vpn}" netns "${namespace}"

      must ip link set "${veth_default}" up
      must nsdo ip link set "${veth_vpn}" up

      must ip address add "10.10.10.10/31" dev "${veth_default}"
      must nsdo ip
      address add "10.10.10.11/31" dev "${veth_vpn}"

      must nsdo ip
      route add default via "10.10.10.10" dev "${veth_vpn}"

      # Set up NAT and IP forwarding
      must sysctl --quiet net.ipv4.ip_forward=1

      # check if we need to enable masquerading via firewalld for veth_default
      if hush ps -C 'firewalld'; then
      echo "[firewalld] enabling firewalld based masquerading for ${veth_default}"

      if [[ $(firewall-cmd --get-zones | grep "${namespace}") != *"${namespace}"* ]]
      then
      echo "[firewalld] creating permanent new zone ${namespace} with target default"
      must firewall-cmd -q --permanent --new-zone="${namespace}"
      must firewall-cmd -q --permanent --zone="${namespace}" --set-target="default"
      must firewall-cmd -q --reload
      fi

      # add interface to our zone
      echo "[firewalld] adding ${veth_default} and ${veth_vpn} to zone ${namespace}"
      must firewall-cmd -q --zone="${namespace}" --change-interface="${veth_default}"

      # apply our source range to our zone
      echo "[firewalld] adding 10.10.10.10/31 as source for ${namespace}"
      must firewall-cmd -q --zone="${namespace}" --add-source=10.10.10.10/31

      # enable masquerading from our new source range on the default zone
      default_zone=$(firewall-cmd --get-default-zone)
      echo "[firewalld] enabling masquerading on default zone: ${default_zone}"
      must firewall-cmd -q --zone="${default_zone}" --add-masquerade
      must firewall-cmd -q --zone="${default_zone}" --add-rich-rule='rule family="ipv4" source address="10.10.10.10/31" masquerade'

      # optionally allow ports, services, etc. on our zone

      # enabling desired ports
      #echo "enabling all port traffic on zone ${namespace}"
      #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/udp
      #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/tcp

      # enable services
      #echo "enabling dns on zone ${namespace}"
      #must firewall-cmd -q --zone="${namespace}" --add-service=dns

      else
      must iptables --table "nat" --append "POSTROUTING" --jump "MASQUERADE" --source "10.10.10.10/31"
      fi

      # Set up DNS inside the new namespace
      printf > "${namespace_dir}/resolv.conf"
      "nameserver %snnameserver %sn"
      "108.62.19.131"
      "104.238.194.235"

      # drop in a shell to debug namespace connectivity ... the exit trap will catch exit from this and clean up
      if [[ "$_debug" == 1 ]]; then
      nsdo "${SHELL}"
      fi

      # Launch openvpn
      local tun="tunvpn"
      nsdo openvpn
      --cd "$(dirname "${config}")"
      --config "${config}"
      --dev "${tun}"
      --errors-to-stderr &

      openvpn_pid=$(ps --ppid "$!"
      --format "pid"
      --no-headers
      )

      >&2 printf "waiting for openvpn (pid = %d)n" "${openvpn_pid}"

      while ! hush nsdo ip link show "${tun}"; do
      if ! is_running "${openvpn_pid}"; then
      clean_exit 1
      fi
      sleep 0.2
      done

      # Removing the default route protects from exposure if openvpn exits
      # prematurely.
      must nsdo ip
      route delete default via "10.10.10.10" dev "${veth_vpn}"

      nsdo sudo -u "${user}" "${cmd}" "$@"
      }

      if [[ $# == 0 ]]; then
      quick_die "${usage}"
      elif [[ "$(id -u)" != 0 ]]; then
      sudo "$0" "$@"
      exit "$?"
      fi

      # Stuff needed by clean_exit() to restore previous state.
      namespace="vpnshift"
      namespace_dir="/etc/netns/${namespace}"
      forward="$(sysctl --values "net.ipv4.ip_forward")"
      rules="$(iptables-save -t nat)"
      veth_default="veth_default"
      veth_vpn="veth_vpn"

      openvpn_pid= # This is set later.

      # Enable cleanup routine.
      trap 'clean_exit 1' INT TERM
      trap 'clean_exit $?' EXIT

      main "$@"





      share|improve this answer




























        0














        I have been using the forked script provided by Felix for about six months and up until the past week it has been working quite well (once I added the sysctl -w net.ipv4.ip_forward=1 line suggested by Nehal J Wani and commented out the pipefail - as it would crash with it otherwise).



        Yet I noticed in the past week it has completely stopped working (Debian 9.4). I spent a lot of time trying to debug what went wrong, as the script is quite useful - but no matter what I tried I couldn't get it working again.



        As this is such a useful feature, I wanted to provide an alternative fork of Schnouki's work that's works well in case anyone runs into the same issue.



        https://github.com/crasm/vpnshift.sh



        #!/bin/bash
        #
        # Copyright (c) 2016, crasm <crasm@vczf.io>
        #
        # Permission to use, copy, modify, and/or distribute this software for any
        # purpose with or without fee is hereby granted, provided that the above
        # copyright notice and this permission notice appear in all copies.
        #
        # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


        usage="usage: vpnshift -c <config> [<command> [<arg>...]]
        optional:
        -u <user> Execute <command> as <user>
        -d Toggle namespace debug shell

        if not otherwise specified:
        - The command defaults to the user's shell (${SHELL}).
        - The user must be inferred from sudo.
        "

        quick_die() {
        format="$1"; shift
        >&2 printf "${format}n" "$@"
        exit 1
        }

        die() {
        format="$1"; shift
        >&2 printf "${format}n" "$@"
        clean_exit 1
        }

        hush() {
        eval "$@" > /dev/null 2> /dev/null
        }

        must() {
        eval "$@" || die "failed: %s" "$*"
        }

        is_running() {
        local pid="$1"
        hush kill -s 0 "${pid}"
        }

        sig_kill() {
        local pid="$1"
        hush kill -s KILL "${pid}"
        }

        sig_term() {
        local pid="$1"
        hush kill -s TERM "${pid}"
        }

        clean_exit() {
        local exit_code="$1"

        if is_running "${openvpn_pid}"; then
        # Kill openvpn.
        sig_term "${openvpn_pid}"
        >&2 printf "stopping openvpn (pid = %d)." "${openvpn_pid}"
        for i in {1..100}; do
        if is_running "${openvpn_pid}"; then
        sleep 0.1
        printf "."
        else
        break
        fi
        done
        printf "n"

        if is_running "${openvpn_pid}"; then
        >&2 echo "forced to kill openvpn"
        sig_kill "${openvpn_pid}"
        fi
        else
        >&2 echo "openvpn exited"
        fi

        # don't start cleaning up until openvpn is gone
        hush ip netns delete "${namespace}"
        hush rm --recursive --force "${namespace_dir}"
        hush sysctl --quiet net.ipv4.ip_forward="${forward}"
        if hush ps -C 'firewalld'; then
        echo "[firewalld] clearing firewalld state"
        hush systemctl restart firewalld
        else
        echo "${rules}" | hush iptables-restore
        fi

        # Sometimes there's a lag for the veths to be deleted by linux, so we
        # delete it manually.
        hush ip link delete "${veth_default}"
        hush ip link delete "${veth_vpn}"

        exit "${exit_code}"
        }

        nsdo() {
        ip netns exec "${namespace}" "$@"
        }

        _debug=0

        main() {
        local config=
        local user="${SUDO_USER}"
        while getopts "hdc:u:" opt; do
        case "${opt}" in
        h) quick_die "${usage}" ;;
        d) _debug=1 ;;
        c) config="$(realpath "${OPTARG}")" ;;
        u) user="${OPTARG}" ;;
        *) quick_die "unknown option: %s" "${opt}" ;;
        esac
        done
        shift $(( OPTIND - 1 ))

        if [[ -z "${config}" ]]; then
        quick_die "openvpn config is required"
        fi

        if [[ -z "${user}" ]]; then
        quick_die "user must be provided explicitly via '-u' or implicitly via SUDO_USER"
        fi

        local cmd="$1"; shift

        if [[ -z "${cmd}" ]]; then
        cmd="${SHELL}"
        fi

        must ip netns add vpnshift
        must mkdir --parents "${namespace_dir}"

        # Set up loopback interface

        must nsdo ip address add '127.0.0.1/8' dev lo
        must nsdo ip address add '::1/128' dev lo
        must nsdo ip link set lo up

        # Set up veth tunnel

        must ip link add "${veth_vpn}" type veth peer name "${veth_default}"
        must ip link set "${veth_vpn}" netns "${namespace}"

        must ip link set "${veth_default}" up
        must nsdo ip link set "${veth_vpn}" up

        must ip address add "10.10.10.10/31" dev "${veth_default}"
        must nsdo ip
        address add "10.10.10.11/31" dev "${veth_vpn}"

        must nsdo ip
        route add default via "10.10.10.10" dev "${veth_vpn}"

        # Set up NAT and IP forwarding
        must sysctl --quiet net.ipv4.ip_forward=1

        # check if we need to enable masquerading via firewalld for veth_default
        if hush ps -C 'firewalld'; then
        echo "[firewalld] enabling firewalld based masquerading for ${veth_default}"

        if [[ $(firewall-cmd --get-zones | grep "${namespace}") != *"${namespace}"* ]]
        then
        echo "[firewalld] creating permanent new zone ${namespace} with target default"
        must firewall-cmd -q --permanent --new-zone="${namespace}"
        must firewall-cmd -q --permanent --zone="${namespace}" --set-target="default"
        must firewall-cmd -q --reload
        fi

        # add interface to our zone
        echo "[firewalld] adding ${veth_default} and ${veth_vpn} to zone ${namespace}"
        must firewall-cmd -q --zone="${namespace}" --change-interface="${veth_default}"

        # apply our source range to our zone
        echo "[firewalld] adding 10.10.10.10/31 as source for ${namespace}"
        must firewall-cmd -q --zone="${namespace}" --add-source=10.10.10.10/31

        # enable masquerading from our new source range on the default zone
        default_zone=$(firewall-cmd --get-default-zone)
        echo "[firewalld] enabling masquerading on default zone: ${default_zone}"
        must firewall-cmd -q --zone="${default_zone}" --add-masquerade
        must firewall-cmd -q --zone="${default_zone}" --add-rich-rule='rule family="ipv4" source address="10.10.10.10/31" masquerade'

        # optionally allow ports, services, etc. on our zone

        # enabling desired ports
        #echo "enabling all port traffic on zone ${namespace}"
        #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/udp
        #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/tcp

        # enable services
        #echo "enabling dns on zone ${namespace}"
        #must firewall-cmd -q --zone="${namespace}" --add-service=dns

        else
        must iptables --table "nat" --append "POSTROUTING" --jump "MASQUERADE" --source "10.10.10.10/31"
        fi

        # Set up DNS inside the new namespace
        printf > "${namespace_dir}/resolv.conf"
        "nameserver %snnameserver %sn"
        "108.62.19.131"
        "104.238.194.235"

        # drop in a shell to debug namespace connectivity ... the exit trap will catch exit from this and clean up
        if [[ "$_debug" == 1 ]]; then
        nsdo "${SHELL}"
        fi

        # Launch openvpn
        local tun="tunvpn"
        nsdo openvpn
        --cd "$(dirname "${config}")"
        --config "${config}"
        --dev "${tun}"
        --errors-to-stderr &

        openvpn_pid=$(ps --ppid "$!"
        --format "pid"
        --no-headers
        )

        >&2 printf "waiting for openvpn (pid = %d)n" "${openvpn_pid}"

        while ! hush nsdo ip link show "${tun}"; do
        if ! is_running "${openvpn_pid}"; then
        clean_exit 1
        fi
        sleep 0.2
        done

        # Removing the default route protects from exposure if openvpn exits
        # prematurely.
        must nsdo ip
        route delete default via "10.10.10.10" dev "${veth_vpn}"

        nsdo sudo -u "${user}" "${cmd}" "$@"
        }

        if [[ $# == 0 ]]; then
        quick_die "${usage}"
        elif [[ "$(id -u)" != 0 ]]; then
        sudo "$0" "$@"
        exit "$?"
        fi

        # Stuff needed by clean_exit() to restore previous state.
        namespace="vpnshift"
        namespace_dir="/etc/netns/${namespace}"
        forward="$(sysctl --values "net.ipv4.ip_forward")"
        rules="$(iptables-save -t nat)"
        veth_default="veth_default"
        veth_vpn="veth_vpn"

        openvpn_pid= # This is set later.

        # Enable cleanup routine.
        trap 'clean_exit 1' INT TERM
        trap 'clean_exit $?' EXIT

        main "$@"





        share|improve this answer


























          0












          0








          0







          I have been using the forked script provided by Felix for about six months and up until the past week it has been working quite well (once I added the sysctl -w net.ipv4.ip_forward=1 line suggested by Nehal J Wani and commented out the pipefail - as it would crash with it otherwise).



          Yet I noticed in the past week it has completely stopped working (Debian 9.4). I spent a lot of time trying to debug what went wrong, as the script is quite useful - but no matter what I tried I couldn't get it working again.



          As this is such a useful feature, I wanted to provide an alternative fork of Schnouki's work that's works well in case anyone runs into the same issue.



          https://github.com/crasm/vpnshift.sh



          #!/bin/bash
          #
          # Copyright (c) 2016, crasm <crasm@vczf.io>
          #
          # Permission to use, copy, modify, and/or distribute this software for any
          # purpose with or without fee is hereby granted, provided that the above
          # copyright notice and this permission notice appear in all copies.
          #
          # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
          # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
          # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
          # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
          # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
          # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
          # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


          usage="usage: vpnshift -c <config> [<command> [<arg>...]]
          optional:
          -u <user> Execute <command> as <user>
          -d Toggle namespace debug shell

          if not otherwise specified:
          - The command defaults to the user's shell (${SHELL}).
          - The user must be inferred from sudo.
          "

          quick_die() {
          format="$1"; shift
          >&2 printf "${format}n" "$@"
          exit 1
          }

          die() {
          format="$1"; shift
          >&2 printf "${format}n" "$@"
          clean_exit 1
          }

          hush() {
          eval "$@" > /dev/null 2> /dev/null
          }

          must() {
          eval "$@" || die "failed: %s" "$*"
          }

          is_running() {
          local pid="$1"
          hush kill -s 0 "${pid}"
          }

          sig_kill() {
          local pid="$1"
          hush kill -s KILL "${pid}"
          }

          sig_term() {
          local pid="$1"
          hush kill -s TERM "${pid}"
          }

          clean_exit() {
          local exit_code="$1"

          if is_running "${openvpn_pid}"; then
          # Kill openvpn.
          sig_term "${openvpn_pid}"
          >&2 printf "stopping openvpn (pid = %d)." "${openvpn_pid}"
          for i in {1..100}; do
          if is_running "${openvpn_pid}"; then
          sleep 0.1
          printf "."
          else
          break
          fi
          done
          printf "n"

          if is_running "${openvpn_pid}"; then
          >&2 echo "forced to kill openvpn"
          sig_kill "${openvpn_pid}"
          fi
          else
          >&2 echo "openvpn exited"
          fi

          # don't start cleaning up until openvpn is gone
          hush ip netns delete "${namespace}"
          hush rm --recursive --force "${namespace_dir}"
          hush sysctl --quiet net.ipv4.ip_forward="${forward}"
          if hush ps -C 'firewalld'; then
          echo "[firewalld] clearing firewalld state"
          hush systemctl restart firewalld
          else
          echo "${rules}" | hush iptables-restore
          fi

          # Sometimes there's a lag for the veths to be deleted by linux, so we
          # delete it manually.
          hush ip link delete "${veth_default}"
          hush ip link delete "${veth_vpn}"

          exit "${exit_code}"
          }

          nsdo() {
          ip netns exec "${namespace}" "$@"
          }

          _debug=0

          main() {
          local config=
          local user="${SUDO_USER}"
          while getopts "hdc:u:" opt; do
          case "${opt}" in
          h) quick_die "${usage}" ;;
          d) _debug=1 ;;
          c) config="$(realpath "${OPTARG}")" ;;
          u) user="${OPTARG}" ;;
          *) quick_die "unknown option: %s" "${opt}" ;;
          esac
          done
          shift $(( OPTIND - 1 ))

          if [[ -z "${config}" ]]; then
          quick_die "openvpn config is required"
          fi

          if [[ -z "${user}" ]]; then
          quick_die "user must be provided explicitly via '-u' or implicitly via SUDO_USER"
          fi

          local cmd="$1"; shift

          if [[ -z "${cmd}" ]]; then
          cmd="${SHELL}"
          fi

          must ip netns add vpnshift
          must mkdir --parents "${namespace_dir}"

          # Set up loopback interface

          must nsdo ip address add '127.0.0.1/8' dev lo
          must nsdo ip address add '::1/128' dev lo
          must nsdo ip link set lo up

          # Set up veth tunnel

          must ip link add "${veth_vpn}" type veth peer name "${veth_default}"
          must ip link set "${veth_vpn}" netns "${namespace}"

          must ip link set "${veth_default}" up
          must nsdo ip link set "${veth_vpn}" up

          must ip address add "10.10.10.10/31" dev "${veth_default}"
          must nsdo ip
          address add "10.10.10.11/31" dev "${veth_vpn}"

          must nsdo ip
          route add default via "10.10.10.10" dev "${veth_vpn}"

          # Set up NAT and IP forwarding
          must sysctl --quiet net.ipv4.ip_forward=1

          # check if we need to enable masquerading via firewalld for veth_default
          if hush ps -C 'firewalld'; then
          echo "[firewalld] enabling firewalld based masquerading for ${veth_default}"

          if [[ $(firewall-cmd --get-zones | grep "${namespace}") != *"${namespace}"* ]]
          then
          echo "[firewalld] creating permanent new zone ${namespace} with target default"
          must firewall-cmd -q --permanent --new-zone="${namespace}"
          must firewall-cmd -q --permanent --zone="${namespace}" --set-target="default"
          must firewall-cmd -q --reload
          fi

          # add interface to our zone
          echo "[firewalld] adding ${veth_default} and ${veth_vpn} to zone ${namespace}"
          must firewall-cmd -q --zone="${namespace}" --change-interface="${veth_default}"

          # apply our source range to our zone
          echo "[firewalld] adding 10.10.10.10/31 as source for ${namespace}"
          must firewall-cmd -q --zone="${namespace}" --add-source=10.10.10.10/31

          # enable masquerading from our new source range on the default zone
          default_zone=$(firewall-cmd --get-default-zone)
          echo "[firewalld] enabling masquerading on default zone: ${default_zone}"
          must firewall-cmd -q --zone="${default_zone}" --add-masquerade
          must firewall-cmd -q --zone="${default_zone}" --add-rich-rule='rule family="ipv4" source address="10.10.10.10/31" masquerade'

          # optionally allow ports, services, etc. on our zone

          # enabling desired ports
          #echo "enabling all port traffic on zone ${namespace}"
          #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/udp
          #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/tcp

          # enable services
          #echo "enabling dns on zone ${namespace}"
          #must firewall-cmd -q --zone="${namespace}" --add-service=dns

          else
          must iptables --table "nat" --append "POSTROUTING" --jump "MASQUERADE" --source "10.10.10.10/31"
          fi

          # Set up DNS inside the new namespace
          printf > "${namespace_dir}/resolv.conf"
          "nameserver %snnameserver %sn"
          "108.62.19.131"
          "104.238.194.235"

          # drop in a shell to debug namespace connectivity ... the exit trap will catch exit from this and clean up
          if [[ "$_debug" == 1 ]]; then
          nsdo "${SHELL}"
          fi

          # Launch openvpn
          local tun="tunvpn"
          nsdo openvpn
          --cd "$(dirname "${config}")"
          --config "${config}"
          --dev "${tun}"
          --errors-to-stderr &

          openvpn_pid=$(ps --ppid "$!"
          --format "pid"
          --no-headers
          )

          >&2 printf "waiting for openvpn (pid = %d)n" "${openvpn_pid}"

          while ! hush nsdo ip link show "${tun}"; do
          if ! is_running "${openvpn_pid}"; then
          clean_exit 1
          fi
          sleep 0.2
          done

          # Removing the default route protects from exposure if openvpn exits
          # prematurely.
          must nsdo ip
          route delete default via "10.10.10.10" dev "${veth_vpn}"

          nsdo sudo -u "${user}" "${cmd}" "$@"
          }

          if [[ $# == 0 ]]; then
          quick_die "${usage}"
          elif [[ "$(id -u)" != 0 ]]; then
          sudo "$0" "$@"
          exit "$?"
          fi

          # Stuff needed by clean_exit() to restore previous state.
          namespace="vpnshift"
          namespace_dir="/etc/netns/${namespace}"
          forward="$(sysctl --values "net.ipv4.ip_forward")"
          rules="$(iptables-save -t nat)"
          veth_default="veth_default"
          veth_vpn="veth_vpn"

          openvpn_pid= # This is set later.

          # Enable cleanup routine.
          trap 'clean_exit 1' INT TERM
          trap 'clean_exit $?' EXIT

          main "$@"





          share|improve this answer













          I have been using the forked script provided by Felix for about six months and up until the past week it has been working quite well (once I added the sysctl -w net.ipv4.ip_forward=1 line suggested by Nehal J Wani and commented out the pipefail - as it would crash with it otherwise).



          Yet I noticed in the past week it has completely stopped working (Debian 9.4). I spent a lot of time trying to debug what went wrong, as the script is quite useful - but no matter what I tried I couldn't get it working again.



          As this is such a useful feature, I wanted to provide an alternative fork of Schnouki's work that's works well in case anyone runs into the same issue.



          https://github.com/crasm/vpnshift.sh



          #!/bin/bash
          #
          # Copyright (c) 2016, crasm <crasm@vczf.io>
          #
          # Permission to use, copy, modify, and/or distribute this software for any
          # purpose with or without fee is hereby granted, provided that the above
          # copyright notice and this permission notice appear in all copies.
          #
          # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
          # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
          # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
          # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
          # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
          # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
          # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


          usage="usage: vpnshift -c <config> [<command> [<arg>...]]
          optional:
          -u <user> Execute <command> as <user>
          -d Toggle namespace debug shell

          if not otherwise specified:
          - The command defaults to the user's shell (${SHELL}).
          - The user must be inferred from sudo.
          "

          quick_die() {
          format="$1"; shift
          >&2 printf "${format}n" "$@"
          exit 1
          }

          die() {
          format="$1"; shift
          >&2 printf "${format}n" "$@"
          clean_exit 1
          }

          hush() {
          eval "$@" > /dev/null 2> /dev/null
          }

          must() {
          eval "$@" || die "failed: %s" "$*"
          }

          is_running() {
          local pid="$1"
          hush kill -s 0 "${pid}"
          }

          sig_kill() {
          local pid="$1"
          hush kill -s KILL "${pid}"
          }

          sig_term() {
          local pid="$1"
          hush kill -s TERM "${pid}"
          }

          clean_exit() {
          local exit_code="$1"

          if is_running "${openvpn_pid}"; then
          # Kill openvpn.
          sig_term "${openvpn_pid}"
          >&2 printf "stopping openvpn (pid = %d)." "${openvpn_pid}"
          for i in {1..100}; do
          if is_running "${openvpn_pid}"; then
          sleep 0.1
          printf "."
          else
          break
          fi
          done
          printf "n"

          if is_running "${openvpn_pid}"; then
          >&2 echo "forced to kill openvpn"
          sig_kill "${openvpn_pid}"
          fi
          else
          >&2 echo "openvpn exited"
          fi

          # don't start cleaning up until openvpn is gone
          hush ip netns delete "${namespace}"
          hush rm --recursive --force "${namespace_dir}"
          hush sysctl --quiet net.ipv4.ip_forward="${forward}"
          if hush ps -C 'firewalld'; then
          echo "[firewalld] clearing firewalld state"
          hush systemctl restart firewalld
          else
          echo "${rules}" | hush iptables-restore
          fi

          # Sometimes there's a lag for the veths to be deleted by linux, so we
          # delete it manually.
          hush ip link delete "${veth_default}"
          hush ip link delete "${veth_vpn}"

          exit "${exit_code}"
          }

          nsdo() {
          ip netns exec "${namespace}" "$@"
          }

          _debug=0

          main() {
          local config=
          local user="${SUDO_USER}"
          while getopts "hdc:u:" opt; do
          case "${opt}" in
          h) quick_die "${usage}" ;;
          d) _debug=1 ;;
          c) config="$(realpath "${OPTARG}")" ;;
          u) user="${OPTARG}" ;;
          *) quick_die "unknown option: %s" "${opt}" ;;
          esac
          done
          shift $(( OPTIND - 1 ))

          if [[ -z "${config}" ]]; then
          quick_die "openvpn config is required"
          fi

          if [[ -z "${user}" ]]; then
          quick_die "user must be provided explicitly via '-u' or implicitly via SUDO_USER"
          fi

          local cmd="$1"; shift

          if [[ -z "${cmd}" ]]; then
          cmd="${SHELL}"
          fi

          must ip netns add vpnshift
          must mkdir --parents "${namespace_dir}"

          # Set up loopback interface

          must nsdo ip address add '127.0.0.1/8' dev lo
          must nsdo ip address add '::1/128' dev lo
          must nsdo ip link set lo up

          # Set up veth tunnel

          must ip link add "${veth_vpn}" type veth peer name "${veth_default}"
          must ip link set "${veth_vpn}" netns "${namespace}"

          must ip link set "${veth_default}" up
          must nsdo ip link set "${veth_vpn}" up

          must ip address add "10.10.10.10/31" dev "${veth_default}"
          must nsdo ip
          address add "10.10.10.11/31" dev "${veth_vpn}"

          must nsdo ip
          route add default via "10.10.10.10" dev "${veth_vpn}"

          # Set up NAT and IP forwarding
          must sysctl --quiet net.ipv4.ip_forward=1

          # check if we need to enable masquerading via firewalld for veth_default
          if hush ps -C 'firewalld'; then
          echo "[firewalld] enabling firewalld based masquerading for ${veth_default}"

          if [[ $(firewall-cmd --get-zones | grep "${namespace}") != *"${namespace}"* ]]
          then
          echo "[firewalld] creating permanent new zone ${namespace} with target default"
          must firewall-cmd -q --permanent --new-zone="${namespace}"
          must firewall-cmd -q --permanent --zone="${namespace}" --set-target="default"
          must firewall-cmd -q --reload
          fi

          # add interface to our zone
          echo "[firewalld] adding ${veth_default} and ${veth_vpn} to zone ${namespace}"
          must firewall-cmd -q --zone="${namespace}" --change-interface="${veth_default}"

          # apply our source range to our zone
          echo "[firewalld] adding 10.10.10.10/31 as source for ${namespace}"
          must firewall-cmd -q --zone="${namespace}" --add-source=10.10.10.10/31

          # enable masquerading from our new source range on the default zone
          default_zone=$(firewall-cmd --get-default-zone)
          echo "[firewalld] enabling masquerading on default zone: ${default_zone}"
          must firewall-cmd -q --zone="${default_zone}" --add-masquerade
          must firewall-cmd -q --zone="${default_zone}" --add-rich-rule='rule family="ipv4" source address="10.10.10.10/31" masquerade'

          # optionally allow ports, services, etc. on our zone

          # enabling desired ports
          #echo "enabling all port traffic on zone ${namespace}"
          #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/udp
          #must firewall-cmd -q --zone="${namespace}" --add-port=1025-65535/tcp

          # enable services
          #echo "enabling dns on zone ${namespace}"
          #must firewall-cmd -q --zone="${namespace}" --add-service=dns

          else
          must iptables --table "nat" --append "POSTROUTING" --jump "MASQUERADE" --source "10.10.10.10/31"
          fi

          # Set up DNS inside the new namespace
          printf > "${namespace_dir}/resolv.conf"
          "nameserver %snnameserver %sn"
          "108.62.19.131"
          "104.238.194.235"

          # drop in a shell to debug namespace connectivity ... the exit trap will catch exit from this and clean up
          if [[ "$_debug" == 1 ]]; then
          nsdo "${SHELL}"
          fi

          # Launch openvpn
          local tun="tunvpn"
          nsdo openvpn
          --cd "$(dirname "${config}")"
          --config "${config}"
          --dev "${tun}"
          --errors-to-stderr &

          openvpn_pid=$(ps --ppid "$!"
          --format "pid"
          --no-headers
          )

          >&2 printf "waiting for openvpn (pid = %d)n" "${openvpn_pid}"

          while ! hush nsdo ip link show "${tun}"; do
          if ! is_running "${openvpn_pid}"; then
          clean_exit 1
          fi
          sleep 0.2
          done

          # Removing the default route protects from exposure if openvpn exits
          # prematurely.
          must nsdo ip
          route delete default via "10.10.10.10" dev "${veth_vpn}"

          nsdo sudo -u "${user}" "${cmd}" "$@"
          }

          if [[ $# == 0 ]]; then
          quick_die "${usage}"
          elif [[ "$(id -u)" != 0 ]]; then
          sudo "$0" "$@"
          exit "$?"
          fi

          # Stuff needed by clean_exit() to restore previous state.
          namespace="vpnshift"
          namespace_dir="/etc/netns/${namespace}"
          forward="$(sysctl --values "net.ipv4.ip_forward")"
          rules="$(iptables-save -t nat)"
          veth_default="veth_default"
          veth_vpn="veth_vpn"

          openvpn_pid= # This is set later.

          # Enable cleanup routine.
          trap 'clean_exit 1' INT TERM
          trap 'clean_exit $?' EXIT

          main "$@"






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered May 10 '18 at 21:04









          zefferzeffer

          1




          1























              0














              I have a computer connected to a lan with wired ethernet, which allowed me to take a bit of a different tack with this: bridging! No iptables modifications or manual IP address specifications are required. Here's my whole solution in commands.



              lan='eth0'  # Replace with your lan interface.
              nsname='vpn'

              # Make the netns.
              ip netns add "$nsname"

              # Make the inter-namespace pipe.
              ip link add veth.host type veth peer veth.vpn
              ip link set veth.host up
              ip link set veth.vpn netns "$nsname" up

              # Bridge the host end of the pipe with the wired ethernet.
              ip link add br0 type bridge
              ip link set veth.host master br0
              ip link set "$lan" master br0 up
              ip link set br0 up

              # Start dhcpcd on the bridge for the host to use and on veth.vpn
              # for the vpn netns to use. The router will grant separate IP
              # addresses to both! (They have different MAC addresses.)
              dhcpcd br0
              ip netns exec "$nsname" dhcpcd veth.vpn

              # OK, now start the VPN.
              ip netns exec "$nsname" openvpn --config etc etc etc




              share




























                0














                I have a computer connected to a lan with wired ethernet, which allowed me to take a bit of a different tack with this: bridging! No iptables modifications or manual IP address specifications are required. Here's my whole solution in commands.



                lan='eth0'  # Replace with your lan interface.
                nsname='vpn'

                # Make the netns.
                ip netns add "$nsname"

                # Make the inter-namespace pipe.
                ip link add veth.host type veth peer veth.vpn
                ip link set veth.host up
                ip link set veth.vpn netns "$nsname" up

                # Bridge the host end of the pipe with the wired ethernet.
                ip link add br0 type bridge
                ip link set veth.host master br0
                ip link set "$lan" master br0 up
                ip link set br0 up

                # Start dhcpcd on the bridge for the host to use and on veth.vpn
                # for the vpn netns to use. The router will grant separate IP
                # addresses to both! (They have different MAC addresses.)
                dhcpcd br0
                ip netns exec "$nsname" dhcpcd veth.vpn

                # OK, now start the VPN.
                ip netns exec "$nsname" openvpn --config etc etc etc




                share


























                  0












                  0








                  0







                  I have a computer connected to a lan with wired ethernet, which allowed me to take a bit of a different tack with this: bridging! No iptables modifications or manual IP address specifications are required. Here's my whole solution in commands.



                  lan='eth0'  # Replace with your lan interface.
                  nsname='vpn'

                  # Make the netns.
                  ip netns add "$nsname"

                  # Make the inter-namespace pipe.
                  ip link add veth.host type veth peer veth.vpn
                  ip link set veth.host up
                  ip link set veth.vpn netns "$nsname" up

                  # Bridge the host end of the pipe with the wired ethernet.
                  ip link add br0 type bridge
                  ip link set veth.host master br0
                  ip link set "$lan" master br0 up
                  ip link set br0 up

                  # Start dhcpcd on the bridge for the host to use and on veth.vpn
                  # for the vpn netns to use. The router will grant separate IP
                  # addresses to both! (They have different MAC addresses.)
                  dhcpcd br0
                  ip netns exec "$nsname" dhcpcd veth.vpn

                  # OK, now start the VPN.
                  ip netns exec "$nsname" openvpn --config etc etc etc




                  share













                  I have a computer connected to a lan with wired ethernet, which allowed me to take a bit of a different tack with this: bridging! No iptables modifications or manual IP address specifications are required. Here's my whole solution in commands.



                  lan='eth0'  # Replace with your lan interface.
                  nsname='vpn'

                  # Make the netns.
                  ip netns add "$nsname"

                  # Make the inter-namespace pipe.
                  ip link add veth.host type veth peer veth.vpn
                  ip link set veth.host up
                  ip link set veth.vpn netns "$nsname" up

                  # Bridge the host end of the pipe with the wired ethernet.
                  ip link add br0 type bridge
                  ip link set veth.host master br0
                  ip link set "$lan" master br0 up
                  ip link set br0 up

                  # Start dhcpcd on the bridge for the host to use and on veth.vpn
                  # for the vpn netns to use. The router will grant separate IP
                  # addresses to both! (They have different MAC addresses.)
                  dhcpcd br0
                  ip netns exec "$nsname" dhcpcd veth.vpn

                  # OK, now start the VPN.
                  ip netns exec "$nsname" openvpn --config etc etc etc





                  share











                  share


                  share










                  answered 5 mins ago









                  enigmaticPhysicistenigmaticPhysicist

                  5271410




                  5271410






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Unix & Linux Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f270883%2ftrying-to-run-openvpn-in-network-namespace%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      CARDNET

                      Boot-repair Failure: Unable to locate package grub-common:i386

                      濃尾地震