Created
July 30, 2017 09:43
-
-
Save nehaljwani/f6e9d12102157161bfceb7eea80c319f to your computer and use it in GitHub Desktop.
OpenConnect VPN Inside Linux Network Namespace
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# start openconnect tunnel and shell 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 ------------ | |
NS_NAME="myvpn" | |
NS_EXEC="sudo -E ip netns exec $NS_NAME" | |
VPN_ENDPOINT="superawesomevpn.io/someplace" | |
VPN_USER="nwani" | |
WIRED_INTERFACE="enp1s0" | |
WIRELESS_INTERFACE="wlp2s0" | |
OUT_IF="${NS_NAME}0" | |
IN_IF="${NS_NAME}1" | |
OUT_IP="10.200.200.1" | |
IN_IP="10.200.200.2" | |
read -s -p "VPN Password: " VPN_PASS | |
set -uxeo pipefail | |
start_vpn() { | |
echo "Add network interface" | |
# create the network namespace | |
sudo 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 (${IN_IF}) to use the interface out of the namespace (${OUT_IF}) | |
# as its default gateway | |
sudo ip link add "${OUT_IF}" type veth peer name "${IN_IF}" | |
sudo ip link set "${OUT_IF}" up | |
sudo ip link set "${IN_IF}" netns "${NS_NAME}" up | |
sudo ip addr add "${OUT_IP}"/24 dev "${OUT_IF}" | |
$NS_EXEC ip addr add "${IN_IP}"/24 dev "${IN_IF}" | |
$NS_EXEC ip link set dev "${IN_IF}" mtu 1492 | |
$NS_EXEC ip route add default via "${OUT_IP}" dev "${IN_IF}" | |
# make sure ipv4 forwarding is enabled | |
sudo sysctl -w net.ipv4.ip_forward=1 | |
# configure the nameserver to use inside the namespace | |
# TODO use VPN-provided DNS servers in order to prevent leaks | |
sudo mkdir -p "/etc/netns/${NS_NAME}" | |
cat <<EOF | sudo tee "/etc/netns/${NS_NAME}/resolv.conf" || exit 1 | |
nameserver 8.8.8.8 | |
nameserver 8.8.4.4 | |
EOF | |
# IPv4 NAT, you may need to adjust the interface name prefixes | |
sudo iptables -t nat -A POSTROUTING -o "${WIRED_INTERFACE}" -m mark --mark 0x29a -j MASQUERADE | |
sudo iptables -t nat -A POSTROUTING -o "${WIRELESS_INTERFACE}" -m mark --mark 0x29a -j MASQUERADE | |
sudo iptables -t mangle -A PREROUTING -i "${OUT_IF}" -j MARK --set-xmark 0x29a/0xffffffff | |
# start openconnect in the namespace | |
echo "Starting VPN" | |
set +x | |
echo -e "${VPN_PASS}\npush" | $NS_EXEC /usr/sbin/openconnect --interface vpn0 $VPN_ENDPOINT -u $VPN_USER --passwd-on-stdin & | |
# wait for the tunnel interface to come up | |
while ! $NS_EXEC ip link show dev vpn0 >/dev/null 2>&1 ; do sleep .5 ; done | |
} | |
stop_vpn() { | |
echo "Stopping VPN" | |
sudo ip netns pids $NS_NAME | sudo xargs -rd'\n' kill -SIGINT | |
# TODO wait for terminate | |
sleep 2 | |
# clear NAT | |
sudo iptables -t nat -D POSTROUTING -o "${WIRED_INTERFACE}" -m mark --mark 0x29a -j MASQUERADE | |
sudo iptables -t nat -D POSTROUTING -o "${WIRELESS_INTERFACE}" -m mark --mark 0x29a -j MASQUERADE | |
sudo iptables -t mangle -D PREROUTING -i "${OUT_IF}" -j MARK --set-xmark 0x29a/0xffffffff | |
echo "Delete network interface" | |
sudo rm -rf "/etc/netns/${NS_NAME}" | |
sudo ip netns delete "${NS_NAME}" | |
sudo ip link delete "${OUT_IF}" | |
} | |
start_vpn | |
# start app inside n/w namespace | |
$NS_EXEC su ${USER} | |
trap stop_vpn EXIT |
Does using a subshell for the openconnect command help?
@nehaljwani thanks for the suggestion. I tried that but couldn't make it work. I've ended up getting it working by replacing the openconnect line with this:
$NS_EXEC /usr/sbin/openconnect -b --authgroup=VPN --interface vpn0 $VPN_ENDPOINT -u $VPN_USER
I enter both passwords manually to stdin, then openconnect
goes to the background (-b
) and the shell script resumes
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the script, just what I was looking for!
My VPN provider also requires one time code to be passed in aswell, so I'm trying to get an expect script working to accommodate that. I'm not having any success.... I can run openconnect inside expect script but if I send it to the background, it seems unable to read the input properly. If I don't background it, it works fine but then it holds up the namespace script and prevents it dropping the user into a shell 😞