cat /dev/brain |

VPN autostart

published on Friday, February 24, 2017

In VPN in a Nutshell I have described a command prefix vpnbox. This precommand executes applications in a network namespace but does not take care to check if VPN is already up and automatically start if it is not.

Unfortunately, I know no good (simple) way to do this. Please tell me if you've got any better solutions.

First, modify the vpnbox command as follows:

/usr/local/bin/vpnbox

#! /usr/bin/bash

user=$(whoami)

# check if there is a default route in the netns going over tun0:
# NOTE: 'tun0' may not be the correct interface name
vpn_online() {
    sudo ip netns exec vpn sudo -u $user -- ip route \
        | grep default | grep tun0
}

if ! vpn_online; then
    # Execute openvpn in daemon mode:
    sudo /bin/openvpn --config /etc/vpn/CONFIG.conf --daemon

    # Wait for completion. Otherwise routes/DNS information may not be
    # setup when the main program starts:
    echo "Waiting for route."
    while ! vpn_online; do
        sleep 0.1
    done
fi

# Execute the actual command as before:
sudo ip netns exec vpn sudo -u $user -- "$@"

To make this work without passwords, type sudo visudo to add the following to your sudoers:

/etc/sudoers

# put this near the end of the file:
# this line is unchanged from the previous post:
alice ALL=(ALL:ALL) NOPASSWD: /usr/bin/ip netns exec vpn sudo -u alice -- *

# The following line is new and allows to start the vpn without password:
alice ALL=(ALL:ALL) NOPASSWD: /usr/bin/openvpn --config /etc/openvpn/CONFIG.conf --daemon

WARNING: You must specify the exact line here. You must not use the * as a lazy shortcut here, otherwise a user can specify as additional parameters any script and it will be executed as root.

Advanced version

Personally, I'm using another (more complex) solution that does not provide much benefit over the simpler one given above. But since I've gone through the development effort and learnt something from it and also it's slightly nicer, I cannot let go yet.

The difference is that it does print some output of the openvpn command to standard output and can exit early if an error occurs during the initialization phase.

This is accomplished by exchanging the plain sudo openvpn statement in the vpnbox script above by the crazier command:

sudo openvpn-daemonize /etc/openvpn/CONFIG.conf || exit 1

Huh? This wasn't so bad, was it?

Yeah, but you will also have to provide the openvpn-daemonize script. This time I highly recommend to put it actually in /usr/local/bin and not in a user path. Make it writable by root only (because we are executing it with sudo).

/usr/local/bin/openvpn-daemonize

#! /bin/zsh

cd /etc/openvpn
config=$1

basename=$(basename ${config%.*})
log=/var/log/vpn/$basename.log
writepid=/var/log/vpn/$basename.pid

# Truncate log file to make sure it doesn't contain remnants
echo >$log

# Start VPN in background, this does not block
/bin/openvpn --config $config --log $log --writepid $writepid --daemon

# Create a temporary pipe that will be used to connect the standard IO of
# the next two processes
pipe=$(mktemp -u)
mkfifo $pipe

# Search for markers in the fifo stream, quit with exit code when found
sed -e '/Initialization Sequence Completed/q0' \
    -e '/Connection refused/q1' <$pipe & sed_PID=$!

# Follow the log, write output to pipe, but also exit when 'sed' exits
tail -n +0 -f $log  --pid $sed_PID >> $pipe
exitcode=$?

# Cleanup and exit
rm $pipe
exit $exitcode

Also, convenience demands to add the following additional line in sudoers:

alice ALL=(ALL:ALL) NOPASSWD: /usr/local/bin/openvpn-daemonize /etc/openvpn/CONFIG.conf

This entry was tagged config, gist, linux, privacy, utility and vpn