Jump to content
Not connected, Your IP: 3.16.82.182
MassiveGoldenSwan

Prevent Leaks with Linux and Network Namespaces

Recommended Posts

This is meant for the How-To forum, but I'm posting it as a draft here first to get feedback on it.

 

In my case, all I need is to run certain programs within the VPN, and stop those programs from communicating if the VPN goes down. My networking knowledge is limited, but with help from my good friend fr0zen, I've got this setup going.

 

Network namespaces are a pretty old feature of the Linux kernel at this point (2008), introduced as part of cgroups. The bulk of the logic behind setting them up for VPN usage comes from this blog post.

 

The final scripts are posted at the end of this post, but I will explain how they do their magic to the best of my abilities.

start-vpn.sh

This is the primary script, the one you call directly. The other two are helper scripts.

 

First, the variables:

REMOTE=america.vpn.airdns.org
BRIDGE=192.168.1.6
INTERNAL=192.168.1.8
GATEWAY=192.168.1.1
IFACE=eth0

USER=jaquer
COMMAND="screen -dmS vpn -c /home/jaquer/.screenrc-vpn"

CONFIG=AirVPN_America_UDP-443.ovpn 

REMOTE is the hostname that you will be connecting to. It comes from the .ovpn file you download from AirVPN. I suppose if you wanted to be really clever you could grep it out of the file, but I like to keep my scripts lean and mean.

 

BRIDGE is the IP address that we will bridge with out VPN. This is the local address for your computer. Again, I'm sure you could script it if you wanted, but I assign my computers static IPs when they run important services.

 

INTERNAL will be the IP we assign to the VPN on our local network. Again, I pick an IP I know will not be assigned to another device.

 

GATEWAY is... your regular gateway. Usually your routed. You can determine it via ip route show. It's the IP number listed as default via.

 

IFACE is the interface you will use to bridge the two connections. Keep in mind, this interface is brought down while the script runs. You will lose connectivity at this point, so make sure you test the script over the terminal as opposed to SSH'ing into the computer. If you happen to have two NICs, I highly recommend you run the script on the second one (eth1).

 

USER and COMMAND are where things start to get interesting. Using namespaces, you need to prepend every command with ip netns exec vpn. The beauty of creating a screen session under the namespace means you can just switch to it, and now every program you run in it will inherit the VPN network settings.

 

CONFIG is the path to your .ovpn file. Keep it in the same directory as the scripts.

 

Now, we're ready to get the connectivity going.

REMOTE=$(resolveip -s ${REMOTE})

We need to get the IP of the remote server now, because we won't be able to resolve it once we remove the default gateway a bit into the script.

ip netns add vpn || exit 1
ip link add veth0 type veth peer name veth1 || exit 1
ifconfig veth0 0.0.0.0 up || exit 1
ip link set veth1 netns vpn || exit 1

These setup the basic virtual interface for the bridge.

ifdown ${IFACE} || exit 1
ifconfig ${IFACE} 0.0.0.0 up || exit 1
brctl addbr br0 || exit 1
brctl addif br0 eth1 veth0 || exit 1
ifconfig br0 ${BRIDGE} netmask 255.255.255.0 up || exit 1

Here we bring down out physical interface, and bring it back up as part of the bridge. You will lose connectivity at this point, so make sure you test the script over the terminal as opposed to SSH'ing into the computer.

route add default gw ${GATEWAY}
ip netns exec vpn ifconfig veth1 ${INTERNAL} netmask 255.255.255.0 up || exit 1 

We re-add the gateway to the interface's route, and bring the virtual interface up.

ip netns exec vpn route add ${REMOTE} gw ${GATEWAY} || exit 1 

This is a clever bit of the script. We want the gateway to route packets to the remote server's IP only. Since we're calling it inside the namespace, nothing else running in the namespace will resolve. We can only connect to the remote server at this point.

ip netns exec vpn su --login --command "${COMMAND}" ${USER} || exit 1
ip netns exec vpn openvpn --config ${CONFIG} --script-security 2 --up vpn-up.sh &

We run the screen session inside the namespace, and run OpenVPN inside it too, with a custom script to be called when the connection is completed.

PID=${!}
trap "kill ${PID}; wait ${PID}" INT
wait
./vpn-down.sh

We capture the PID of the OpenVPN process, and wait for it to exit. The trap command will catch Ctrl+C and kill said process if we need to exit manually. Finally, we revert all the changes we made when we exit the VPN.

vpn-up.sh
ip route add default via ${route_vpn_gateway}
echo ${ifconfig_local} > /tmp/vpn-ip.txt

This script seems simple, but it's actually the one that insures we have connectivity. It adds the VPN gateway as the default, which insures that all traffic going out (except to the VPN server itself), is routed through the VPN. We also save the internal IP to a text file, because we can then use it to bind services to it for double protection.

vpn-down.sh

This is just the cleanup script. It will prompt you to exit the screen session and all the programs withing it first. In order to be able to delete the bridge, we have to bring our main interface down again. You will lose connectivity at this point, so make sure you test the script over the terminal as opposed to SSH'ing into the computer.

 

To recap:

 

As root, run start-vpn.sh. When the connection is made, switch user accounts to the USER account, and re-attach the screen session ( screen -dr vpn). Now any program you run from within the session will only use the VPN connection. Test it by running wget -qO- http://ipecho.net/plain.

 

That's the gist of it. I hope somebody finds it helpful. Here are the full scripts:

 

start-vpn.sh

#!/bin/sh

REMOTE=america.vpn.airdns.org
BRIDGE=192.168.1.6
INTERNAL=192.168.1.8
GATEWAY=192.168.1.1
IFACE=eth0

USER=jaquer
COMMAND="screen -dmS vpn -c /home/jaquer/.screenrc-vpn"

CONFIG=AirVPN_America_UDP-443.ovpn

REMOTE=$(resolveip -s ${REMOTE})

cd $(dirname ${0})

ip netns add vpn || exit 1
ip link add veth0 type veth peer name veth1 || exit 1
ifconfig veth0 0.0.0.0 up || exit 1
ip link set veth1 netns vpn || exit 1
ifdown ${IFACE} || exit 1
ifconfig ${IFACE} 0.0.0.0 up || exit 1
brctl addbr br0 || exit 1
brctl addif br0 eth1 veth0 || exit 1
ifconfig br0 ${BRIDGE} netmask 255.255.255.0 up || exit 1
route add default gw ${GATEWAY}
ip netns exec vpn ifconfig veth1 ${INTERNAL} netmask 255.255.255.0 up || exit 1
ip netns exec vpn route add ${REMOTE} gw ${GATEWAY} || exit 1

ip netns exec vpn su --login --command "${COMMAND}" ${USER} || exit 1

ip netns exec vpn openvpn --config ${CONFIG} --script-security 2 --up vpn-up.sh &

PID=${!}

trap "kill ${PID}; wait ${PID}" INT

wait

./vpn-down.sh

vpn-up.sh

#!/bin/sh

ip route add default via ${route_vpn_gateway}
echo ${ifconfig_local} > /tmp/vpn-ip.txt[/code]

vpn-down.sh

#!/bin/sh

IFACE=eth0

echo ""
echo "OpenVPN exited. Exit all commands running inside VPN before continuing."
echo -n "Press ENTER to continue..."
read INPUT

ifconfig veth0 down
ifconfig br0 down
ifconfig ${IFACE} down
brctl delbr br0
ip netns del vpn
ifconfig ${IFACE} up

rm -f /tmp/vpn-ip.txt

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Security Check
    Play CAPTCHA Audio
    Refresh Image

×
×
  • Create New...