Trying to run OpenVPN in Network Namespace
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
add a comment |
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
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
add a comment |
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
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
linux networking openvpn namespace
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
add a comment |
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
add a comment |
3 Answers
3
active
oldest
votes
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
@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 tosysctl -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
|
show 2 more comments
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 "$@"
add a comment |
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
@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 tosysctl -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
|
show 2 more comments
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
@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 tosysctl -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
|
show 2 more comments
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
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
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 tosysctl -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
|
show 2 more comments
@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 tosysctl -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
|
show 2 more comments
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 "$@"
add a comment |
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 "$@"
add a comment |
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 "$@"
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 "$@"
answered May 10 '18 at 21:04
zefferzeffer
1
1
add a comment |
add a comment |
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
add a comment |
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
add a comment |
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
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
answered 5 mins ago
enigmaticPhysicistenigmaticPhysicist
5271410
5271410
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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