Skip to content

Instantly share code, notes, and snippets.

@nehaljwani
Created July 12, 2020 16:16
Show Gist options
  • Save nehaljwani/0f63165bf842e22d8d9e80e05f1a53f1 to your computer and use it in GitHub Desktop.
Save nehaljwani/0f63165bf842e22d8d9e80e05f1a53f1 to your computer and use it in GitHub Desktop.
OpenFortiVPN Inside Linux Network Namespace
#!/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="mycompanyvpn"
NS_EXEC="sudo ip netns exec $NS_NAME"
VPN_ENDPOINT="vpn.mycompany.com"
VPN_USER="<username>"
WIRED_INTERFACE="enp1s0"
WIRELESS_INTERFACE="wlp2s0"
OUT_IF="${NS_NAME}0"
IN_IF="${NS_NAME}1"
OUT_IP="10.202.200.1"
IN_IP="10.202.200.2"
#set -uxeo pipefail
start_vpn() {
echo -n "VPN Password: "
read -s VPN_PASS
echo "Add network interface"
# create the network namespace
sudo ip netns add "$NS_NAME"
# start the loopback interface in the namespace
eval $NS_EXEC ip addr add 127.0.0.1/8 dev lo
eval $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}"
eval $NS_EXEC ip addr add "${IN_IP}"/24 dev "${IN_IF}"
eval $NS_EXEC ip link set dev "${IN_IF}" mtu 1492
eval $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
# Works well with v1.10.0
# Works well with v1.11.0 (auto adds search domain too, nice!)
# Doesn't work well with v1.12.0 (tries to use resolvconf, which doesn't work)
# eval $NS_EXEC /home/wani/repos/openfortivpn/openfortivpn -vvv $VPN_ENDPOINT:443 --username=$VPN_USER --otp=1 -p$VPN_PASS &
# Works well with v1.13.3 (requires the use of --use-resolveconf=0)
# Works will with v1.14.1
eval $NS_EXEC /home/wani/repos/openfortivpn/openfortivpn --use-resolvconf=0 -vvv $VPN_ENDPOINT:443 --username=$VPN_USER --otp=1 -p$VPN_PASS &
# wait for the tunnel interface to come up
while ! eval $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 network namespace
#eval $NS_EXEC su ${USER}
#trap stop_vpn EXIT
@nehaljwani
Copy link
Author

nehaljwani commented Jul 12, 2020

I typically use this like this (in a tmux session):

source openfortivpn_inside_netns.sh
start_vpn

When I want to close it, I do (in the same tmux session):

stop_vpn

To use this tunnel to SSH to my office computer, I have this in ~/.bashrc:

tssh () {
    sudo /usr/sbin/ip netns exec mycompanyvpn \
        su $(id -un) -c \
        "SSH_AUTH_SOCK=/run/user/$(id -u)/keyring/ssh ssh -v companyusername@$1"
}

And then I use it like this:

tssh hostname.mycompany.com

And I have a password less sudo rule in /etc/sudoers

<username>   ALL=(ALL) NOPASSWD: /usr/sbin/ip netns exec mycompanyvpn su *

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment