Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Systemd service with optional failover with multiple vpn backends #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
install:
cp systemd/[email protected] /etc/systemd/system/[email protected]
cp openvpn-netns /usr/local/bin/openvpn-netns
cp openvpn-netns-shell /usr/local/bin/openvpn-netns-shell
cp openvpn-netns-service /usr/local/bin/openvpn-netns-service
cp -r openvpn-scripts /usr/local/bin/

uninstall:
rm /etc/systemd/system/[email protected]
rm /usr/local/bin/openvpn-netns
rm /usr/local/bin/openvpn-netns-shell
rm /usr/local/bin/openvpn-netns-service
rm -r /usr/local/bin/openvpn-scripts
63 changes: 53 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openvpn-netns
=============

Start OpenVPN connection inside Linux network namespace.
Start an OpenVPN connection inside a Linux network namespace (netns).

These scripts allow some programs to use the VPN connection while the
rest of the system uses the normal network connection. For programs
Expand All @@ -10,6 +10,58 @@ is through the VPN tunnel. This prevents VPN leaks. Multiple VPN
connections can be opened at the same time each in its separate
namespace.

Installing
----------

Run `sudo make install`.
Run `sudo systemctl daemon-reload` to refresh the systemd service if necessary.

Uninstalling
----------

Run `sudo make uninstall`.

Systemd service
---------------

To create a new service which will start an OpenVPN connection in
a new network namespace:

Choose a name for the netns (in this example: vpn0)
Create the corresponding folder in /etc/openvpn-netns/:

sudo mkdir -p /etc/openvpn-netns/vpn0

Put in that folder:

- a `params` file containing the params used by openvpn
echo "--auth-user-pass pass --dev tun0" > /etc/openvpn-netns/vpn0/params

- a `config.ovpn` OpenVPN configuration file

- a `pass` file containing the password

Once the files are in place, you can start the service with:

sudo systemctl start [email protected]

You can check the logs with:

journalctl -u [email protected] -f

You can enable it at boot with:

sudo systemctl enable [email protected]

Systemd service with multiple failover backend vpn providers
------------------------------------------------------------

The systemd service can be configured to try multiple vpn providers,
trying another one in the list if the current one fails.

To do that, instead of providing the `config.ovpn` file and `params` file to the
`/etc/openvpn-netns/vpn0` folder, you can put multiple folders in there, each containing
the necessary files. The systemd service script will try each folder in succession.

Scripts
-------
Expand Down Expand Up @@ -63,7 +115,6 @@ even if openvpn reconnects immediately, all apps started via `ip netns
exec vpn COMMAND` will break and will have to be restarted. This is
because the former namespace to which they were attached is destroyed.


Settings
--------

Expand All @@ -83,7 +134,6 @@ file doesn't exist, it is automatically generated from DNS settings
from the server when the connection is started and deleted when the
connection is terminated.


IPv6
----

Expand All @@ -93,11 +143,4 @@ experimental. To turn on IPv6 support, use command line option
`--setenv IPV6 on`.


Installing
----------

sudo ln -s "$PWD"/openvpn-netns /usr/local/bin/openvpn-netns
sudo ln -s "$PWD"/openvpn-netns-shell /usr/local/bin/openvpn-netns-shell


[netns-exec]: https://github.com/pekman/netns-exec
4 changes: 2 additions & 2 deletions openvpn-netns
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ case "$1" in
esac

exec sudo openvpn \
$config_arg "$@" \
--ifconfig-noexec --route-noexec \
--script-security 2 \
--setenv NETNS "$NETNS" \
--up "$SCRIPT_DIR"/netns \
--route-up "$SCRIPT_DIR"/netns \
$config_arg "$@"
--route-up "$SCRIPT_DIR"/netns
89 changes: 89 additions & 0 deletions openvpn-netns-service
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/bash

if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit
fi

NETNS=$1

if [ -z "$NETNS" ]; then
echo "$0 should be run with a netns parameter."
exit -1
fi
# Variables
NETNS_FOLDER=/etc/openvpn-netns/$NETNS
RUN_FOLDER=/var/run/openvpn-netns/$NETNS
INDEX_FILE=$RUN_FOLDER/index

# Function to run openvpn-netns
openvpn_netns_run() {
local folder=$1
echo "Using folder $folder"

local params_file=${folder}params
local conf_file=${folder}config.ovpn

# Check if params file exists
if [ ! -e "$params_file" ]; then
echo "File '$params_file' is not found"
exit -1
fi

# Check if config.ovpn file exists
if [ ! -e "$conf_file" ]; then
echo "File '$conf_file' is not found"
exit -1
fi

# Read params
read params < "$params_file"

# Run the command with the selected folder as the working directory
echo "Running openvpn-netns on netns $NETNS with params: $params in folder $folder"
(cd "$folder" && NETNS=$NETNS /usr/local/bin/openvpn-netns --config "$conf_file" $params)

}

# Check for a single config.ovpn file in the netns folder
if [ -e "$NETNS_FOLDER/config.ovpn" ]; then
openvpn_netns_run "$NETNS_FOLDER/"
exit 0
fi

# Ensure the run folder exists
if [ ! -d "$RUN_FOLDER" ]; then
mkdir -p "$RUN_FOLDER"
fi

# Ensure the index file exists
if [ ! -e "$INDEX_FILE" ]; then
echo "0" > "$INDEX_FILE"
fi

# Read the current index
current_index=$(cat "$INDEX_FILE")

# List all directories in the NETNS_FOLDER and sort them alphabetically
folders=($(ls -d $NETNS_FOLDER/*/ | sort))

# Check if there are any folders
if [ ${#folders[@]} -eq 0 ]; then
echo "No folders found in $NETNS_FOLDER"
exit -1
fi

# Get the folder to use
selected_folder=${folders[$current_index]}

# Calculate the next index
next_index=$((current_index + 1))
if [ $next_index -ge ${#folders[@]} ]; then
next_index=0
fi

# Update the index file
echo "$next_index" > "$INDEX_FILE"

# Run openvpn-netns with the selected folder
openvpn_netns_run "$selected_folder"
17 changes: 17 additions & 0 deletions systemd/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[Unit]
Description=OpenVPN tunnel for %I
After=network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO

[Service]
ExecStart=/usr/local/bin/openvpn-netns-service %i
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_SYS_ADMIN
LimitNPROC=10
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target