I often have to deal with VPNs, either to connect to the company network, my own network when I’m abroad or to various other places where I’ve got servers I manage.
All of those VPNs use OpenVPN, all with a similar configuration and unfortunately quite a lot of them with overlapping networks. That means that when I connect to them, parts of my own network are no longer reachable or it means that I can’t connect to more than one of them at once.
Those I suspect are all pretty common issues with VPN users, especially those working with or for companies who over the years ended up using most of the rfc1918 subnets.
So I thought, I’m working with containers every day, nowadays we have those cool namespaces in the kernel which let you run crazy things as a a regular user, including getting your own, empty network stack, so why not use that?
Well, that’s what I ended up doing and so far, that’s all done in less than 100 lines of good old POSIX shell script 🙂
That gives me, fully unprivileged non-overlapping VPNs! OpenVPN and everything else run as my own user and nobody other than the user spawning the container can possibly get access to the resources behind the VPN.
The code is available at: git clone git://github.com/stgraber/vpn-container
Then it’s as simple as: ./start-vpn VPN-NAME CONFIG
What happens next is the script will call socat to proxy the VPN TCP socket to a UNIX socket, then a user namespace, network namespace, mount namespace and uts namespace are all created for the container. Your user is root in that namespace and so can start openvpn and create network interfaces and routes. With careful use of some bind-mounts, resolvconf and byobu are also made to work so DNS resolution is functional and we can start byobu to easily allow as many shell as you want in there.
In the end it looks like this:
stgraber@dakara:~/vpn$ ./start-vpn stgraber.net ../stgraber-vpn/stgraber.conf WARN: could not reopen tty: No such file or directory lxc: call to cgmanager_move_pid_abs_sync(name=systemd) failed: invalid request Fri Sep 26 17:48:07 2014 OpenVPN 2.3.2 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [eurephia] [MH] [IPv6] built on Feb 4 2014 Fri Sep 26 17:48:07 2014 WARNING: No server certificate verification method has been enabled. See http://openvpn.net/howto.html#mitm for more info. Fri Sep 26 17:48:07 2014 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts Fri Sep 26 17:48:07 2014 Attempting to establish TCP connection with [AF_INET]127.0.0.1:1194 [nonblock] Fri Sep 26 17:48:07 2014 TCP connection established with [AF_INET]127.0.0.1:1194 Fri Sep 26 17:48:07 2014 TCPv4_CLIENT link local: [undef] Fri Sep 26 17:48:07 2014 TCPv4_CLIENT link remote: [AF_INET]127.0.0.1:1194 Fri Sep 26 17:48:09 2014 [vorash.stgraber.org] Peer Connection Initiated with [AF_INET]127.0.0.1:1194 Fri Sep 26 17:48:12 2014 TUN/TAP device tun0 opened Fri Sep 26 17:48:12 2014 Note: Cannot set tx queue length on tun0: Operation not permitted (errno=1) Fri Sep 26 17:48:12 2014 do_ifconfig, tt->ipv6=1, tt->did_ifconfig_ipv6_setup=1 Fri Sep 26 17:48:12 2014 /sbin/ip link set dev tun0 up mtu 1500 Fri Sep 26 17:48:12 2014 /sbin/ip addr add dev tun0 172.16.35.50/24 broadcast 172.16.35.255 Fri Sep 26 17:48:12 2014 /sbin/ip -6 addr add 2001:470:b368:1035::50/64 dev tun0 Fri Sep 26 17:48:12 2014 /etc/openvpn/update-resolv-conf tun0 1500 1544 172.16.35.50 255.255.255.0 init dhcp-option DNS 172.16.20.30 dhcp-option DNS 172.16.20.31 dhcp-option DNS 2001:470:b368:1020:216:3eff:fe24:5827 dhcp-option DNS nameserver dhcp-option DOMAIN stgraber.net Fri Sep 26 17:48:12 2014 add_route_ipv6(2607:f2c0:f00f:2700::/56 -> 2001:470:b368:1035::1 metric -1) dev tun0 Fri Sep 26 17:48:12 2014 add_route_ipv6(2001:470:714b::/48 -> 2001:470:b368:1035::1 metric -1) dev tun0 Fri Sep 26 17:48:12 2014 add_route_ipv6(2001:470:b368::/48 -> 2001:470:b368:1035::1 metric -1) dev tun0 Fri Sep 26 17:48:12 2014 add_route_ipv6(2001:470:b511::/48 -> 2001:470:b368:1035::1 metric -1) dev tun0 Fri Sep 26 17:48:12 2014 add_route_ipv6(2001:470:b512::/48 -> 2001:470:b368:1035::1 metric -1) dev tun0 Fri Sep 26 17:48:12 2014 Initialization Sequence Completed To attach to this VPN, use: byobu -S /home/stgraber/vpn/stgraber.net.byobu To kill this VPN, do: byobu -S /home/stgraber/vpn/stgraber.net.byobu kill-server or from inside byobu: byobu kill-server
After that, just copy/paste the byobu command and you’ll get a shell inside the container. Don’t be alarmed by the fact that you’re root in there. root is mapped to your user’s uid and gid outside the container so it’s actually just your usual user but with a different name and with privileges against the resources owned by the container.
You can now use the VPN as you want without any possible overlap or conflict with any route or VPN you may be running on that system and with absolutely no possibility that a user sharing your machine may access your running VPN.
This has so far been tested with 5 different VPNs, on a regular Ubuntu 14.04 LTS system with all VPNs being TCP based. UDP based VPNs would probably just need a couple of tweaks to the socat unix-socket proxy.
Enjoy!
Nice writeup. Just wanted to mention the handy “ip netns” which helps if you don’t need a full blown container. I use it as a quick way to select the default route for this or that process.
Be great if you added an example of your – ip netns – setup or script.
You’re fanatic:)
Great write up, now the question is how can I replicate that in LXD ?
I am using LXC containers for OpenVPN servers with success.
I tried to do active backup with two tunnels and two tap interfaces both slaved into a bond interface within an LXC container.
But I didn’t manage to get access to the bond interface within the container.
Is there anything I can do to access the bond iface in LXC?
I’m getting this following notice, should it be a concern?
“Note: Cannot set tx queue length on tun0: Operation not permitted (errno=1)”
Ubuntu 18.04
LXD 3.0.3
Openvpn 2.4.7
@Gigga
same error was discussed here:
https://discuss.linuxcontainers.org/t/openvpn-on-lxd-2-0/969