Difficulties in an unprivileged container
Installing openvpn from a debian package and then simply running it seems like the normal way to go. However, trying to do this in Docker, unprivileged, is much harder. Openvpn needs access to various privileged network bits that aren't available typically:
The tun interface from the host needs to be available, so we mount /dev/net as a volume into the container.
The typical capabilities that are dropped have to be not dropped (drop_capabilities: false
in float) and docker needs to have NET_ADMIN capability added (docker_options: '--cap-add=NET_ADMIN'
in float). It also requires that we build the Docker container in a way that enables certain capabilities on the openvpn binary, as well as /bin/ip (which it uses to setup things):
RUN setcap cap_net_admin,cap_net_bind_service+ep /usr/sbin/openvpn
RUN setcap cap_net_admin+ep /bin/ip
All of this lets us actually get openvpn running, in an "unprivileged" container. It has a lot of access, which makes it not very unprivileged anymore, but it does actually "work". Where "work" means you can connect to it from a client, but then you cannot route traffic, because we need to be able to setup certain masquerading, otherwise we end up with this:
Dec 23 15:33:31 ptfloatrp01 ovpn-tcp-server[32024]: UNLIMITED/107.179.249.231:43700 MULTI: bad source address from client [192.168.1.112], packet dropped
So the question now is: should we stop trying to make an unprivileged openvpn service and if so, should we just use the container from https://github.com/kylemanna/docker-openvpn to do this, or just add the pieces that we need (they have a number of things we do not need).
Specifically, these are the things that need to be setup:
function setupIptablesAndRouting {
iptables -t nat -C POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE || {
iptables -t nat -A POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE
}
for i in "${OVPN_ROUTES[@]}"; do
iptables -t nat -C POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE || {
iptables -t nat -A POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE
}
done
}
Where $OVPN_SERVER
is the network
netmask
passed to openvpn for its --server
flag (or server 10.41.0.0 255.255.248.0
in the config file), and $OVPN_NATDEVICE
is eth0
unless something else is done.
For the second one, $OVPN_ROUTES
is just the same thing as $OVPN_SERVER
unless something more complicated is passed.