aboutsummaryrefslogtreecommitdiff
#!/bin/sh

# We give the full path, because PATH environment variable
# might be unset if run by cron
OVPN_COMMAND="/usr/sbin/openvpn"

OPENVPN_CONFIG="$1"
# for routing some traffic from within the namespace to physical
# network (e.g. database connection) we need to create a veth pair;
# ip datagrams routed through veth pair are going to have veth's private address
# as their source address - we need to change it to the address of our physical
# network device using iptables' SNAT. This address is provided by the caller.
PHYSICAL_IP="$2"
# as we want multiple instances of vpn_wrapper.sh to be able to
# run simultaneously, we need unique ip addresses for veth devices, which
# caller provides to us in command line arguments
VETH_HOST0="$3"
VETH_HOST1="$4"
# caller specifies space-delimited subnets, traffic to which should not be
# routed through the vpn (<database_ip>/32 is going to be here)
ROUTE_THROUGH_VETH="$5"
# we use a unique id provided in 6th argument to tag namespace name
ID="$6"

# rest of args is the command to run in network namespace
for _ in `seq 6`; do
    shift
done

# to enable multiple instances of this script to run simultaneously,
# we tag namespace name
NAMESPACE_NAME=0tdns$ID
NETNS_SCRIPT=/var/lib/0tdns/netns-script

# in case we want some process in the namespace to be able
# to resolve domain names via libc we put some random public
# dns in namespace sepcific's resolv.conf;
# note, that while libunbound we're using will probably have
# dns addresses provided by us, it is still possible to pass
# a domain name as forwarder address to unbound, in which case
# it will try to resolve it first using libc
DEFAULT_DNS=23.253.163.53
mkdir -p /etc/netns/$NAMESPACE_NAME/
echo nameserver $DEFAULT_DNS > /etc/netns/$NAMESPACE_NAME/resolv.conf

# starts openvpn with our just-created helper script, which calls
# the netns-script, which creates tun inside network namespace
# of name $NAMESPACE_NAME
# we could consider using --daemon option instead of &
$OVPN_COMMAND --ifconfig-noexec --route-noexec --up $NETNS_SCRIPT \
	      --route-up $NETNS_SCRIPT --down $NETNS_SCRIPT \
	      --config "$OPENVPN_CONFIG" --script-security 2 \
	      --connect-timeout 20 --connect-retry-max 1 --verb 0 \
	      --setenv NAMESPACE_NAME $NAMESPACE_NAME \
	      --setenv WRAPPER_PID $$ \
	      --setenv VETH_HOST0 $VETH_HOST0 \
	      --setenv VETH_HOST1 $VETH_HOST1 \
	      --setenv ROUTE_THROUGH_VETH "$ROUTE_THROUGH_VETH"\ $DEFAULT_DNS/32 \
	      --setenv PHYSICAL_IP $PHYSICAL_IP &

OPENVPN_PID=$!

# waiting for signal from our netns script
# https://stackoverflow.com/questions/9052847/implementing-infinite-wait-in-shell-scripting
trap true usr1

# wait on openvpn process;
# if we get sigusr1 from netns-script - wait will
# terminate with 138 (128 + signal number);
# if openvpn process dies - wait will also terminate,
# but with openvpn's exit value
wait $OPENVPN_PID

if [ $? = 138 ]; then
    # we received sigusr1 from netns-script, namespace is ready

    # run the provided command inside newly created namespace
    # under '0tdns' user;
    ip netns exec $NAMESPACE_NAME sudo -u 0tdns "$@"

    if [ $? = 0 ]; then
	RETVAL=0
    else
	RETVAL=2
    fi
    
    # close the connection
    kill $OPENVPN_PID
    wait $OPENVPN_PID
else
    RETVAL=1
fi

# we no longer need those
rm -r /etc/netns/$NAMESPACE_NAME/

# return 0 on success, 1 on failed vpn connection,
# 2 on problems within the program we ran in the namespace
exit $RETVAL